我知道,在const int *ptr
我们可以更改地址但不能更改值。即,ptr
将存储在读写部分(堆栈)中,并且对象或实体将存储在数据分段的只读部分中。因此,我们可以更改指针ptr
指向的地址,但不能更改常量的对象。
int main()
{
const int *ptr=500;
(*ptr)++;
printf("%d\n",*ptr);
}
输出是将只读位置分配给* ptr
int main()
{
const int *ptr=500;
ptr++;
printf("%d\n",*ptr);
}
没有编译错误,但在运行时输出是“分段错误”。
我同意第一个问题。为什么我在第二个问题中遇到分段错误?它们究竟存放在哪里?
答案 0 :(得分:5)
分段错误的原因与您的想法不同。
不因为const
。
这是因为在执行*ptr
当您指向“某事”时,仍然不允许您访问数据(也就是取消引用指针),直到您将指针指向属于您的某些内存为止。
示例:
int x = 0;
int* p = (int*)500;
int a = *p; // Invalid - p is not pointing to any memory that belongs to the program
p = &x;
int b = *p; // Fine - p is pointing to the variable x
p++;
int c = *p; // Invalid - p is not pointing to any memory that belongs to the program
“无效”代码可能会产生分段错误。另一方面,它也可能只是执行并产生意外结果(甚至更糟:产生预期结果)。
答案 1 :(得分:1)
const int *ptr=500; // WRONG
这声明了一个局部变量,它是一个指向某个常量整数的指针。 const
只告诉编译器不允许更新(覆盖)解除引用的指针存储单元。
但是,您的代码不正确;你可能想要:
const int *ptr = (const int*)500;
指针初始化为address 500(初始化指针)。
在大多数系统上,该地址(以及以下的地址,例如sizeof(int)
以来的地址504为4)不在virtual address space之内。因此取消引用它(*ptr
)是undefined behavior,并且通常会提供一些segmentation fault。另请参阅this。
ptr将存储在读写部分(堆栈)中,对象或实体将存储在数据段的只读部分。
这是错误的。在编译时没有做任何事情来将内存区域保持为只读text segment(但是,大多数编译器在编译时将大多数文字或const
静态或全局数据定义在其中) 。只是你禁止编译器更新指向的东西(没有强制转换)。
如果在运行时需要只读内存区域,则需要向操作系统询问(例如在Linux上使用mmap(2)& mprotect(2))。 BTW保护适用于pages。
在Linux上,使用pmap(1)(或proc(5),例如从您的程序中顺序读取伪文件/proc/self/maps
。您可能想要添加
char cmdbuf[64];
snprintf(cmdbuf, sizeof(cmdbuf), "pmap %d", (int) getpid());
system(cmdbuf);
在代码中ptr
取消引用之前,以了解其虚拟地址空间是什么。
尝试
cat /proc/self/maps
和
cat /proc/$$/maps
并了解他们的输出(请注意$$
已扩展到shell的pid)。也许在你的错误程序(你应该用gcc -Wall -g
编译)上试验strace(1)。
答案 2 :(得分:1)
这里有很多混乱。
并且对象或实体将存储在数据分段的只读部分
不,不需要存储指向对象的位置。这仅在声明指向对象时由const
或static
等任何限定符/说明符确定。
const int * ptr = 500;
这不是有效的C,代码必须产生编译器消息。无法将整数赋给指针,两者之间必须有转换。 GCC在这里有一个已知的缺陷,你必须将它配置为标准编译器。 gcc -std=c11 -pedantic-errors
。
如果您的代码如const int *ptr=(int*)500;
是有效的C,那么它会将指针设置为指向地址500.如果地址500处有int
,代码将正常工作。如果没有你可以访问的内存,那么你会得到一些实现定义的行为,比如崩溃 - 内存映射超出了语言的范围。
(* PTR)++;
这不是有效的C,代码必须产生编译器消息。您不能修改只读位置。
总的来说,您的编译器配置似乎很差。正确配置的GCC会产生2个编译器错误。