在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. C++中Lambda表达式转化为函数指针

    // ----------------------------------------------------------- auto combineCallbackLambda = [](GLdou ...

  2. python常用库简单使用( PyPDF2 )

    PyPDF2学习 1  这个模块的名字对大小写是敏感的,所以,确保y是小写的,其他字母都是大写的

  3. JavaScript Location 对象用法

    Location 对象 Location对象包含有关当前URL的信息.location对象是window对象的一部分,可以通过window.location属性访问. 注意:没有适用于location ...

  4. pushad与popad

    版权声明:本文为博主原创文章,转载请附上原文出处链接和本声明.2019-08-24,00:40:12作者By-----溺心与沉浮----博客园 PUSHAD与POPAD 这两条指令其实就是讲EAX,E ...

  5. secruity

    security3.x <?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns=& ...

  6. LeetCode——Employees Earning More Than Their Managers

    The Employee table holds all employees including their managers. Every employee has an Id, and there ...

  7. Spring Cloud Netflix之Eureka服务消费者

    Eureka服务消费者介绍 Eureka服务消费者用于发现服务和消费服务,发现服务通过Eureka Client完成,消费服务通过Ribbon完成,以实现负载均衡.在实际项目中,一个服务往往同时是服务 ...

  8. 防止xss攻击的前端的方法

    项目当中在进行安全测试的时候,遇到了xss的攻击,要求前端来做个防御,针对于遇到的xss攻击,做个总结 1.xss---存储型xss的攻击 前端只要在接收到后台数据的时候做个特殊字符的过滤,即可抵制攻 ...

  9. K8s的api gateway---ambassador实操

    对于api gateway,以前总是认知感觉和proxy差不多. 最近几天,撸完了ambassador的官方文档,才比较系统的了解了gateway的功能. 它和传统的nginx proxy或是k8s里 ...

  10. Rocketmq原理&最佳实践

    MQ背景&选型 消息队列作为高并发系统的核心组件之一,能够帮助业务系统解构提升开发效率和系统稳定性.主要具有以下优势: 削峰填谷(主要解决瞬时写压力大于应用服务能力导致消息丢失.系统奔溃等问题 ...