为什么此C代码段不会导致分段错误?

时间:2016-06-23 10:36:48

标签: c pointers casting dereference

int main(int argc, char **argv) {
    unsigned int ptr1 = *((unsigned int *)(argv[1]));
    printf("ptr1 = 0x%x\n", ptr1);
    exit(0);
}

这是视频教程中的代码段。我不确定我是否理解为什么代码在运行时不会导致分段错误。

(unsigned int *)(argv[1])这看起来像传递给程序的第一个参数被转换为unsigned int。因此,如果参数是'AAAA',那么'0x41414141'现在是指向内存中某个位置的指针。

然后当我们执行此操作时*((unsigned int *)(argv[1])),我们不是要引用地址0x41414141指向的值吗?据我所知,该过程可能无法访问此地址。那怎么没有发生分段错误呢?该程序的输出是 -

ptr1 = 0x41414141

我已经在Linux上使用gcc编译了这个程序。

2 个答案:

答案 0 :(得分:5)

注意:此答案解释了代码在此特定情况下的工作原理,有关更完整的答案,请参阅@Lundin帖子。

(unsigned int *)(argv[1])char*转换为unsigned int*

*((unsigned int *)(argv[1]))解除引用将char*转换为unsigned int*

假设argv[1]指向AAAA字符串。它以41 41 41 41(十六进制)存储在内存中。然后,您将其解释为unsigned int,结果为ptr1 = 0x41414141

请看下面的图表:左侧查看器将内存解释为char*,右侧查看器将其解释为unsigned int*(不关心little-endian / big-endian差异):

enter image description here

所以,没有理由让segfault

答案 1 :(得分:5)

这段代码既危险又不好。逐步说明代码的作用以及为什么不好:

  • argv是指向字符(char*[])的指针数组。或者,如果你愿意的话,指针(char**)到这样一个指向角色的指针数组中的第一个项目。

  • argv[0]指向可执行文件的名称,argv[1]是指向传递的第一个参数的指针。

  • (unsigned int *)(argv[1])将指向第一个参数的指针char*转换为指向int unsigned int*的指针。这绝不保证是安全的转换。这里有两个主要的错误:

    • 如果这个新的整数地址未对齐,访问它将调用未定义的行为,可能是程序崩溃。

    • 转换违反了严格别名规则,该规则(简单地说)表明编译器可能认为char*指向的内容永远不会通过其他随机指针类型访问。因此编译器可能会认为argv[i]指向的内存从未被您的程序使用过。当您调用未定义的行为时,可能会发生任何奇怪的优化。

  • 鉴于特定编译器将指针转换的确定性行为指定为非标准扩展,那么它将尝试访问指向的字符串,就好像它是一个整数一样。如果字符串是例如"ABCD",则结果整数(假设为32位)可以是0x414243440x44434241。哪个适用取决于CPU的endianess。这样的代码是不可移植的。

但是,只访问argv数组指向的内存当然不会产生任何影响。如果您无法读取此内存,则无法使用argv参数。它们的存储方式究竟是依赖于操作系统的,但它必须是允许进程访问的内部地址空间。

因此,只要您在分配的内存中保持访问权限,程序肯定不会崩溃或出错。如果字符串argv[1]只有1个字符长,那么您可能会遇到段错误。