为什么设置解除引用指针的值会引发分段错误11?为了明确我的意思,请查看以下代码:
#include <stdio.h>
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;
答案 0 :(得分:3)
这里的问题是ptr
没有指向分配的空间。请参阅以下内容:
#include <stdio.h>
#include <stdlib.h>
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;
}
答案 1 :(得分:2)
这不是'非法',只是简单的实施定义。实际上,在某些平台(例如DOS)上,需要特定的内存地址,例如将文本写入以0xB8000开头的视频缓冲区,或者将内存映射到SNES上的控制器I / O.
在大多数当前的操作系统中,出于安全原因使用了一个名为ASLR的功能,它使古老的专用地址模式成为过去,有利于通过驱动程序和内核层,这就是对于你运行它的大多数地方来说都是“非法的”。
答案 2 :(得分:1)
这里最基本的问题是你没有将ptr
分配给有效的内存地址,在某些情况下0
是有效的内存地址但通常不是。{1}}。由于ptr
在您的第一种情况下是全局变量,因此它将初始化为0
。雷米巴尔问了一个很好的跟进问题,best answer让我意识到这是一个重新宣布:
*ptr = 2;
然后您将ptr
设置为2
值,除非偶然指向有效的内存地址。
如果ptr
是本地或自动变量,那么它将是未初始化的,并且它的值将是不确定的。在C和C ++中,使用具有不确定值的指针为undefined behavior。在大多数情况下,使用NULL
指针也是未定义的行为,尽管允许实现来定义行为。
在大多数尝试访问内存的现代系统中,您的进程不拥有将导致segmentation fault。
您可以通过几种方式为ptr
分配有效的内存地址,例如:
int k = 7;
int *ptr = &k;
^
请注意使用&
获取k
的地址,或者您可以使用malloc为其动态分配内存。
答案 3 :(得分:0)
您的代码无效,但有些C编译器可能允许它与旧版本的语言兼容。
语句(包括赋值语句)如果出现在函数体外,则是非法的(语法错误)。
你有:
int *ptr;
*ptr = 2;
在文件范围内。第一行是int*
对象的有效声明,名为ptr
,隐式初始化为空指针值。第二行看起来像,就像赋值语句一样,但由于它在函数之外,编译器很可能甚至不会尝试以这种方式解释它。 gcc将其视为声明。旧版本的C允许您在声明中省略类型名称; C99删除了“隐式int
”规则。所以gcc对待
*ptr = 2;
等同于
int *ptr = 2;
并产生以下警告:
c.c:4:1: warning: data definition has no type or storage class [enabled by default]
c.c:4:8: warning: initialization makes pointer from integer without a cast [enabled by default]
第一个警告是因为您从声明中省略了int
(或其他类型名称)。第二个是因为2
是int
类型的值,并且您正在使用它来初始化int*
类型的对象;没有从int
到int*
的隐式转换(除了空指针常量的特殊情况)。
一旦你超越了它,你有两个相同对象的声明 - 但它们是兼容的,所以这是允许的。并且指针变量被初始化为(int*)2
,这是一个垃圾指针值(在内存地址0x00000002
上不太可能有用)。
在main
功能中,您可以:
printf("%d\n", *ptr);
尝试在该内存地址处打印int
对象的值。由于该地址不太可能是您的程序有权访问的地址,因此分段错误并不是一个令人惊讶的结果。 (更一般地说,行为未定义。)
(这是C中一个相当常见的问题:程序中的小错误可能会导致某些内容仍然编译,但与您的意图完全不同。我想到的方式是C的语法相对“密集” ;对有效程序的小型随机调整通常会产生不同但语法上有效的程序,而不是创建语法错误。)
这就是你的程序实际上做的事情;我确定这不是你打算做的。
深吸一口气,继续阅读。
这可能更接近你的意图:
#include <stdio.h>
int *ptr;
int main(void) {
*ptr = 2;
printf("%d\n", *ptr);
return 0;
}
由于现在没有ptr
的初始化器,因此它被隐式初始化为空指针值。 (如果在ptr
中定义main
,则其初始值将为垃圾。)赋值语句尝试取消引用该空指针,从而导致分段错误(同样,行为未定义;分段错误可能是一个结果)。执行永远不会到达printf
电话。
我认为
*ptr=2
会将指针ptr
指向的rvalue设置为2.是不是这样?
不完全。指针不指向右值; “rvalue”仅仅是表达式的价值。指针指向对象(如果它们指向任何东西)。作业
*ptr = 2;
会将值2
分配给ptr
指向的对象 - 但ptr
不指向对象!
现在让我们看一下你的程序实际可行的版本:
#include <stdio.h>
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