在C语言中玩转指针自增

3

当我发现指针可以自增时,我开始在c语言中进行一些尝试。于是我想,为什么不试试看是否可能用这个程序来清除我的RAM:

#include <stdio.h>

int main()
{
    int number = 0;
    int *pointer = &number;
    while (true) {
        pointer++;
        *pointer = 0;
    }
    return 0;
}

这个不起作用的原因是我的操作系统,还是C语言有针对此类问题的异常处理机制?

提前致谢!


在哪个世界上这个问题“不是一个真正的问题”?有些人就是那么近乎愉快... - Ed S.
5个回答

3
在任何现代操作系统中,每个进程都有自己的地址空间。在其中,它将找到自己的可执行代码和数据。因此,您不能简单地访问“计算机的RAM”。您能做的全部是在程序的地址空间中搞砸事情。
您可以通过并行运行以下代码来轻松看到这一点:
int main()
{
    int i=0;
    printf("The address of i = %p\n",&i);
    sleep(60);
}

您(可能)会发现所有进程都打印出相同的内存地址。当然,它们并没有指向物理内存的相同位。
附注:当加载共享库(“DLL”)时,您将有多个进程指向内存中相同的物理位。这样,很多可执行代码只需为所有进程加载一次。这将是只读内存,因此使用相同库的其他进程无法更改其他进程的可执行代码。其背后的机制称为内存映射,请查看 mmap() 系统调用以获取更多信息。)

@iccthedral,没错。在各种操作系统上,这个例子清楚地证明了程序存在于它们自己的地址空间中。而访问“你的计算机的[整个]RAM”并不仅仅是通过调整指针来完成。 - mvds

3
这个问题的原因是操作系统,还是C语言本身有特殊规定呢?就语言而言,这是未定义的行为。当你篡改不属于你的内存时,由操作系统决定会发生什么。你为“number”分配了“sizeof int”字节大小的内存,将其地址存储在指针中,并继续向该块内存之外移动,直到出现明显的错误。一旦增加“pointer”并写入该位置,就会引发未定义的行为。你不能随心所欲地读写随机的内存位置(虽然你可以这样做,但程序没有保证以任何特定的方式运行)。

好的,我只是认为这样可以清除我的RAM,或者当其他程序开始被覆盖时关闭我的系统。 - Reppien
@ThaPal:就语言而言,行为是未定义的,因此您无法推断会发生什么或不会发生什么。当您越过操作系统知道您不拥有的内存边界时,操作系统可能会使用segfault终止您的应用程序,但同样,您不能指望它。 - Ed S.

2

number被声明在栈上,而你正在清除它以及其后的内存位置。所以你正在破坏栈。这可能会导致程序崩溃;这是未定义的行为。栈的破坏可能会导致你的函数看起来可以干净地退出而没有任何不良影响,但更有可能的情况是当进程尝试访问无效的内存位置时,会导致SEGV错误。


只是为了增加一点严谨性; C语言并不指代“堆栈”。它是自动存储期变量的实现细节。就C语言而言,这里可能发生任何事情。当然,在现实中,你是正确的。 - Ed S.

1

被称为内存保护的东西将会阻止您这样做。基本上,如果程序试图更改未分配的RAM部分(通常称为分段错误),操作系统将终止该程序。

在特权模式下运行程序时,例如编写设备驱动程序或针对裸机嵌入式系统进行编程,理论上可以这样做。


“理论上”,因为这是未定义行为,所以任何事情都有可能发生。 - Ed S.
以特权模式运行类似这样的东西听起来像是捣乱我的电脑的有趣方式。我甚至没有说反话。更好的是:添加一个GUI,试图解释它正在做什么,而不是按顺序清除RAM,而是随机顺序进行。 - AJMansfield
即使使用内存映射I/O,你也很难对硬件造成永久性损坏。在最坏的情况下,你需要手动重新刷写固件。 - user267885
@Tibor 我知道修复不会花费太长时间。我所说的“break”是指“无法使用”。电脑肯定会在我修复之前变得无法使用。 - AJMansfield

1

仅仅是为了支持其他人已经说过的话,你的程序很可能会在你写入一些无效或非法的内存位置时导致分段错误

为了看到这是因为更新某个随机内存地址的内容而发生的,我将展示您的程序在GDB下运行的情况。

[jrn@localhost SO]$ gcc -ggdb dumb.c 
[jrn@localhost SO]$ gdb ./a.out 
GNU gdb (GDB) CentOS (7.0.1-42.el5.centos)
Reading symbols from /home/jrn/source/SO/a.out...done.
(gdb) run
Starting program: /home/jrn/source/SO/a.out 

Program received signal SIGSEGV, Segmentation fault.
0x08048399 in main () at dumb.c:9
9               *pointer = 0;
(gdb) list
4       {
5           int number = 0;
6           int *pointer = &number;
7           while (1) {
8               pointer++;
9               *pointer = 0;
10          }
11          return 0;
12      }
13

在第9行,当您写入一些不应该写入的内存地址时,就会出现错误。操作系统使用内存保护(通常由CPU提供)来检测错误的写入。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接