我对以下两种方式感到困惑:
char var[3][15] = {"Hello_world!", "good", "bad"}; // as known this is for an 2D array.
char *var[3] = {"Hello_world!", "good", "bad"}; // and this I think also 2D array, but how.
- 他们之间有什么区别?
- 使用那个或那个的情况是什么?
我对以下两种方式感到困惑:
char var[3][15] = {"Hello_world!", "good", "bad"}; // as known this is for an 2D array.
char *var[3] = {"Hello_world!", "good", "bad"}; // and this I think also 2D array, but how.
第一个是二维数组。
第二个是指针的一维数组。如果这些指针指向数组,那么它也将是一个二维数组,但这并不能仅仅通过查看类型来保证。
例如,您可以有一个包含 5 个NULL
指针的数组:
char *var[5] = {0, 0, 0, 0, 0};
在这种情况下,它肯定不是二维数组,并且尝试访问var[i][j]
将导致未定义的行为(很可能是内存访问错误或“segfault”)。
有两种类型的二维数组,你有两种类型。
第一个是由 5 个对象组成的数组char[15]
,它们在内存中按顺序排列。每个“行”末尾的未使用字节(在您的特定情况下但并非总是如此)用零填充。这是大多数人在说“二维数组”时想到的,但也有人称它为“方形”数组以区别于其他类型。初始化它时,字符串文字直接复制到数组中。
[0][ 0] = 'H'
[0][ 1] = 'e'
...
[0][14] = '\0' (end of each row is filled with zeros)
[1][ 0] = 'G'
[1][ 1] = 'o'
...
[3][13] = '\0' (end of each row is filled with zeros)
[3][14] = '\0' (end of each row is filled with zeros)
第二个是一个由 5 个char*
(指针)组成的数组,它通常指的是char
一个大小不必相同的对象数组。如果它们确实指向 char 对象数组,则可以将其作为二维数组访问。由于每个数组的长度可能不同,这被称为“锯齿状数组”。在您的代码中,有三个“行”,第一个是 13 个字符长,第二个是 5 个字符长,第三个是 4 个字符长。数组的第一个索引在内存中是连续的,但是形成“内部”索引的数组可以在内存中的任何位置。当您初始化它时,这将形成一个指向实际字符串文字的一维数组。它们一起形成一个二维数组。
[0] -> "Hello_world!"
[1] --------------------------------->"good"
[2] ---------------------->"bad"
第一个实际上是在内存中创建一个 45 (3*15) 字节的块,其基址位于将被调用的某个内存位置var
。C 将允许您将这些处理为二维数组,因此var[a][b]
与*(var + a*15 + b)
. 然后,编译器将在正确的位置用您的字符串预填充这 45 个字节,以保持 2D 数组的感觉。
第二种方法是用 3 个指针分配一块内存。如果您的指针每个为 8 个字节,var
则为 24 (3*8) 个字节的一维数组。此外,C 将使用您的数据字符串在内存中的其他位置创建一些其他数组。然后它将前者指向后者。也就是说,以下大致相当于您的第二种方法:
char s1[] = "Hello_world!";
char s2[] = "good";
char s3[] = "bad";
char *var[3] = { &(s1[0]), &(s2[0]), &(s3[0]) };
但是请注意,var[a][b] == *(var + a*15 + b)
不保持与第一种方法的等效性。例如,var[0]
将指向s1
,从技术上讲,它可以是内存中的任何位置(与 wheres2
或s3
are 无关)。然后var[0][b]
是该位置的一些偏移量。
第一个是二维字符数组。第二个是 char *(指针)的一维数组。