C语言-struct内存分布

(27) 2024-03-23 07:01:01

看到一个之前一直没有意识到的问题,代码如下:

#include <stdio.h>
#include <string.h>

typedef struct helloTest {
	char a[4];
	char b[24];
}helloTest_t;

int main (void)
{

	helloTest_t t1;
	printf("address,a:%p, b:%p\n", &t1.a, &t1.b);

	strcpy(t1.b, "hello world!");
	strcpy(t1.a, "hello");
	printf("%s\n", t1.b);
	return 0;
}

思考printf输出的是什么,如果你的答案是“hello world!”,那这篇文章请接着往下看。

答案是:o

address,a:0x7fff1219ddc0, b:0x7fff1219ddc4
o

 为什么呢?

看结构体中的a,b变量,其内存分布是连续的,那也就是,当我从b的首地址先拷贝“hello world!”字符串,也就是从c4地址开始,模型如下:

C语言-struct内存分布 (https://mushiming.com/)  第1张

然后从a的首地址c0开始拷贝“hello”,内存分布则变成:

C语言-struct内存分布 (https://mushiming.com/)  第2张 

所以打印t1.b时,即从c4地址开始,遇到'\0'停止,输出:o。

  • 如果程序变为这样呢:
#include <stdio.h>
#include <string.h>

typedef struct helloTest {
	char a[4];
	char b[24];
}helloTest_t;

int main (void)
{

	helloTest_t t1;
	printf("address,a:%p, b:%p\n", &t1.a, &t1.b);

	strcpy(t1.a, "hello");
	printf("%c\n", t1.a[4]);
	return 0;
}

可以看到上面程序对数组a的访问,有越界行为,猜一下能编译并运行成功吗?

答案是可以,输出:o

        因为结构体的内存分配是连续的,所以从数组a的首地址开始拷贝,“hello” 加上'\0'总共6个字节,是没有超过结构体总的长度的(28Byte),所以该程序不会报错,且能正常运行。

        有人问,a[4]不是越界了嘛

        a[4] 可以理解为:以a的首地址,也就是c0开始偏移4Byte,也就是*(t1.a+4);

  • 再思考一个,如果把结构体中的b变量去掉,会发生什么?

这里有点离谱,在linux中编译的,竟然没有报错,且能正常运行,可能是gcc版本有点低。从而能看出strcpy这个函数有多危险,慎用!!!

C语言-struct内存分布 (https://mushiming.com/)  第3张

 请用strncpy_s(char * str2, int size2, char * str1, int size1);代替

THE END

发表回复