在我的应用程序中,我有一对嵌套的循环,它们遵循类似嵌套的链表以解析数据。我犯了一个愚蠢的错误,并将一个结构转换为子结构,例如:
if (((ENTITY *) OuterEntityLoop->data)->visible == true) {
而不是:
if (((ENTITY_RECORD *) OuterEntityLoop->data)->entity->visible == true) {
这导致了一个问题,即大约 70% 的运行会导致应用程序完全停止 - 不会崩溃,只是坐着旋转。程序流中的诊断printf
将以奇怪的顺序触发或根本不触发,尽管它在大多数情况下自发恢复了几次,但它破坏了应用程序。
所以事情是这样的。即使在将内部逻辑缩减为绝对不是基于逻辑错误的无限循环之后,循环只包含我的printf
,它仍然被破坏了。
事情二:当结构被错误地识别时,如果我尝试访问一个不存在的属性,即使它没有现有的属性,它仍然会抱怨。
我的问题是:
- 为什么会损坏记忆?简单地读取垃圾内存会破坏程序的控制结构吗?如果没有,这是否意味着即使电子围栏不再抱怨,我仍然在某处有泄漏?
- 我认为它抱怨不存在的属性的原因是因为它按照给定的类型定义,而不是实际存在的东西。现在我已经打出来了,这在我心中就不那么可疑了,但我想确认我在这里没有偏离基础。
当程序访问无效内存时,甚至用于读取时,真的不知道会发生什么。 在某些系统上,任何内存读取操作要么有效,要么导致程序立即崩溃,但在其他系统上,错误读取可能会被误解为执行某些操作的信号。 您没有指定您使用的是PC还是嵌入式系统,但是在嵌入式系统上,通常有许多地址在被读取时触发各种操作[例如,从串行端口取消排队接收的数据,或确认中断];错误读取此类地址可能会导致串行数据丢失,或者可能导致中断控制器认为中断已被处理,而实际上并非如此。
此外,在某些嵌入式系统中,尝试读取无效地址可能会产生其他更糟糕的影响,这些影响并非真正设计使然,而是偶然性。 例如,在我设计的一个系统上,我必须连接一个内存设备,该设备在读取周期后下车有点慢。 如果下一次内存读取是从至少有一个等待状态或位于不同总线上的内存区域执行的,则不会有问题。 但是,如果在快速外部存储器分区中运行的代码试图读取该区域,则存储设备无法快速下车会损坏下一个获取指令的某些位。 所有这一切的净效果是,从位于某些地方的代码访问慢速设备没有问题,但是从位于快速分区中的代码访问它 - 有意或无意 - 会导致奇怪且不可重现的故障。
欢迎来到C,在这里,铸造的强大功能允许你使任何内存看起来像你想要的任何对象,但风险自负。 如果您强制转换的东西实际上不是该类型的对象,并且该类型包含指向其他内容的指针,则您将面临崩溃的风险。 因为即使尝试读取尚未实际映射到进程虚拟内存地址空间的随机内存也可能导致内核,或者从没有读取权限的某些内存区域读取也会导致内核,如 NULL 指针。
例:
#include <stdio.h>
#include <stdlib.h>
struct foo
{
int x;
int y;
int z;
};
struct bar
{
int x;
int y;
struct foo *p;
};
void evil_cast(void *p)
{
/* hmm... maybe this is a bar pointer */
struct bar *q = (struct bar *)p;
if (q != NULL) /* q is some valid pointer */
{
/* as long as q points readable memory q->x will return some value, */
/* this has a fairly high probability of success */
printf("random pointer to a bar, x value x(%d)n", q->x);
/* hmm... lets use the foo pointer from my random bar */
if (q->p != NULL)
{
/* very high probabilty of coring, since the likely hood that a */
/* random piece of memory contains a valid address is much lower */
printf("random value of x from a random foo pointer, from a random bar pointer x value x(%d)n", q->p->x);
}
}
}
int main(int argc, char *argv[])
{
int *random_heap_data = (int *)malloc(1024); /* just random head memory */
/* setup the first 5 locations to be some integers */
random_heap_data[0] = 1;
random_heap_data[1] = 2;
random_heap_data[2] = 3;
random_heap_data[3] = 4;
random_heap_data[4] = 5;
evil_cast(random_heap_data);
return 0;
}