访问检查通过监视每个读取、写入、分配和释放操作,检查程序是否正确访问内存。
程序可能会以各种方式错误读取或写入内存,这称为内存访问错误。例如,程序可能通过 free() 调用堆块,引用已取消分配的内存块。或者,函数可能会将指针返回局部变量,而在访问该指针时,将产生错误。访问错误可能会导致程序中指针混乱,并可导致程序行为异常,包括输出错误和段违规。有些类型的内存访问错误很难发现。
运行时检查保存有一个跟踪程序使用的每个内存块状态的表。运行时检查会将每个内存操作与其涉及的内存块的状态进行对照,然后确定相应操作是否有效。可能的内存状态为:
Unallocated, initial state(未分配,初始状态)。尚未分配内存。读取、写入或释放此内存是非法操作,因为它不归程序所有。
Allocated, but uninitialized(已分配,但尚未初始化)。已为程序分配了内存,但尚未初始化此内存。写入或释放此内存是合法操作,但读取是非法操作,因为尚未初始化此内存。例如,输入函数时,已分配局部变量的堆栈内存,但未初始化此内存。
Read-only(只读)。读取只读内存是合法操作,但写入或释放只读内存是非法操作。
Allocated and initialized(已分配且已初始化)。读取、写入或释放已分配且已初始化的内存是合法操作。
使用运行时检查来查找内存访问错误与使用编译器查找程序中的语法错误没有什么不同。在这两种情况下,都会生成错误列表,并提供与每个错误对应的错误消息,说明出错原因和程序中的出错位置。在这两种情况下,应该从错误列表的顶部开始依次向下修复程序中的错误。在链锁反应下,一个错误可导致其他错误发生。因此,该链中的第一个错误是“首要原因”,修复该错误也可能会修复一些后续错误。
例如,从未初始化的内存区进行读取会创建不正确的指针,这样,取消其引用时,便会导致出现其他无效的读取或写入,而这又会导致出现另一个错误。
错误类型。
尝试访问的类型(读取或写入)。
尝试访问的大小。
尝试访问的地址。
泄漏块的大小。
有关地址的更多详细信息。例如,如果地址在邻近堆栈中,便会提供其相对当前堆栈指针的位置。如果地址在堆中,便会提供最近堆块的地址、大小和相对位置。
出错时调用堆栈(批处理模式)。
如果地址在堆中,便会提供最近堆块的分配跟踪。
出错位置。如果有行号信息,则此信息包括行号和函数。如果无行号,运行时检查会提供函数和地址。
以下示例显示的是一个典型的访问错误。
Read from uninitialized (rui): Attempting to read 4 bytes at address 0xefffee50 which is 96 bytes above the current stack pointer Variable is ”j’ Current function is rui 12 i = j;
rui-请参见从未初始化的内存中读 (rui) 错误
rua-请参见从未分配的内存中读 (rua) 错误
rob-请参见从数组越界中读 (rob) 错误
wua-请参见写入到未分配内存 (wua) 错误
wro-请参见写入到只读内存 (wro) 错误
wob-请参见写入到数组越界内存 (wob) 错误
mar-请参见未对齐读 (mar) 错误
maw-请参见未对齐写 (maw) 错误
duf-请参见重复释放 (duf) 错误
baf-请参见错误释放 (baf) 错误
maf-请参见未对齐释放 (maf) 错误
oom-请参见内存不足 (oom) 错误