Reputation: 422
All i am trying with the following source code, is to understand in depth how 2d arrays (tables) works in C. The code bellow may seems much but as you can see its nothing but simple prints and assignments.
Code:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, char* argv[]){
char** as = (char **)malloc(2 * sizeof(char *));
for (int i=0; i<2; i++){
as[i] = (char *)malloc(3 * sizeof(char));
}
strcpy(&as[0][0],"a");
strcpy(&as[0][1],"b");
strcpy(&as[0][2],"c");
strcpy(&as[1][0],"aa");
strcpy(&as[1][1],"bb");
strcpy(&as[1][2],"cc");
printf("as[0][0] <%s>\n",&as[0][0]);
printf("as[0][1] <%s>\n",&as[0][1]);
printf("as[0][2] <%s>\n",&as[0][2]);
printf("as[1][0] <%s>\n",&as[1][0]);
printf("as[1][1] <%s>\n",&as[1][1]);
printf("as[1][2] <%s>\n",&as[1][2]);
for (int i=0; i<2; i++){
free(as[i]);
}
free(as);
/*************** PART 2 **********************/
as = (char **)malloc(2 * sizeof(int));
for (int i=0; i<2; i++){
as[i] = (char *)malloc(3 * sizeof(int));
}
printf("enter:as[0][0]: "); scanf("%s",&as[0][0]); // Input: a
printf("enter:as[0][1]: "); scanf("%s",&as[0][1]); // Input: b
printf("enter:as[0][2]: "); scanf("%s",&as[0][2]); // Input: c
printf("enter:as[1][0]: "); scanf("%s",&as[1][0]); // Input: aa
printf("enter:as[1][1]: "); scanf("%s",&as[1][1]); // Input: bb
printf("enter:as[1][2]: "); scanf("%s",&as[1][2]); // Input: cc
printf("as[0][0] <%s>\n",&as[0][0]);
printf("as[0][1] <%s>\n",&as[0][1]);
printf("as[0][2] <%s>\n",&as[0][2]);
printf("as[1][0] <%s>\n",&as[1][0]);
printf("as[1][1] <%s>\n",&as[1][1]);
printf("as[1][2] <%s>\n",&as[1][2]);
for (int i=0; i<2; i++){
free(as[i]);
}
free(as);
return 0;
}
The result i am excepting is something like the tab on the left but all i am getting into ./a.out
is the tab on the right
as[0][0] <a> } as[0][0] <abc> }
as[0][1] <b> } as[0][1] <bc> }
as[0][2] <c> } as[0][2] <c> }
as[1][0] <aa> } Excepted Result as[1][0] <abcc> } Final Result
as[1][1] <bb> } as[1][1] <bcc> }
as[1][2] <cc> } as[1][2] <cc> }
My first thought is that i am not using correctly strcpy()
. That's why /PART2/ exists in the code, but gives out the same results.
I know there is lot of same issue topics explaining 2d arrays and i did my fair research getting around the above subject. A
Upvotes: 0
Views: 184
Reputation: 20631
First: you don't actually have a 2D array.
char** as = (char **)malloc(2 * sizeof(char *));
for (int i=0; i<2; i++){
as[i] = (char *)malloc(3 * sizeof(char));
}
This allocates a 1D array, and points as
to it. Then, for each element, it allocates space and assigns the pointer to that space to the element. So what you have is a 1D array where each element is a pointer to another 1D array; let's call this a "2 level array" although I'm not sure if there's a better term available.
For a 2D array, you would instead write:
char (*as)[2][3] = malloc(2 * 3 * sizeof(char));
However, if you want the array to hold strings (as char *
) instead of individual characters then your declaration would need to be:
char *(*as)[2][3] = malloc(2 * 3 * sizeof(char));
Alternatively, your original code could be changed for a 2 level array of char *
:
char*** as = malloc(2 * sizeof(char **));
for (int i=0; i<2; i++){
as[i] = malloc(3 * sizeof(char *));
}
In both cases, you also need to allocate storage for the strings themselves. Instead of:
strcpy(&as[0][0],"a");
you should probably use:
as[0][0] = strdup("a");
So, what actually happened with your code to give you the results you got? well, here:
strcpy(&as[0][0],"a");
strcpy(&as[0][1],"b");
strcpy(&as[0][2],"c");
strcpy(&as[1][0],"aa");
strcpy(&as[1][1],"bb");
strcpy(&as[1][2],"cc");
If as
is a char **
, then as[0]
is a char *
and as[0][0]
is a simple char
. You are then taking the address of this and copying an entire string to it. At best, this is odd; at worst, particularly for that last line, it's undefined behaviour - because you are copying past the end of an allocated array.
That is:
strcpy(&as[1][2],"cc");
You are copying to the 3rd and "4th" character of as[1]
(that is, positions 2 and 3). But you only allocated space for 3:
as[i] = (char *)malloc(3 * sizeof(char));
(In fact, an additional two lines above have the same issue, because copying a string also copies the "nul terminator" byte at the end of the string).
When you print the strings, you print the result of the previous strcpy
invocations. Consider, for as[0]
:
strcpy(&as[0][0],"a");
Ok, as[0]
now contains "a", represented as 'a'
in as[0][0]
and '\0'
, the string terminator, in as[0][1]
.
strcpy(&as[0][1],"b");
Now, you have 'b'
in as[0][1]
and '\0'
in as[0][2]
.
strcpy(&as[0][2],"c");
As explained before, this is invalid; but supposing we ignore that for now, you end up with:
as[0][0]
= 'a'
as[0][1]
= 'b'
as[0][2]
= 'c'
However, now you are printing:
printf("as[0][0] <%s>\n",&as[0][0]);
... and you say that you expect "a" as the output. However, the string as[0]
contains "abc", (and is nul terminated only due to an accident of behaviour) so that is what you get when you print it.
To clarify:
as[0]
and as[1]
are string pointersas[0][i]
, for any i
, is a character within the string pointed to by as[0]
When you take the address of a character within a string, and copy another string to that address, you copy over the containing string starting at that particular character.
Upvotes: 4