疑问
首先通过下面例子抛出疑问:
- define.c
#include <stdio.h>
char g_str[] = "KunQAQrz";
void define_print(void) {
printf("define_print() : %s\n", g_str);
}
- main.c
#include <stdio.h>
extern char* g_str;
int main() {
define_print();
printf("main() : %s\n", g_str);
return 0;
}
对以上代码编译运行:
程序结果
为什么会发生段错误呢?下面将对错误进行分析。
原因分析
指针与数组
指针:
- 指针的本质是一个变量,它保存的目标值是一个内存地址。
- 指针运算与 * 操作符配合使用能够模拟数组的行为。
数组:
- 数组是一段连续的内存空间。
- 数组名可看做指向数组第一个元素的常量指针。
指针与数组在汇编层面的不同
#include <stdio.h>
int main() {
int a[3] = {0};
int* p = a;
p[0] = 1;
p[1] = 2;
a[2] = 3;
return 0;
}
编译生成以上代码的可执行文件,并用objdump
反汇编生成汇编代码。
以下为部分汇编代码:
p[0] = 1; /* 省略了加上偏移量的操作 */
117b: 48 8b 45 e0 mov -0x20(%rbp),%rax
117f: c7 00 01 00 00 00 movl $0x1,(%rax)
p[1] = 2;
1185: 48 8b 45 e0 mov -0x20(%rbp),%rax /* 将指针p存的地址传入寄存器中 */
1189: 48 83 c0 04 add $0x4,%rax /* 加上偏移量 */
118d: c7 00 02 00 00 00 movl $0x2,(%rax) /* 将值赋值给寄存器所存地址的内存处 */
a[2] = 3;
1193: c7 45 f4 03 00 00 00 movl $0x3,-0xc(%rbp) /* 直接将值赋值给对应内存处 */
通过上述汇编代码可得知,操作指针比操作数组多一个寻址操作。
指针与数组的不同
解决疑问
通过原因分析了解到指针与数组在汇编层面是不同的,而疑问中的例子,将define.c
中的g_str[]
数组在main.c
中当作指针来编译。这样得到的结果就是在main.c
读取g_str
会多一次寻址,从而访问野地址的内存。
错误描述
所以在
main.c
想要正常打印g_str
的内容,传入g_str
的地址即可:
- main.c
#include <stdio.h>
extern char* g_str;
int main() {
define_print();
printf("main() : %s\n", &g_str);
return 0;
}
结果:
image