为什么在更改 main 的签名时会出现段错误?

Why am I getting segfault when changing the signature of main?

我正在尝试接触 C,并编写了这个程序,它在随机位置显示 1 kb 的 RAM。这是代码,它工作正常:

#include <stdio.h>

int main(){
    char *mem;
    for(int i =0; i < 1024; i++){
        mem++;
        printf("%c", *mem);
    }
    return 0;
}

在那之后,我对我的代码进行了以下更改,每次 运行 我的程序都会出现段错误:

#include <stdio.h>


// Just added this signature
int main(int argc, char *argv[]){
    char *mem;
    for(int i =0; i < 1024; i++){
        mem++;
        printf("%c", *mem);
    }
    return 0;
}

我的蜘蛛感觉告诉我,我得到的段错误是随机的,也应该是在第一个例子中引起的,但是 运行一次又一次地使用不同的程序让它看起来像是可预测的行为。

$ gcc -v
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/c++/4.2.1
Apple LLVM version 7.3.0 (clang-703.0.31)
Target: x86_64-apple-darwin15.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

当您尝试

时,您的两个代码段都会调用 undefined behavior
  1. 越界(mem++;,没有分配)
  2. 使用未初始化的值(访问 *mem

使用当前版本。

记住,指针不会神奇地继承(或获取)内存,一般来说,您需要使指针指向有效的东西。

您从未初始化 mem,因此其内容未定义。当您尝试使用 ++ 增加它或取消引用指针时,您会得到 undefined behavior.

未定义行为可能发生的事情之一是程序可能看起来正常运行,而进行看似无关的更改会导致崩溃。

您需要初始化mem。我猜你只是想读取随机内存,但这是不允许的。例如,您可能正在尝试读取由不同进程使用的内存,或者您可能正在尝试读取一些在您的计算机中甚至不存在的地址。

通过更改 main 的签名,您已经更改了 mem 中的随机垃圾值。它可能的工作方式是 mem 从某个寄存器中获取随机值。当您修改函数签名时,argcargv 正在使用这些寄存器。因此 mem 正在获取垃圾堆栈值的不同垃圾寄存器值。在任何情况下,您都不应该尝试跟随垃圾指针。

仅仅因为它在一个示例中有效,只意味着你很幸运。你仍然不应该这样做。稍微改一下很可能就不行了。

mem的值未定义(未初始化),但不是随机的。如果在调用 main 之前调用了其他 C 运行时函数,则 mem 使用的堆栈槽中可能有一个有效指针。将参数添加到 main 更改使用哪个插槽并更改行为。这可能意味着代码不会崩溃,尽管它不正确。