在C语言中,指针的重要性不言而喻,但在很多时候指针又被认为是一把双刃剑。一方面,指针是构建数据结构和操作内存的精确而高效的工具。另一方面,它们又很容易误用,从而产生不可预知的软件bug。下面总结一下指针使用的常见错误。

一、使用未初始化的指针

这个错误很常见,指针未初始化时,系统会给指针分配个随机地址,示例如下:

int *p; //或者 int *p = NULL;
···
*p = 10; //错误,指针未初始化

上述程序将值10写到未知的内存位置,如果p指向系统内存空间,这样很可能把系统本来地址里的内容给覆盖了,会导致程序或者系统的崩溃。

二、没有释放内存

在堆中开辟内存以后,使用完成必须释放内存,否则会造成内存泄漏,示例如下:

int *p = (int *)malloc(100);
···
free(p);
p = NULL;

三、不断修改内存指针变量

很多时候使用指针开辟了内存空间,然后如果对指针指向进行改变操作,操作完成后直接释放内存,会释放了不该释放的位置;另外程序丢失了对已开辟内存空间的控制,造成内存泄漏,示例如下:

//这种时候一般会定义两个指向同一个开辟的内存空间的指针变量,一个用于操作,一个用于释放,避免造成内存泄漏
char *p = (char *)malloc(100);
strcpy(p, "abcdefg");
p += 1; //对指针指向进行改变操作
*p = 'a';
free(p);
p = NULL;

四、注意"野指针"

"野指针"不是NULL指针,是指向“垃圾”内存的指针。人们一般不会错用NULL指针,因为用if语句很容易判断。但是“野指针”是很危险的,if语句对它不起作用。“野指针”的成因主要有两种:

第一种,指针变量没有初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。示例如下:

char *p; //错误,未初始化,成为野指针

第二种,指针变量被free或者delete之后,没有置为NULL,让人误以为是个合法的指针。示例如下:

char *p = (char *)malloc(100);
...
free(p); //错误,没有置为NULL,成为野指针

五、指针参数申请内存的常见错误

如果函数的参数是一个指针,不要指望用该指针去申请动态内存,示例如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h> void getMemory(char *p, int num)
{
p = (char*)malloc(sizeof(char) * num);
} int main()
{
char *pStr = NULL;
pStr = getMemory(pStr, 200);
strcpy(pStr, "hello world!"); //错误,pStr仍然是空指针
printf("%s", pStr);
free(pStr);
pStr = NULL; return 0;
}

上面getMemory(str, 200)并没有使pStr获得期望的内存,pStr依旧是NULL,为什么?

问题出在getMemory函数中。编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是_p,编译器使_p = p。在本例中,_p申请了新的内存,只是把_p所指的内存地址改变了,但是p丝毫未变,仍然是空指针。事实上,每执行一次getMemory函数就会泄漏一块内存,因为没有用free释放内存。

指针参数申请内存的两种正确用法

第一种用法:使用函数返回值来传递动态内存,示例如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h> char* getMemory(char *p, int num)
{
p = (char *)malloc(sizeof(char) * num);
return p;
} int main()
{
char *pStr = NULL;
pStr = getMemory(pStr, 200);
strcpy(pStr, "hello world!");
printf("%s", pStr);
free(pStr);
pStr = NULL; return 0;
}

这里强调不要用return语句返回指向”栈内存“的指针,因为”栈内存“指针在函数结束时已经被自动回收。

第二种用法:使用指向指针的指针作为参数来传递动态内存,示例如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h> void getMemory(char **p, int num)
{
*p = (char *)malloc(sizeof(char) * num);
} int main()
{
char *pStr = NULL;
getMemory(&pStr, 200);
strcpy(pStr, "hello world!");
printf("%s", pStr);
free(pStr);
pStr = NULL; return 0;
}

在需要修改指针变量本身的时候,需要使用指向指针的指针作为参数,这也是传值与传地址的差别所在。

参考:

深入理解C语言-指针使用的常见错误

深入理解C语言 - 指针使用的常见错误的更多相关文章

  1. R语言编程中的常见错误

    R语言编程中的常见错误有一些错误是R的初学者和经验丰富的R程序员都可能常犯的.如果程序出错了,请检查以下几方面. 使用了错误的大小写.help().Help()和HELP()是三个不同的函数(只有第 ...

  2. "深入理解C语言" 指针

    本文对coolshell中的"深入理解C语言"这篇文章中提到的指针问题, 进行简要的分析. #include <stdio.h> int main(void){ ]; ...

  3. 深入理解C语言 - 指针详解

    一.什么是指针 C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址.CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位.这里,数据对象是指存储在 ...

  4. 理解C语言中几个常见修饰符

    写在前面 今天下午一个同事问「register」关键字是什么作用?噢,你说的是「register」啊,它的作用是……脑袋突然断片儿,我擦,啥意思来着,这么熟悉的陌生感.做C语言开发时间也不短了,不过好 ...

  5. 小强版之无码理解C语言指针

     1. 先从普通变量开始   2. 撸完变量撸指针   3. 故事情节进一步发展,此处少儿不宜   4. 奶茶妹妹捉奸,小强死定了   5. 源码欣赏  #include <stdio.h> ...

  6. 理解C语言中指针的声明以及复杂声明的语法

    昨天刚把<C程序设计语言>中"指针与数组"章节读完,最终把心中的疑惑彻底解开了.如今记录下我对指针声明的理解.顺便说下怎样在C语言中创建复杂声明以及读懂复杂声明. 本文 ...

  7. 这样子来理解C语言中指针的指针

    友情提示:阅读本文前,请先参考我的之前的文章<从四个属性的角度来理解C语言的指针也许会更好理解>,若已阅读,请继续往下看. 我从4个属性的角度来总结了C语言中的指针概念.对于C语言的一个指 ...

  8. 对C语言指针的理解

    一个小程序引发对于C语言指针的思考: #include <bits/stdc++.h> using namespace std; void my_swap (int* a,int* b) ...

  9. C语言-指针

    C指针基础知识 C语言中,指针无疑是最令人头疼的.今天无事就来学学C语言的指针,在此留下点笔记,仅供个人参考. 首先要搞懂的是,指针是什么? 指针:是用来存放内存地址的变量. 不管是什么类型的指针,存 ...

随机推荐

  1. LeetCode 1293. Shortest Path in a Grid with Obstacles Elimination

    题目 非常简单的BFS 暴搜 struct Node { int x; int y; int k; int ans; Node(){} Node(int x,int y,int k,int ans) ...

  2. vue入门案例

    1.技术在迭代,有时候你为了生活没有办法,必须掌握一些新的技术,可能你不会或者没有时间造轮子,那么就先把利用轮子吧. <!DOCTYPE html> <html> <he ...

  3. 查询SAP系统支持的ABAP版本

    7.52可以使用select 内表,但是怎么看版本呢? 如果有在开发中用到ABAP 7.4&7.5个版本的新语法时,需要考虑到系统支持的ABAP版本,那么要怎么查看呢? 其实这个和SAP的内核 ...

  4. SqlServer 查看数据库、添加数据文件

    一.查看SqlServer实例的数据库列表 1).直接在SSMS(SqlServer Management Studio)管理工具里面 展开实例下面的所有数据库便可查看  2).使用Transact- ...

  5. 【WPF入门视频】Microsoft ToDo 应用项目实战

    项目实战视频地址 第一天 第二天 第三天 第四天 第五天 第六天 项目实战源代码下载地址: 项目源代码下载

  6. AES加解密异常java.security.InvalidKeyException: Illegal key size

    AES加解密异常 Java后台AES解密,抛出异常如下:java.security.InvalidKeyException: Illegal key size Illegal key size or ...

  7. js字符串转为数字方法parseInt()、减号、乘号、JSON.parse()、Number()的效率比较

    var a = '1'; var b = '0x1'; var runTest = function(timeTag, testFunction) { console.time(timeTag); f ...

  8. maven 学习---Maven 构建配置文件

    什么是构建配置文件? 构建配置文件是一组配置的集合,用来设置或者覆盖 Maven 构建的默认配置.使用构建配置文件,可以为不同的环境定制构建过程,例如 Producation 和 Developmen ...

  9. VMware虚拟机可与Win10物理机互ping并可访问互联网的设置方法

     一.系统环境: VMware 15.Windows 10 1903.Windows 7 虚拟机 二.具体步骤: 1.Win10物理机,控制面板-所有控制面板项-网络连接-物理网卡适配器右键-属性-共 ...

  10. 【Idea】idea中编译后无法提示错误信息的解决方案