为什么将derefernce指针设置为等于原始非法?

为什么设置解除引用指针的值会引发分段错误11? 为了明确我的意思,请查看以下代码:

#include  int *ptr; *ptr = 2; int main(){ printf("%d\n", *ptr); return 0; } 

我认为* ptr = 2会将指针ptr指向的rvalue设置为2.是不是这样? 对于那些c专家程序员,我很抱歉,这真的很容易/很明显。

如果该值具有内存地址,我们是否只允许将取消引用的指针(即* ptr)设置为值? 即喜欢做:

 int k = 7; int *ptr = k; 

然后:

 *ptr = 2; 

这里的问题是ptr没有指向分配的空间。 请参阅以下内容:

 #include  #include  int main(void){ // Create a pointer to an integer. // This pointer will point to some random (likely unallocated) memory address. // Trying set the value at this memory address will almost certainly cause a segfault. int *ptr; // Create a new integer on the heap, and assign its address to ptr. // Don't forget to call free() on it later! ptr = malloc(sizeof(*ptr)); // Alternatively, we could create a new integer on the stack, and have // ptr point to this. int value; ptr = &value; // Set the value of our new integer to 2. *ptr = 2; // Print out the value at our now properly set integer. printf("%d\n", *ptr); return 0; } 

它不是“非法的”,只是简单的实现定义。 实际上,在某些平台(例如DOS)上,需要特定的内存地址,例如将文本写入以0xB8000开头的video缓冲区,或者将内存映射到SNES上的控制器I / O.

在大多数当前的操作系统中,出于安全原因使用了一个名为ASLR的function,这使得专用地址的古老模式成为过去,有利于通过驱动程序和内核层,这使得它对大多数地方来说是“非法的”你会运行它。

这里最基本的问题是你没有将ptr分配给有效的内存地址,在某些情况下, 0是有效的内存地址,但通常不是。 由于ptr在第一种情况下是全局变量 ,因此它将初始化为0 。 雷米巴尔问了一个很好的后续问题, 最好的回答让我意识到这是一个重新宣布:

 *ptr = 2; 

然后你将ptr设置为值为2 ,除非偶然指向有效的内存地址。

如果ptr是一个本地或自动变量,那么它将是未初始化的,它的值将是不确定的。 在C和C ++中使用具有不确定值的指针是未定义的行为 。 虽然允许实现定义行为,但在大多数情况下使用NULL指针也是未定义的行为。

在大多数试图访问内存的现代系统中,您的进程不拥有将导致分段错误 。

您可以通过几种方式为ptr分配有效的内存地址,例如:

 int k = 7; int *ptr = &k; ^ 

注意使用of来获取k的地址,或者你可以使用malloc为它动态分配内存。

您的代码无效,但某些C编译器可能允许它与旧版本的语言兼容。

如果语句出现在函数体外,则语句(包括赋值语句)是非法的(语法错误)。

你有:

 int *ptr; *ptr = 2; 

在文件范围。 第一行是一个名为ptrint*对象的有效声明,隐式初始化为空指针值。 第二行看起来像赋值语句,但由于它在函数外部,编译器很可能甚至不会尝试以这种方式解释它。 gcc将其视为声明 。 旧版本的C允许您在声明中省略类型名称; C99删除了“隐含int ”规则。 所以gcc对待

 *ptr = 2; 

相当于

 int *ptr = 2; 

并产生以下警告:

 cc:4:1: warning: data definition has no type or storage class [enabled by default] cc:4:8: warning: initialization makes pointer from integer without a cast [enabled by default] 

第一个警告是因为您从声明中省略了int (或其他类型名称)。 第二个是因为2int类型的值,并且您正在使用它来初始化int*类型的对象; 没有从intint*隐式转换(除了空指针常量的特殊情况)。

一旦你越过它,你有两个相同对象的声明 – 但它们是兼容的,所以这是允许的。 并且指针变量被初始化为(int*)2 ,这是一个垃圾指针值(在内存地址0x00000002可能没有任何用处)。

在您的mainfunction中,您可以:

 printf("%d\n", *ptr); 

它试图在该内存地址处打印int对象的值。 由于该地址不太可能是您的程序有权访问的地址,因此分段错误并不是一个令人惊讶的结果。 (更一般地说,行为未定义。)

(这在C中是一个相当普遍的问题:程序中的小错误可能会导致某些内容仍然编译,但与您的意图完全不同。我想到的方式是C的语法相对“密集”;小随机调整到有效的程序通常会生成不同但语法上有效的程序,而不是创建语法错误。)

这就是你的程序实际上做的事情; 我确定这不是你打算做的。

深呼吸,继续阅读。


这里的东西可能更接近你的意图:

 #include  int *ptr; int main(void) { *ptr = 2; printf("%d\n", *ptr); return 0; } 

由于现在没有ptr的初始化器,因此它被隐式初始化为空指针值。 (如果在main中定义了ptr ,它的初始值将是垃圾。)赋值语句尝试取消引用该空指针,导致分段错误(同样,行为未定义;分段错误可能是结果)。 执行永远不会到达printf调用。

我认为*ptr=2会将指针ptr指向的rvalue设置为2.是不是这样?

不完全的。 指针不指向右值; “rvalue”仅仅是表达式的价值。 指针指向对象 (如果它们指向任何东西)。 分配

 *ptr = 2; 

2赋给ptr指向的对象 – 但是ptr不指向对象!

现在让我们看一下你的程序实际可行的版本:

 #include  int *ptr; int variable; int main(void) { ptr = &variable; *ptr = 2; printf("*ptr = %d\n", *ptr); printf("variable = %d\n", variable); return 0; } 

现在ptr指向一个对象, *ptr = 2为该对象赋值。 输出是:

 *ptr = 2 variable = 2