c ++指针段错误没有编译器警告,在先前分配时有效

c++ pointer segfaults without compiler warning, works when previously assigned

我很难理解指针,how/when它们失败了。所以我做了一个小程序,它创建一个指针,给它赋值,然后打印那个值。使用 gcc 和 clang 都可以很好地编译,并且在使用 -Wall 开关时不会发出任何警告。为什么它会发生段错误,为什么当我首先为指针分配不同的值时它不会发生段错误?我以为我已经将指针初始化到某个地方。当我只是在没有初始化的情况下声明指针时,我理所当然地收到编译器警告。然而,在这里我没有得到编译器警告,但它仍然是段错误。

#include<iostream>

int main(){
  int *b = (int*)  12;  //pointer gets initialized, so it points to somewhere
  //int a = 13;   //works fine when uncommenting this and the next line
  //b = &a;
  *b = 11;
  std::cout << "*b = " << *b << "\n";
    return 0;
}

指针是存储地址的变量。

int *b = (int*)  12;

这将 b 声明为指向类型 int 的值的指针,并使用地址 12 对其进行初始化。你知道地址 12 上有什么吗?不,你不知道。因此,您不应使用该地址。

*b = 11;

这在指针b指向的地址存储了一个整数11。然而,由于指针 b 指向地址 12,整数 11 覆盖了那个地址的东西(我们甚至不知道它覆盖了什么,因为我们不知道什么是从那里开始)。这可能会损坏堆或堆栈或程序代码,或者只是导致访问冲突,任何事情都可能发生。

但是如果你先这样做:

b = &a;

然后指针b现在指向存储变量a的地址。因此,随后在该地址写入 11 只会覆盖 a 的值(从 13 到 11),这是一个完全有效的操作,没有问题。

I just need it to point to some location that can hold an int

的确如此,但这还不够。不仅该位置能够保存 int,该位置还必须可供您的程序存储这样的 int。这意味着该位置需要是现有对象的位置(例如,类型 int 的现有变量)或能够容纳 int 的新分配的内存块。例如:

b = new int;

这将动态分配存储一个int所需的内存,并将新分配的内存地址分配给指针b

请记住,在完成此类动态分配的内存后,您应该释放它:

delete b;

否则会出现内存泄漏(至少直到整个进程退出为止)。

如今,在现代 C++ 中,很少需要使用原始指针来手动管理动态内存 allocations/deallocations。为此,您可以使用标准容器 and/or 智能指针。

指针是保存内存地址的变量。
你“可以”在你的指针中有任何内存地址,但是试图从允许你的应用程序读取的内存 space 之外读取,将触发 OS 以段错误杀死你的应用程序。

如果你允许我打个比方:

You can write on a paper the address of any person in your country. But if you try to enter that house without permission, you most probably will get stopped by the police.

返回代码:

int *b = (int*) 123; // ok, you can save what you want.
std::cout << *b << std::endl; // SEGFAULT: you are not allowed to read at 123.

当您取消注释两行代码时:

int a = 13;
b = &a;

基本上,b不是any-more指向那个被禁止的123地址,而是指向a的地址。 a 在你自己的代码里,它的内存对你来说不是禁止的,所以在这之后访问 *b 是允许的。

C++ 不禁止读取任何 hard-coded 地址(实际上它在某些情况下很有用),但您的 OS 可能不允许您弄乱该内存。

另一方面,编译器能够检测到没有初始化就使用的变量,并且可以警告这种情况。这与原始指针无关。