为什么这个C代码片段不会导致分段错误?

为什么这个C代码片段不会导致分段错误?,c,pointers,casting,dereference,C,Pointers,Casting,Dereference,这是视频教程中的代码片段。我不确定我是否理解为什么代码在运行时不会导致分段错误 (unsigned int*)(argv[1])这看起来像是传递给程序的第一个参数被强制转换为无符号int。因此,如果参数是“AAAA”,那么“0x4141”现在是指向内存中某个位置的指针 然后当我们这样做时-*((unsigned int*)(argv[1]),我们不是在推迟address0x4141所指向的值吗?据我所知,进程可能无法访问此地址。那么,为什么没有出现分段错误呢?程序的输出是- ptr1=0x414

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

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

然后当我们这样做时-
*((unsigned int*)(argv[1])
,我们不是在推迟address
0x4141
所指向的值吗?据我所知,进程可能无法访问此地址。那么,为什么没有出现分段错误呢?程序的输出是-

ptr1=0x4141


我用gcc在Linux上编译了这个程序

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


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

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

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

请看下图:左侧查看器将内存解释为
char*
,右侧查看器将内存解释为
unsigned int*
(不关心小端/大端的差异):


因此,没有理由使用segfault

此代码既危险又糟糕。一步一步地解释代码的作用以及原因:

  • argv
    是指向字符的指针数组(
    char*[]
    )。或者,如果您愿意,可以使用指向字符指针数组中第一项的指针(
    char**

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

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

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

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

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

然而,仅仅访问
argv
数组指向的内存当然不会产生任何影响。如果无法读取此内存,则无法使用argv参数。它们的具体存储方式取决于操作系统,但它必须位于允许进程访问的地址空间内


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

argv[1]
持有指向
char
数据的指针,您可以将该指针(而不是它所指向的)转换为指向
无符号int
数据的指针,然后取消引用该指针。您没有取消引用
0x4141
。您正在取消引用
argv[0]
,但使用的是
unsigned int*
彩色眼镜。很抱歉,这不是一个好的答案,因为您几乎没有提到代码中所有定义不好的行为。阅读此答案的读者可能会认为OP的代码是完美的代码,并且提到的教程不是垃圾。
因此,没有理由使用segfault
它是不正确的,有。您可以将任何内容强制转换为
char*
,但如果以相反的方式执行,则会调用未定义的行为。它不能保证工作,因为
argv[1]
不一定正确对齐。它在这里工作只是因为x86支持未对齐的访问,但大多数其他体系结构都是禁止的it@Lundin,同意。正在等待OP取消选中“接受”按钮,以便删除答案。建议:请立即停止观看Youtube教程。留下评论,说这是一个糟糕的教程。请随意发布此帖子的链接,以警告其他人不要观看。事实上,作者承认代码很糟糕,但上下文不同。这是视频的一个链接。@user1720897好吧。。。杜尔。该视频中的代码与您的问题中的代码不同。他将结果存储在
无符号整数*
中,而您将其存储在
无符号整数
中。完全不同的程序(但同样糟糕)。这段视频似乎是关于将数据侵入不同的静态内存位置,并以车库黑客通常的心态:“让我们希望这能奏效”或“哇,这一次奏效了”。是的。代码是不同的。我在玩代码,试图向自己解释代码,但我偶然发现了一些我不理解的东西。@user1720897存储结果
int main(int argc, char **argv) {
    unsigned int ptr1 = *((unsigned int *)(argv[1]));
    printf("ptr1 = 0x%x\n", ptr1);
    exit(0);
}