Страницы

Поиск по вопросам

среда, 5 февраля 2020 г.

При передаче массива строк в функцию строки равны NULL

#c


Есть непонятная проблема по передача массива строк в функцию на языке Си.

Вызывающий код:

char str[33][40];
CreateCSV((char**)str);


Функция:

void CreateCSV(char** str)
{
    char strData[]= {'"','Д','А','Т','А','"',';','\0'};

    if(str[0x00] != NULL)
        strcpy (str[0x00], strData);
    else
        Console("СтрокаЗаголовка[0x00] == NULL");
}


Почему у меня str[0x00] == NULL? Ведь я явно создал 33 строки и зарезервировал по
40 символов под каждую.
    


Ответы

Ответ 1



Если в кратце - используйте одинаковый прототип для объявления переменной и для параметров в функции и для strData. Это не самый красивый вариант, но он сработает Вот как будет выглядеть Ваша программа: #include #include void CreateCSV(char str[33][40]) { char strData[33][40]= {"\"","D","A","T","A","\"",";","\0"}; if(str != NULL) memcpy (str, strData, sizeof(strData)); else printf ("error\n"); } int main (void) { char str[33][40] = {'\0'}; int i=0; CreateCSV(str); for (i=0; i<33; i++) printf ("%s\n", str[i]); return 0; } Обратите внимание на сл. нюансы - я использовал функцию копирования памяти а не строк "memcpy". И проверял на NULL имя str без указания сдвига. Это связано с тем, как язык Си представляет и обрабатывает многомерные массивы. Также при заполнении strData использовал двойные кавычки, иначе компилятор соединял буквы в одну строку, а не трактовал их как ряд строк с 1 буквой в каждой строчке. Если в деталях и особенностях физики языка Си: Массивы объявленные как char str[33][40]; и как char str**; это совершенно разные вещи. char str[33][40]; - трактуеться как длинный одномерный массив глде все строки слеплены в одну одна-за-другой. Что-то ввиде "строка 1 \0\0\0\0\0...\0 строка 2 \0\0\0\0\0...\0 ....... строка 33 \0\0\0\0\0...\0 " и размер такого массива будет равер 33*40*sizeof(char)=33*40*1=1320. При объявлении такого массива, компилятор выделяет 1320 байт в стеке, и трактует переменную 'str' как указатель на эту длинную строку. char str;** - это массив указателей на одномерные строки. Воспринимаеться компилятором как {*pointer_to_str1, *pointer_to_str2, ... *pointer_to_str33}, при этом сами строки лежат вне этого массива. Размер такого массива будет равен 33*sizeof(char*) = 33*4 = 132/ При объявлении такого массива, компилятор проводит несколько операций. Сначала компилятор выделит 33 независимых строки в стеке , затем создаст массив указателей и заполняет его указателями на эти 33 независимые строки. Вот как будет выглядеть Ваша программа если strData объявить как char**: #include #include void CreateCSV(char str[33][40]) { char *strData[33] = {"\"","D","A","T","A","\"",";","\0"}; if(str != NULL) { int i=0; for (i=0; i<33; i++) if (strData[i] != NULL) strncpy (str[i], strData[i], 40); } else printf ("error\n"); } int main (void) { char str[33][40] = {'\0'}; int i=0; CreateCSV(str); for (i=0; i<33; i++) printf ("%s\n", str[i]); return 0; } PS оба примера проверены на gcc

Ответ 2



Основная проблема - в преобразовании (char**)str тип str - char (*)[33]. Рассмотрим простую программу: int main() { long a[10][10]; for (int i=0;i<10;i++) for (int j=0;j<10;j++) a[i][j] = -10000 - i*100 - j; cout << (long)a< *(a + i*10 + j). В массиве int ** данные упакованы не подряд а (примерно) так: b --> [0][1]...[9] b[0] --> [0][1]...[9] b[1] --> [0][1]...[9] ... b[9] --> [0][1]...[9] При этом они даже не обязаны лежать подряд. b[i][j] <==> * ( *(b+i) + j) Обратите внимание, что нужно уже 2 раза разыменовывать указатель. В этом смысле массивы a[10][10] можно считать одномерными, а остальное - удобство пользователя. После преобразования происходит следующее (доступ в b[3][5] к примеру): (адрес a и b совпадает) берётся элемент по адресу (b + 3) смотрим в массив a --> это будет элемент a[3] берётся элемент по адресу ( a[3] + 5) в a[3] какое-то значение, мы его пытаемся разыменовать - UB (а обычно SEG_FAULT) Кстати компилятор не просто так запрещает прямое присваивание int **b = a; Имеет смысл читать эти предупреждения. Если вам нужно передать такой массив в функцию, то есть разные варианты, как правильно написать прототип func( char x[33][40]) первый размер можно не писать func( char x[][40]) - самые простые.

Комментариев нет:

Отправить комментарий