正如您在问题中所说,该声明printf("%d\n", *c);
暴露了未定义的行为。由于undefined,您对它的任何期望都是错误的。这包括获得特定错误或任何错误。
C 运行时库不会检查您的程序访问内存的方式。如果它这样做,程序将运行得非常非常慢。关于堆,C 运行时库做了一些基本的检查;例如,它在程序启动期间将某个值放在地址0
上,并在程序结束时检查该值是否仍然存在。如果值发生了变化,那么一个空指针被取消引用以进行写入,它可以警告你这一点。
之后c = c + 20;
,c
很可能指向属于您的程序的一块内存。它可以是堆上的一个空闲区域,它可以在堆管理器用来处理堆的数据结构中,但很有可能它仍然在同一个内存页面上。
如果您运气不好并且c + 20
落在存储的内存页面之外,c
那么就会发生由操作系统处理的异常情况。它终止程序并显示与您在问题中列出的错误消息类似的错误消息(想法相同,每个操作系统上的单词和演示文稿都不同)。
更新
分配内存不是某种魔法。该程序从一个由操作系统为此目的分配给程序的内存块(称为“堆” )开始。
C 运行时库包含管理堆的代码。此代码使用此内存的一小部分进行簿记。一个常见的实现使用双链表,列表中每个节点的有效负载是程序使用<memory.h>
(malloc()
等calloc()
) 中声明的函数“分配”的内存块。当调用malloc()
发生时,此代码运行,在列表中创建一个新节点并返回节点有效负载的地址(堆内的地址)。
程序根据需要使用此指针。例如,您的程序可以在 上自由编写c-1
。事实上,它里面malloc()
居然写了信息那里。malloc()
返回后c
,你的代码也可以写在c-1
。从这个OS
角度来看,这两种写操作没有区别。而且由于C
不是托管或解释语言,因此您的程序中没有包含任何代码来监视您编写的代码所做的事情或握住它的手不要写在错误的地方。
如果您在此处写入,c-1
则很有可能会破坏堆管理器使用的数据结构。不会立即发生任何错误。没有显示错误消息,并且您的程序继续运行,显然是正常的。但是在下一次调用处理堆的函数(无论是内存分配还是释放)时,程序将开始造成严重破坏。堆数据结构被破坏任何事情都可能发生。
关于CBMC,我不知道它是如何工作的。也许它无法检测到这种情况。c
或者它可能会报告您的程序是安全的,因为它在递增后没有写入。