C 语言程序中与存储器相关的常见错误(即指针相关)
来源:互联网 发布:python 3.x运算符 编辑:程序博客网 时间:2024/04/29 17:45
下面就来摘录一些常见的错误:
1. 间接引用坏指针(Dereferencing Bad Pointers)
在进程的虚拟地址空间中有较大的洞, 没有映射到任何有意义的数据. 如果我们试图间接引用一个指向这些洞的指针, 那么操作系统就会以段异常(segmentation exception)中止我们的程序. 而且, 虚拟存储器的某些区域是只读的. 试图写这些区域将会以保护异常(protection exception)中止这个程序.
间接引用坏指针的一个常见实例就是经典的 scanf 错误. 假设我们想要使用 scanf 从 stdin 读一个整数到一个变量. 正确的方法是传递给 scanf 一个格式串和变量的地址:
- scanf("%d", &val); // 正确的方法
然而, 对于初学 C 语言的人而言, 很容易传递 val 的内容, 而不是他的地址:
- scanf("%d", val); // 错误的方式
- // GCC 一般会给出警告:格式 ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’ [-Wformat]|
在这种情况下, scanf 将把变量的内容解释为一个地址, 并试图将一个字写到这个位置. 在最好的情况下, 程序立即以异常中止. 在最糟糕的情况下, val 的内容将对应于虚拟存储器的某个合法的读/写区域, 于是我们就覆盖了存储器, 这通常会在相当长的时间后才会造成灾难性且令人困惑的后果.
2. 读未初始化的存储器(Reading Uninitialized Memory)
虽然 bss 存储器位置(未初始化的全局变量)总是被加载器初始化为零(链接器一般会分配有 BSS 段), 但是对于堆存储器(heap memory)却不是这样的. 一个常见的错误就是假设堆存储器被初始化为零:
- /* Return y = Ax */
- int *matvec(int **A, int *x, int n)
- {
- int i, j;
- int *y = (int *)malloc(n * sizeof(int));
- for (i = 0; i < n; i++)
- for (j = 0; j < n; j++)
- y[i] += A[i][j] * x[j];
- return y;
- }
3. 允许栈缓冲区溢出(Allowing Stack Buffer Overflows)
如果一个程序不检查输入串的大小就写入栈中的目标缓冲区, 那么这个程序就会有缓冲区溢出错误(buffer overflow bug). 例如, 下面的函数就有缓冲区溢出错误, 因为 gets 函数拷贝一个任意长度的串到缓冲区. 为了纠正这些错误, 我们必须使用 fgets 函数, 这个函数限制了输入串的大小:
- void bufoverflow()
- {
- char buf[64];
- gets(buf); /* Here is the stack buffer overflow bug */
- return;
- }
Same Size)
一个常见的错误是假设指向对象的指针和他们所指向的对象是相同大小的:
- /* Create an nxm array */
- int **makeArray1(int n, int m)
- {
- int i;
- int **A = (int **)malloc(n * sizeof(int)); // 这个......
- for (i = 0; i < n; i++)
- A[i] = (int *)malloc(m * sizeof(int));
- return A;
- }
这段代码只有在 int 和指向 int 的指针大小相同的机器上运行良好. 但是, 如果我们在像 core i7 这样的机器上运行这段代码, 其中指针大于 int, 那么第七行和第八行的循环将写到超过 A 数组结尾的地方. 因为这些字中的一个很可能是已分配块的边界标记脚部, 所以我们可能不会发现这个错误, 直到我们在这个程序的后面很久释放这个块时, 此时, 分配器中的合并代码会戏剧性的失败, 而没有任何明显的原因. 这是"在远处起作用(action at distance)" 的一个隐秘示例, 这个"action at distance" 是与存储器有关(memory-related)的编程错误的典型情况.
5. 造成错位错误(Making Off-by-One Errors)
错位(off-by-one)错误是另一个很常见的覆盖错误来源:
- /* Create an nxm array */
- int **makeArray2(int n, int m)
- {
- int i;
- int **A = (int **)malloc(n * sizeof(int *));
- for (i = 0; i <= n; i++)
- A[i] = (int *)malloc(m * sizeof(int));
- return A;
- }
6. 引用指针, 而不是他所指向的对象(Referencing a Pointer Instead of the Object It Points to)
如果不注意 C 操作符的优先级和结合性, 我们就会错误的操作指针, 而不是他所指向的对象. 比如, 下面的这个函数, 其目的是删除一个有 *size 项的二叉堆里的第一项, 然后对剩下的 *size - 1 项重新建堆:
- int *binheapDelete(int **binheap, int *size)
- {
- int *packet = binheap[0];
- binheap[0] = binheap[*size - 1];
- *size--; /* This should be (*size)-- */
- heapify(binheap, *size, 0);
- return(packet);
- }
7. 误解指针运算(Misunderstanding Pointer Arithmetic)
另一种常见的错误是忘了指针的算术操作是以他们指向的对象的大小为单位来进行的, 而这种大小单位并不一定是字节. 例如, 下面函数的目的是扫描一个 int 数组, 并返回一个指针, 指向 val 的首次出现:
- int *search(int *p, int val)
- {
- while (*p && *p != val)
- p += sizeof(int); /* Should be p++ */
- return p;
- }
8. 引用不存在的变量(Referencing Nonexistent Variables)
没有太多经验的 C 程序员不理解栈的规则, 有时会引用不再合法的本地变量, 如下列所示:
- int *stackref(void)
- {
- int val;
- return &val;
- }
这个函数返回一个指针(如 p), 指向栈里的一个局部变量, 然后弹出它的栈帧. 尽管 p 仍然指向一个合法的存储器地址, 但是它已经不再指向一个合法的变量了. 当以后在程序中调用其他函数时, 存储器将重用他们的栈帧. 再后来, 如果程序分配某个值给 *p, 那么它可能实际上正在修改另一个函数的栈帧中的一个条目, 从而潜在的带来灾难性的后果. 要是没有发现就悲剧了.
9. 引用空闲堆块中的数据(Referencing Data in Free Heap Blocks)
一个相似的错误就是引用已经被释放了的堆块中的数据. 例如, 考虑下面的例子, 这个实例在第 6 行分配了一个整数数组 x, 在第 10 行中先释放了块 x, 然后在第 14 行中又引用了它:
- int *heapref(int n, int m)
- {
- int i;
- int *x, *y;
- x = (int *)malloc(n * sizeof(int));
- /* ... */ /* Other calls to malloc and free go here */
- free(x);
- y = (int *)malloc(m * sizeof(int));
- for (i = 0; i < m; i++)
- y[i] = x[i]++; /* Oops! x[i] is a word in a free block */
- return y;
- }
10. 引起存储器泄露(Introducing Memory Leaks)
存储器泄露是缓慢, 隐性的杀手(slow, silent killers), 当程序员不小心忘记释放已分配块, 而在堆里创建了垃圾时, 会发生这种问题. 例如, 下面的函数分配了一个堆块 x, 然后不释放他就返回:
- void leak(int n)
- {
- int *x = (int *)malloc(n * sizeof(int));
- return; /* x is garbage at this point */
- }
- C 语言程序中与存储器相关的常见错误(即指针相关)
- C 语言程序中与存储器相关的常见错误(即指针相关)
- C语言入门程序与存储器常见的有关错误
- c程序中常见的与存储器有关的错误
- C程序中常见的与存储器有关的错误
- C程序中常见的与存储器有关的错误
- 与指针相关的常见错误
- C语言中编译相关的常见错误
- 关于C语言等程序中指针相关的问题
- C程序中常见的存储器有关的错误
- C/C++中常见的与存储器有关的错误
- Linux中crontab自动执行程序相关命令与常见的错误
- C语言之指针与字符串的相关操作
- LinuxC简谈之数组、字符串与指针相关的常见错误与问题
- C/C++中指针与数组的相关知识
- c语言中的指针(包括数组与指针相关)
- 深入理解计算机系统学习笔记(三)之C语言常见的与存储器(内存)有关的错误
- C语言指针相关知识点
- ZOJ-2027 Travelling Fee
- jni学习实例(二)-a7105模块驱动之jni
- HDU 4772 Zhuge Liang's Password 选择矩阵
- 实现算法2.11、2.12的程序
- 用JS对登录时提交的信息进行判断
- C 语言程序中与存储器相关的常见错误(即指针相关)
- 用单链表结构实现算法2.2的程序
- 动态性能视图
- 儿童有人图样图衣裤
- 用单链表实现算法2.1
- 关于浮点数和IEEE754标准的一点理解
- IDE集成环境快不为人知的快捷键
- Ubuntu新安装 功能软件集合
- uva 193(回溯)