There is an incomprehensible problem of passing an array of strings to a function in C.

Calling code:

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

Function:

 void CreateCSV(char** str) { char strData[]= {'"','Д','А','Т','А','"',';','\0'}; if(str[0x00] != NULL) strcpy (str[0x00], strData); else Console("СтрокаЗаголовка[0x00] == NULL"); } 

Why do I have str[0x00] == NULL ? After all, I clearly created 33 lines and reserved 40 characters for each.

  • Doesn't the compiler issue a warning to your cast? - VladD
  • without caste warning) - Aldmi
  • @VladD answer in detail or write briefly? - pavel
  • @pavel answer to such questions: "read the messages of the compiler." - αλεχολυτ
  • one
    @Aldmi I'll write right now. - pavel

2 answers 2

If in short - use the same prototype for declaring a variable and for parameters in a function and for strData. This is not the most beautiful option, but it will work.

Here is what your program will look like:

 #include <stdio.h> #include <string.h> 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; } 

Pay attention to the following. nuances - I used the copy memory function and not the memcpy strings. And checked on the NULL name str without specifying the shift. This is due to the way the C language represents and processes multidimensional arrays. Also, when filling in, strData used double quotes, otherwise the compiler put the letters together in one line, and did not treat them as a series of lines with 1 letter in each line.


If in the details and features of the physics of the C language: Arrays declared as

 char str[33][40]; 

And How

 char str**; 

these are completely different things.

char str [33] [40]; - interpreted as a long one-dimensional array where all the lines are stuck together one after another. Something in the form "line 1 \ 0 \ 0 \ 0 \ 0 \ 0 ... \ 0 line 2 \ 0 \ 0 \ 0 \ 0 \ 0 ... \ 0 ....... line 33 \ 0 \ 0 \ 0 \ 0 \ 0 ... \ 0 " and the size of such an array will be raver 33 * 40 * sizeof (char) = 33 * 40 * 1 = 1320.

When declaring such an array, the compiler allocates 1320 bytes in the stack, and treats the variable 'str' as a pointer to this long string.

char str ; ** is an array of pointers to one-dimensional strings. Perceived by the compiler as {* pointer_to_str1, * pointer_to_str2, ... * pointer_to_str33}, while the lines themselves are outside of this array. The size of such an array will be 33 * sizeof (char *) = 33 * 4 = 132 /

When declaring such an array, the compiler performs several operations. First, the compiler allocates 33 independent lines in the stack, then creates an array of pointers and fills it with pointers to these 33 independent lines.

Here is what your program will look like if strData is declared as char **:

 #include <stdio.h> #include <string.h> 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 both examples are checked for gcc

  • returning to your original question - by declaring str [33] [40] you created an empty array, which most likely was automatically filled by the compiler with zeros. Inside the function, you performed one change, the function expected to see a pointer to the string, and received the first character of an empty array - this is where NULL came out - Peter K.

The main problem is in the conversion (char**)str type str - char (*)[33] . Consider a simple program:

 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<<endl; cout << (long)a[0]<<" "<<(long)a[1]<<" "<<(long)a[9]<<endl; int **b = (int **)a; cout << (long)b<<endl; cout << (long)b[0]<<" "<<(long)b[1]<<" "<<(long)b[9]<<endl; return 0; } 

Conclusion: (from me, it is random)

 140727855538752 140727855538752 140727855538832 140727855539472 140727855538752 -10000 -10001 -10009 

As you can see, there was a problem when accessing b[i] we expected that there is a pointer (int *) but there are negative numbers. In b [0] contains [0,0] element, in b [1] - [0,1] in b [9] - [0,9].

Why it happens. In a multidimensional array, the data is packed in a row:

 [0,0][0,1]...[0,9][1,0][1,1]...[1,9]...[9,0][9,1]...[9,9] 

At the same time, a[i][j] <=> *(a + i*10 + j) .

In the array int ** data is not packed in a row but (approximately) like this:

 b --> [0][1]...[9] b[0] --> [0][1]...[9] b[1] --> [0][1]...[9] ... b[9] --> [0][1]...[9] 

However, they do not even have to lie in a row. b[i][j] <==> * ( *(b+i) + j) Please note that you need to dereference the pointer 2 times. In this sense, arrays a[10][10] can be considered one-dimensional, and the rest is user convenience.

After the conversion, the following occurs (access in b [3] [5] for example): (address a and b match)

  • take the element at the address (b + 3) look into the array a -> it will be the element a [3]
  • an element is taken to the address (a [3] + 5) in a [3] some value, we are trying to dereference it - UB (and usually SEG_FAULT)

By the way, the compiler does not just forbid direct assignment int **b = a; It makes sense to read these warnings.

If you need to pass such an array to a function, then there are different options for how to correctly write the prototype of func( char x[33][40]) first size is not to write func( char x[][40]) - the simplest.

  • There are no pictures at hand, who can add) - pavel
  • Thank you very much. I knew about the packaging of data in a row and thought it always goes like this, so without hesitation I passed a pointer to the beginning of the whole area. That is, I need to pass a pointer to an array in my case? - Aldmi
  • @Aldmi depends on the specific task and what will be in this function, as long as you can send str[0] there right away - pavel
  • that is, if I pass str [0], and in the function I call str [i], will I turn to the i-th line? - Aldmi
  • one
    func( char x[33][40]) последний размер можно не писать func( char x[33][]) not only the last one, but the first one (that is, func( char x[][40]) that's right) - avp