在我的代码中:
char *str[] = {"forgs", "do", "not", "die"};
printf("%d %d", sizeof(str), sizeof(str[0]));
我得到的输出是12 2
,所以我的疑问是:
- 为什么有区别?
str
和str[0]
都是char指针,对吗?
虽然这个问题已经被回答并接受了,但我正在添加一些更多的描述(也回答了原来的问题),我想这对新用户会有帮助。(正如我搜索的那样,这个描述在其他任何地方都没有解释(至少在stackoverflow上),因此我现在添加。
首读:sizeof
Operator
当对数组类型应用
sizeof
运算符时,结果是该数组的总字节数。
根据此,当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
检查上面在@codepad工作,注意char
的大小是一个字节,如果你在上面的程序中将char
替换为int
,那么每个输出将乘以你机器上的sizeof(int)
。
char* str[]
和char str[][]
的区别以及它们在内存中的存储方式
Declaration-1:char *str[] = {"forgs", "do", "not", "die"};
在此声明中,str[]
是指向char的指针的数组。每个索引str[i]
都指向{"forgs", "do", "not", "die"};
中字符串的第一个字符,
逻辑上str
应该按照以下方式在内存中排列:
Array Variable: Constant Strings:
--------------- -----------------
str: 201 202 203 204 205 206
+--------+ +-----+-----+-----+-----+-----+-----+
343 | |= *(str + 0) | 'f' | 'o' | 'r' | 'g' | 's' | ' '|
| str[0] |-------| +-----+-----+-----+-----+-----+-----+
| 201 | +-----------▲
+--------+ 502 503 504
| | +-----+-----+-----+
347 | str[1] |= *(str + 1) | 'd' | 'o' | ' '|
| 502 |-------| +-----+-----+-----+
+--------+ +-----------▲
| | 43 44 45 46
351 | 43 | +-----+-----+-----+-----+
| str[2] |= *(str + 2) | 'n' | 'o' | 't' | ' '|
| |-------| +-----+-----+-----+-----+
+--------+ +-----------▲
355 | |
| 9002 | 9002 9003 9004 9005
| str[3] | +-----+-----+-----+-----+
| |= *(str + 3) | 'd' | 'i' | 'e' | ' '|
+--------+ | +-----+-----+-----+-----+
+-----------▲
Diagram: shows that str[i] Points to first char of each constant string literal.
Memory address values are assumption.
注意:str[]
存储在连续内存分配中,每个字符串以随机地址存储在内存中(而不是在连续空间中)。
[答案]
根据Codepad以下代码:
int main(int argc, char **argv){
char *str[] = {"forgs", "do", "not", "die"};
printf("sizeof(str): %lu, sizeof(str[0]): %lun",
sizeof(str),
sizeof(str[0])
);
return 0;
}
输出:
sizeof(str): 16, sizeof(str[0]): 4
在此代码中,
str
是一个包含4字符地址的数组,其中每个char*
的大小为4字节,因此根据上面的引用,数组的总大小为4 * sizeof(char*)
= 16字节。str
的Datatype为char*[4]
。str[0]
只是指向char的指针,所以它有四个字节。str[i]
的Datetype是char*
.
(注意:在某些系统地址中可以是2字节或8字节)
关于输出,还应该阅读glgll对问题的评论:
在任何架构中,第一个值应该是第二个值的4倍。在32位机器上,您应该得到16 4,在64位机器上得到32 8。在一个很旧的上面或者在嵌入式系统上,您甚至可能得到8 2,但永远不会得到12 2,因为数组包含4个相同大小的元素
附加说明:
- 因为每个
str[i]
指向一个char*
(和字符串)是变量,str[i]
可以被分配一个新的字符串地址,例如:str[i] = "yournewname";
对i = 0 to < 4
有效。
还有一点需要注意:
在上面的例子中,
str[i]
指向不能修改的常量字符串字面值;因此,str[i][j] = 'A'
是无效的(我们不能在只读内存上写),这样做将是一个运行时错误。
但是假设str[i]
指向一个简单的char数组,那么str[i][j] = 'A'
可以是一个有效的表达式。
考虑以下代码: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)
查看这里的工作代码:Codepad
Declaration-2:char str[][6] = {"forgs", "do", "not", "die"};
:
这里str
是一个大小为4 * 6的二维字符数组(每行大小相等)。(记住这里你必须明确地在str
的声明中给出列值,但行是4,因为字符串的数量是4)
在内存中str[][]
将类似于下图:
str
+---201---202---203---204---205---206--+
201 | +-----+-----+-----+-----+-----+-----+|
str[0] = *(str + 0)--►| 'f' | 'o' | 'r' | 'g' | 's' | ' '||
207 | +-----+-----+-----+-----+-----+-----+|
str[1] = *(str + 1)--►| 'd' | 'o' | ' '| ' '| ' '| ' '||
213 | +-----+-----+-----+-----+-----+-----+|
str[2] = *(str + 2)--►| 'n' | 'o' | 't' | ' '| ' '| ' '||
219 | +-----+-----+-----+-----+-----+-----+|
str[3] = *(str + 3)--►| 'd' | 'i' | 'e' | ' '| ' '| ' '||
| +-----+-----+-----+-----+-----+-----+|
+--------------------------------------+
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:线性存储器中的多维数组是这样组织的:一行接一行地存储。这是C编程语言使用的方法。
注意两个图的不同之处。
- 在第二种情况下,完整的二维字符数组被分配到连续内存中。
- 对于任何
i = 0 to 2
,str[i]
和str[i + 1]
的值相差6字节(即等于一行的长度)。 - 图中双边界线表示
str
代表完整的6 * 4 = 24个字符。
现在考虑类似的代码你张贴在你的问题为二维字符数组,检查在Codepad:
int main(int argc, char **argv){
char str[][6] = {"forgs", "do", "not", "die"};
printf("sizeof(str): %lu, sizeof(str[0]): %lun",
sizeof(str),
sizeof(str[0])
);
return 0;
}
输出:
sizeof(str): 24, sizeof(str[0]): 6
根据sizeof
运算符对数组的处理,当应用二维数组的大小为时,应该返回整个数组的大小,即24字节。
如我们所知,
sizeof
操作符在应用数组名时返回整个数组的大小。因此,对于sizeof(str)
,它返回= 24,即完整的2D字符数组的大小,由24个字符(6-cols* 4-rows)组成。在这里,
str
的声明类型是char[4][6]
。一个更有趣的点是
str[i]
代表一个数组聊天,它的类型是char[6]
。sizeof(str[0])
是完整数组的大小= 6(行长度)。
附加说明:
在第二个声明中
str[i][j]
不是常量,它的内容可以改变,例如str[i][j] = 'A'
是一个有效的操作。str[i]
是char[6]
类型的char数组的名称,str[i]
是一个常量,例如str[i] = "newstring"
是非法操作(感染会导致编译时错误)。
两个声明之间的另一个重要区别:
在<<p> strong> Declaration-1 :char *str[] = {"forgs", "do", "not", "die"};
,&str
的类型是char*(*)[4]
,它的char指针数组地址。在<<p> strong> Declaration-2 :char str[][6] = {"forgs", "do", "not", "die"};
,&str
类型为char(*)[4][6]
,其地址为4行6位二维字符数组
如果想要读取一维数组的类似描述:sizeof(&array)
返回什么?
在大多数情况下,数组名称将衰减为其第一个元素的地址值,并且类型与指向元素类型的指针相同。因此,您会期望裸str
的值等于&str[0]
,类型为指针指向指向char
的指针。
然而,sizeof
的情况并非如此。在本例中,数组名称保持了sizeof
的类型,即指向char
的4个指针数组。
sizeof
返回类型为size_t
。如果你有一个C99编译器,你可以在格式字符串中使用%zu
来打印sizeof
返回的值。
我的电脑上是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(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