在数组指针的情况下,“数组名称”是什么意思?
在我的代码中:
char *str[] = {"forgs", "do", "not", "die"}; printf("%d %d", sizeof(str), sizeof(str[0]));
我得到的输出为12 2
,所以我的疑惑是:
- 为什么有差异?
-
str
和str[0]
都是char指针,对吗?
在大多数情况下,数组的名称会衰减到它的第一个元素的地址的值,并且types与指向元素types的指针相同。 所以,你会希望一个裸str
的值等于&str[0]
的types指针指向char
指针。
但是, sizeof
并不是这种情况。 在这种情况下,数组名称保持其sizeof
,这将是char
指针的数组。
sizeof
的返回types是size_t
。 如果您有C99编译器,则可以在格式string中使用%zu
来打印由sizeof
返回的值。
通过这个问题已经得到了答复和接受,但是我想加一些更多的描述(也回答原来的问题),我想对新用户会有所帮助。 (因为我search,这个描述不被解释在其他地方(至less在stackoverflow),因此我现在join。
首先阅读: sizeof
运算符
6.5.3.4运营商的规模,1125:
将sizeof
运算符应用于数组types时,结果是数组中的总字节数。
据此,当sizeof
应用于静态数组标识符的名称( 不通过malloc分配)时,结果是整个数组的大小(以字节为单位),而不仅仅是地址。 这是规则的一个例外之一, 即数组的名称被转换/衰减为指向数组的第一个元素的指针 ,这可能只是因为实际的数组大小在编译时是固定的, sizeof
运算符评估。
要更好地理解它,请考虑下面的代码:
#include<stdio.h> int main(){ char a1[6], // One dimensional a2[7][6], // Two dimensional a3[5][7][6]; // Three dimensional printf(" sizeof(a1) : %lu \n", sizeof(a1)); printf(" sizeof(a2) : %lu \n", sizeof(a2)); printf(" sizeof(a3) : %lu \n", sizeof(a3)); printf(" Char : %lu \n", sizeof(char)); printf(" Char[6] : %lu \n", sizeof(char[6])); printf(" Char[5][7] : %lu \n", sizeof(char[7][6])); printf(" Char[5][7][6]: %lu \n", sizeof(char[5][7][6])); return 1; }
其输出:
sizeof(a1) : 6 sizeof(a2) : 42 sizeof(a3) : 210 Char : 1 Char[5] : 6 Char[5][7] : 42 Char[5][7][6]: 210
检查上面在@ 键盘上工作,注意char
大小是一个字节,它在上面的程序中用int
replacechar
,然后每个输出将在您的机器上乘以sizeof(int)
。
char* str[]
和char str[][]
之间的区别以及两者如何存储在内存中
声明-1: char *str[] = {"forgs", "do", "not", "die"};
在这个声明中, str[]
是指向 char 的指针数组 。 每个索引str[i]
指向{"forgs", "do", "not", "die"};
的string的第一个字符{"forgs", "do", "not", "die"};
。
逻辑str
应按以下方式排列在内存中:
Array Variable: Constant Strings: --------------- ----------------- str: 201 202 203 204 205 206 +--------+ +-----+-----+-----+-----+-----+-----+ 343 | |= *(str + 0) | 'f' | 'o' | 'r' | 'g' | 's' | '\0'| | str[0] |-------| +-----+-----+-----+-----+-----+-----+ | 201 | +-----------▲ +--------+ 502 503 504 | | +-----+-----+-----+ 347 | str[1] |= *(str + 1) | 'd' | 'o' | '\0'| | 502 |-------| +-----+-----+-----+ +--------+ +-----------▲ | | 43 44 45 46 351 | 43 | +-----+-----+-----+-----+ | str[2] |= *(str + 2) | 'n' | 'o' | 't' | '\0'| | |-------| +-----+-----+-----+-----+ +--------+ +-----------▲ 355 | | | 9002 | 9002 9003 9004 9005 | str[3] | +-----+-----+-----+-----+ | |= *(str + 3) | 'd' | 'i' | 'e' | '\0'| +--------+ | +-----+-----+-----+-----+ +-----------▲ Diagram: shows that str[i] Points to first char of each constant string literal. Memory address values are assumption.
注意: str[]
存储在连续的内存分配中,每个string都以随机地址存储在内存中(不在连续空间中)。
[回答]
根据Codepad的以下代码:
int main(int argc, char **argv){ char *str[] = {"forgs", "do", "not", "die"}; printf("sizeof(str): %lu, sizeof(str[0]): %lu\n", sizeof(str), sizeof(str[0]) ); return 0; }
输出:
sizeof(str): 16, sizeof(str[0]): 4
-
在这个代码中,
str
是一个4个字符地址的数组,每个char*
的大小是4个字节,所以根据上面的引用,数组的总大小是4 * sizeof(char*)
= 16个字节。 -
str
数据types是char*[4]
。 -
str[0]
不过是指向char的指针,所以它的四个字节。str[i]
Datetype是char*
。
(注意:某些系统地址可以是2字节或8字节)
对于输出还应该读一下glglgl对这个问题的评论 :
无论你是什么架构,第一个值应该是第二个值的四倍。 在一个32位的机器上,你应该得到16 4,在一个64位一个32 8.在一个非常旧的或embedded式系统上,你甚至可能得到8 2,但从来没有12 2,因为该数组包含4个元素相同的尺寸
其他要点:
- 因为每个
str[i]
指向一个char*
(和string)是可变的,str[i]
可以被赋予一个新的string地址,例如:str[i] = "yournewname";
对于i = 0 to < 4
是有效的。
还有一点需要注意:
-
在我们上面的例子中,
str[i]
指向不可修改的string常量, 因此str[i][j] = 'A'
是无效的(我们不能在只读存储器上写),这样做会造成运行时错误。
但是,如果str[i]
指向一个简单的char数组,那么str[i][j] = 'A'
可以是一个有效的expression式。
考虑下面的代码:char a[] = "Hello"; // a[] is simple array char *str[] = {"forgs", "do", "not", "die"}; //str[0][4] = 'A'; // is error because writing on read only memory str[0] = a; str[0][5] = 'A'; // is perfectly valid because str[0] // points to an array (that is not constant)
检查这里工作代码: 键盘
声明-2: char str[][6] = {"forgs", "do", "not", "die"};
:
这里的str
是一个二维的字符数组(每行大小相等),大小为4 * 6(请记住,您必须显式地声明str
列值,但是由于string的数量,行是4 4)
在内存中str[][]
将如下图所示:
str +---201---202---203---204---205---206--+ 201 | +-----+-----+-----+-----+-----+-----+| str[0] = *(str + 0)--►| 'f' | 'o' | 'r' | 'g' | 's' | '\0'|| 207 | +-----+-----+-----+-----+-----+-----+| str[1] = *(str + 1)--►| 'd' | 'o' | '\0'| '\0'| '\0'| '\0'|| 213 | +-----+-----+-----+-----+-----+-----+| str[2] = *(str + 2)--►| 'n' | 'o' | 't' | '\0'| '\0'| '\0'|| 219 | +-----+-----+-----+-----+-----+-----+| str[3] = *(str + 3)--►| 'd' | 'i' | 'e' | '\0'| '\0'| '\0'|| | +-----+-----+-----+-----+-----+-----+| +--------------------------------------+ In Diagram: str[i] = *(str + i) = points to a complete i-row of size = 6 chars. str[i] is an array of 6 chars.
二维数组在存储器中的排列称为“ 行 – 专业” ( Row-Major) :线性存储器中的multidimensional array被组织成使得行被一个接一个地存储。 这是C编程语言使用的方法。
注意两个图表的差异。
- 在第二种情况下,在继续存储器中分配完整的二维字符数组。
- 对于任何
i = 0 to 2
,str[i]
和str[i + 1]
值是不同的6个字节(即等于一行的长度)。 - 这个图中的
str
界线表示str
表示完整的6 * 4 = 24个字符。
现在考虑你的问题中发布的类似的代码2维字符数组,检查在键盘上 :
int main(int argc, char **argv){ char str[][6] = {"forgs", "do", "not", "die"}; printf("sizeof(str): %lu, sizeof(str[0]): %lu\n", sizeof(str), sizeof(str[0]) ); return 0; }
输出:
sizeof(str): 24, sizeof(str[0]): 6
根据数组运算符的sizeof
,在应用二维数组大小时应该返回24字节的整个数组大小。
-
正如我们所知,
sizeof
运算符在应用数组名称时返回整个数组的大小。 所以对于sizeof(str)
它返回= 24,它是由24个字符(6-cols * 4-rows)组成的完整2D字符数组的大小。 -
在这个声明中,
str
types是char[4][6]
。 -
另外一个有趣的地方是
str[i]
代表一个数组聊天,它的types是char[6]
。sizeof(str[0])
是完整数组的大小= 6(行长度)。
其他要点:
-
在第二个声明中,
str[i][j]
不是常量,其内容可以改变,例如str[i][j] = 'A'
是一个有效的操作。 -
str[i]
是types为char[6]
的char数组的名称,它是一个常量并赋值给str[i]
例如str[i] = "newstring"
是非法操作(感染它将是编译时错误)。
两个声明之间的一个更重要的区别:
在声明-1 : char *str[] = {"forgs", "do", "not", "die"};
, &str
types是char*(*)[4]
,它是char指针数组的地址。
在声明-2 : char str[][6] = {"forgs", "do", "not", "die"};
, &str
types是char(*)[4][6]
,它的地址是4行6列的二维char数组。
如果你想读一维数组的类似描述: sizeof(&array)
返回什么?
在我的计算机上是16 4
,我可以这样解释: str
是一个char*
数组,所以sizeof(str)==sizeof(char*)*4
我不知道你为什么得到12 2
。
这两个指针是不同的。 str
是一个array of char pointers
,在你的例子中是一个( char*[4]
), str[0]
是一个char pointer
。
第一个sizeof
返回包含的四个char指针的大小,第二个返回char*
的sizeof。
在我的testing中,结果是:
sizeof(str[0]) = 4 // = sizeof(char*) sizeof(str) = 16 = sizeof(str[0]) + sizeof(str[1]) + sizeof(str[2]) + sizeof(str[3]) = 4 * sizeof(char*) = 4 * 4 = 16