深入理解C语言 - 指针使用的常见错误
在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语言 - 指针使用的常见错误的更多相关文章
- R语言编程中的常见错误
R语言编程中的常见错误有一些错误是R的初学者和经验丰富的R程序员都可能常犯的.如果程序出错了,请检查以下几方面. 使用了错误的大小写.help().Help()和HELP()是三个不同的函数(只有第 ...
- "深入理解C语言" 指针
本文对coolshell中的"深入理解C语言"这篇文章中提到的指针问题, 进行简要的分析. #include <stdio.h> int main(void){ ]; ...
- 深入理解C语言 - 指针详解
一.什么是指针 C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址.CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位.这里,数据对象是指存储在 ...
- 理解C语言中几个常见修饰符
写在前面 今天下午一个同事问「register」关键字是什么作用?噢,你说的是「register」啊,它的作用是……脑袋突然断片儿,我擦,啥意思来着,这么熟悉的陌生感.做C语言开发时间也不短了,不过好 ...
- 小强版之无码理解C语言指针
1. 先从普通变量开始 2. 撸完变量撸指针 3. 故事情节进一步发展,此处少儿不宜 4. 奶茶妹妹捉奸,小强死定了 5. 源码欣赏 #include <stdio.h> ...
- 理解C语言中指针的声明以及复杂声明的语法
昨天刚把<C程序设计语言>中"指针与数组"章节读完,最终把心中的疑惑彻底解开了.如今记录下我对指针声明的理解.顺便说下怎样在C语言中创建复杂声明以及读懂复杂声明. 本文 ...
- 这样子来理解C语言中指针的指针
友情提示:阅读本文前,请先参考我的之前的文章<从四个属性的角度来理解C语言的指针也许会更好理解>,若已阅读,请继续往下看. 我从4个属性的角度来总结了C语言中的指针概念.对于C语言的一个指 ...
- 对C语言指针的理解
一个小程序引发对于C语言指针的思考: #include <bits/stdc++.h> using namespace std; void my_swap (int* a,int* b) ...
- C语言-指针
C指针基础知识 C语言中,指针无疑是最令人头疼的.今天无事就来学学C语言的指针,在此留下点笔记,仅供个人参考. 首先要搞懂的是,指针是什么? 指针:是用来存放内存地址的变量. 不管是什么类型的指针,存 ...
随机推荐
- centos crontab定时任务用法
一.安装crond服务 yum -y update yum -y install cronie yum-cron 二.crontab任务语法 crontab任务配置基本格式: * * * * * co ...
- [Flutter] Windows桌面程序开发
在今年5月的谷歌I/O 2019大会时, 谷歌就宣布了flutter已经支持全平台开发, 包括 android, ios, mac, linux, windows, web 等 . Flutter桌面 ...
- ASP.NET Core快速入门(第5章:认证与授权)--学习笔记
课程链接:http://video.jessetalk.cn/course/explore 良心课程,大家一起来学习哈! 任务31:课时介绍 1.Cookie-based认证与授权 2.Cookie- ...
- SpringCloud的阿里巴巴相关开源组件
Sentinel 阿里巴巴开源产品,把流量作为切入点,从流量控制.熔断降级.系统负载保护等多个维度保护服务的稳定性. Nacos 阿里巴巴开源产品,一个更易于构建云原生应用的动态服务发现.配置管理和服 ...
- 倒计时3天!i春秋四周年盛典狂欢,钜惠不停
六月注定是不平凡的 感恩父亲节 父爱如山亦如海 难忘毕业季 青春无悔不散场 嗨购618 优惠福利送不停 更值得期待的是 在这个不平凡的六月 迎来了i春秋四周年庆典 当周年庆遇到618 会擦出怎样的火花 ...
- PMP备考-第三章-项目管理过程
过程:完成预定目标的,一系列相互关联的活动的集合,以便运用一些列工具与技术把特定的输入转化成特定的输出. 五大项目管理过程组:启动-规划-执行-监控-收尾 戴明环(PDCA循环):计划-实施-检查-行 ...
- 当请求进入Nginx后,每个HTTP执行阶段的作用
阶段顺序 阶段名称 作用 1 NGX_HTTP_POSTREAD_PHASE = 0 接收并读取请求阶段 2 NGX_HTTP_SERVER_REWRITE_PHASE 修改url阶段,通常有重定向和 ...
- mysql udf提权实战测试
根据前天对大牛们的资料学习,进行一次mysql udf提权测试. 测试环境: 受害者系统:centos 7.7 ,docker部署mysql5.6.46, IP:192.168.226.128 攻击者 ...
- django项目中cxselect三级联动
下载cxselect插件放在static文件夹下 前端引入 <script src="/static/js/jQuery-1.8.2.min.js"></scri ...
- Rust中的函数调用
注意区别语句和表达式哟. Rust是一门基于表示式的语言,牢记!!! fn main() { println!("Hello world!"); another_function( ...