我试图了解C结构中的内存分配,但我被卡住了。
struct Person {
char *name;
int age;
int height;
int weight;
};
struct Person *Person_create(char *name, int age, int height, int weight)
{
struct Person *who = malloc(sizeof(struct Person));
assert(who != NULL);
who->age = age;
who->height = height;
who->weight = weight;
who->name = strdup(name);
return who;
}
int main(int argc, char *argv[])
{
struct Person *joe = Person_create("ABC", 10, 170, 60);
printf("Size of joe: %dn", sizeof(*joe));
printf("1. Address of joe t= %xn", joe);
printf("2. Address of Age t= %xn", &joe->age);
printf("3. Address of Height t= %xn", &joe->height);
printf("4. Address of Weight t= %xn", &joe->weight);
printf("5. Address of name t= %xn", joe->name);
...
我不明白的是这个结构的内存分配。在我的打印输出中,我看到了这个:
Size of joe: 24
1. Address of joe = 602010
2. Address of Age = 602018
3. Address of Height = 60201c
4. Address of Weight = 602020
5. Address of name = 602030
问题:
- 为什么1和2之间有差距?
- 为什么4和5之间有差距?
-
*name
的大小是如何计算的,因为名称只指向第一个字符?
对象joe
的地址和数据成员age
的地址之间没有间隔。该extent被数据成员name
占用。
struct Person {
char *name;
int age;
//...
根据输出
1. Address of joe = 602010
2. Address of Age = 602018
占用8字节,即sizeof( char * )
在您的平台上等于8。并且它的地址与对象joe
本身的地址一致。
printf("5. Address of name t= %xn", joe->name);
您没有输出name
本身的地址。您打印了存储在该指针中的值,该值是使用strdup
获得的字符串字面值"ABC"
副本的第一个字符的地址。
因此,输出4和5中的值之间存在差距,因为它们是不同的内存范围。数据成员weight
属于对象joe
,而字符串字面值"ABC"
的副本存储在对象外部。对象只有一个数据成员name
,它指向文本副本的第一个字符。
由于name
是一个指针,所以它的大小计算如下
sizeof( char * )
或
sizeof( joe->name )
等于8,正如我在文章开头所解释的。
如果你想确定字符串字面值的长度,你应该使用标头<string.h>
中声明的标准函数strlen
。例如
printf( "%zun", strlen( joe->name ) );
为什么1和2之间有一个间隙?
结构体的起始地址总是等于它的第一个成员的地址。来自C标准:
6.7.2.1-13。结构体对象的指针经过适当转换后,指向其初始成员
第一个成员不是age
而是name
。所以下面两行应该打印相同的地址:
printf("1. Address of joe t= %xn", joe);
printf("1. Address of name-pointer t= %xn", &joe->name);
在你的代码中,
printf("5. Address of name t= %xn", joe->name);
不打印指针的地址,而是打印指针所指向的数据的地址。
如何计算*name的大小,因为名称只指向第一个字符?
name
是一个指针,它占用8字节的内存,不管它指向的数据的大小(可能是一个字符串,在你的情况下,单个字符,int或其他)。
为什么4和5之间有一个间隙?
用于存储实际name
字符串的内存不在结构体中——strdup
将内存分配到的某个位置以复制字符串。这恰好是在结构体的最后一个成员之后的16字节。这个内存位置然后由你的name
指针指向。
请注意,填充和内存对齐只是结构体大小的一个因素(它们与您显式陈述的问题无关)。由于该结构体包含一个指针(在您的机器上为8字节)和3个整数(每个整数为4字节),因此可以假设总大小为20字节。在大多数平台上,内存是8字节对齐的——这就是为什么你的结构体的大小被四舍五入到24字节。这样,如果你声明一个 Person
s的数组,每个数组元素都从一个8字节对齐的地址开始,也就是说,地址值可以被8平均除以。
c标准唯一保证的是第一个成员的地址与结构体的地址相同,并且后续成员的地址随其在结构体中的位置而增加。
编译器允许在成员之间插入空格;这叫做填充。把它看作是针对特定平台优化结构的编译器。
数组在内存中必须始终是连续的
这是由于所谓的数据对齐。引用本网站
C/c++中的每个数据类型都有对齐要求(实际上它是由处理器体系结构强制的,而不是由语言强制的)
然后扩展对结构的要求:
由于各种数据类型的对齐要求,结构的每个成员都应该自然对齐。
结构体的内存布局是机器相关的,所以除非你试图实现DBMS或设备驱动程序或类似的东西,否则你不应该为此烦恼。
sizeof(*name)
等于sizeof(char)
,我不明白你这里有什么困惑,你能进一步解释一下吗?