一、问题引入
首先,测试环境如下:
![](1.png)
下面是测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <unistd.h> #include <stdio.h> #include <stdlib.h>
typedef struct { int age; char name[8]; } student;
int main(void) { student s[3] = { {1, "a"}, {2, "b"}, {3, "c"} }; student *p = &(s[0]);
printf("%d, %d \n", *s, *p);
return 0; }
|
这段代码非常简单,s
是一个包含 3 个元素的数组,每个元素的类型是结构体 student
。
而 p
是一个指针,它指向变量 s
,也就是说指针 p
中保存的是变量 s
的地址,因为数组名就表示该数组的首地址。
原本,期望结果应该是 1 1
,因为 s
和 p
都指向这个数组的首地址,也就是数组中第一个元素的地址。下面来看打印结果:
![](2.png)
结果竟然是 1 0
!
二、问题分析
1、打印内存模型
下面先看一下结构体 s
的内存模型是否和我们所设想的一致,修改代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include <unistd.h> #include <stdio.h> #include <stdlib.h>
typedef struct { int age; char name[8]; } student;
int main(void) { student s[3] = { {1, "a"}, {2, "b"}, {3, "c"} }; student *p = &(s[0]);
printf("sizeof student = %d \n\n", sizeof(student));
printf("print each byte in s: "); char *pTmp = p; for (int i = 0; i < 3 * sizeof(student); ++i) { if (0 == i % sizeof(student)) printf("\n"); printf("%x ", *(pTmp + i)); } printf("\n\n");
printf("print value of s and p \n\n"); printf("s = 0x%x, p = 0x%x \n\n", s, p);
return 0; }
|
结果如下:
![](3.png)
从打印结果来看是没有任何问题的。
既然 s
和 p
指向同一个地址,那为什么打印出来的结果会不对呢?
2、分开打印
既然第一个 *s
打印结果是正确的,那么就把这个两个数据分开来打印,测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <unistd.h> #include <stdio.h> #include <stdlib.h>
typedef struct { int age; char name[8]; } student;
int main(void) { student s[3] = { {1, "a"}, {2, "b"}, {3, "c"} }; student *p = &(s[0]);
printf("*s = %d \n", *s); printf("*p = %d \n", *p);
return 0; }
|
结果如下:
![](4.png)
分开打印后 *p
的打印结果竟然就正确了。
但从这里看,大致可以判定为 printf
打印出第一个值后影响到了第二值的打印,为什么会出现这种情况呢?
三、进一步排查
既然知道了是 printf
打印时出现的问题,那么下面就继续来对这个问题进行分析。
1、打印两个字符串
1 2 3
| char aa[] = "hello world"; char *p = aa; printf("%d %d \n", *aa, *p);
|
编译运行,结果是 104,没有问题。
2、打印结构体
1 2 3 4
| student s = { 1, "a"};
printf("%d \n", s); printf("%d, %d \n", s, s);
|
结果又打印出了 0:
![](5.png)
四、解决问题
最终再 StackOverflow
上找到了答案:use printf(“%s”,..) to print a struct, the struct’s first variable type is ‘char *’, why can get a right string stored in ‘char *’?。
![](6.png)
总的来说,用 printf
来打印结构体类型的变量是 undefined behavior
,也就是未定义行为。这个时候,会发生什么就取决于编译器了
为了避免发生 undefined behaviour
,可以在编译时加上 -Wall -Wextra -Werror
,这样前面的代码编译时就会报错:
![](7.png)