欢迎关注我的个人博客:www.wuyudong.com, 更多精彩文章与您分享

指针的声明与初始化

1、不恰当的指针声明

考虑如下的声明:

int* ptr1, ptr2; // ptr1为指针,ptr2为整数

正确的写法如下:

int* ptr1, *ptr2;

用类型定义代替宏定义是一个好的习惯,类型定义允许编译器检查作用域规则,而宏定义不一定会。

使用宏定义辅助声明变量,如下所示:

#define PINT int*
PINT ptr1, ptr2;

不过结果和前面所说的一致,更好的方法是使用下面的类型定义:

typedef int* PINT;
PINT ptr1, ptr2;

2、使用指针未初始化

在使用指针之前未初始化会导致运行时错误,如下面的代码:

int* p;
...
printf("%d\n", *p);

指针p未被初始化,可能含有垃圾数据

3、处理未初始化指针

  • 总是用NULL来初始化指针
  • 用assert函数
  • 用第三方工具

把指针初始化为NULL更容易检查是否使用正确,即便这样,检查空值也比较麻烦,如下所示:

int *pi = NULL;
...
if(pi == NULL) {
//不应该解引pi
} else {
//可以使用pi
}

我们可以使用assert函数来测试指针是否为空值:

assert(pi != NULL);

指针的使用问题

缓冲区溢出

缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量,使得溢出的数据覆盖在合法数据上,理想的情况是程序检查数据长度并不允许输入超过缓冲区长度的字符,但是绝大多数程序都会假设数据长度总是与所分配的储存空间相匹配,这就为缓冲区溢出埋下隐患。操作系统所使用的缓冲区又被称为"堆栈".。在各个操作进程之间,指令会被临时储存在"堆栈"当中,"堆栈"也会出现缓冲区溢出。

下面几种情况可能导致缓冲区的溢出:

  • 访问数组元素时没有检查索引值

  • 对数组指针做指针算术运算时不够小心

  • 用gets这样的函数从标准输入读取字符串

  • 误用strcpy和strcat这样的函数

1、测试NULL

使用malloc这样的函数的时候一定要检查返回值,否则可能会导致程序的非正常终止,下面是一般的方法:

float *vector = malloc( * sizeof(float));
if(vector == NULL) {
//malloc分配内存失败
} else {
//处理vector
}

2、错误使用解引操作

声明和初始化指针的常用方法如下:

int num;
int *pi = #

下面是一种看似等价但是错误的声明方法:

int num;
int *pi;
*pi = #

3、迷途指针

参见《C迷途指针

4、越过数组边界访问内存

没有什么可以阻止程序访问为数组分配的空间以外的内存,下面的例子中,我们声明并初始化了三个数组来说明这种行为:

#include<stdio.h>
int main()
{
char firstName[] = "";
char middleName[] = "";
char lastName[] = "";
middleName[-] = 'X';
middleName[] = 'X';
middleName[] = 'X';
printf("%p %s\n", firstName, firstName);
printf("%p %s\n", middleName, middleName);
printf("%p %s\n", lastName, lastName);
return ;
}

运行结果如下:

下图说明了内存分配情况:

5、错误计算数组长度

将数组传给函数时,一定要同时传递数组长度,这个信息帮助函数避免越过数组边界

#include<stdio.h>
void replace(char buffer[], char replacement, size_t size)
{
size_t count = ;
while(*buffer && count++ < size) {
*buffer = replacement;
buffer++;
}
}
int main()
{
char name[];
strcpy(name, "Alexander");
replace(name, '+', sizeof(name));
printf("%s\n", name);
return ;
}

6、错误使用sizeof操作符

其中一个例子是试图检查指针边界但方法错误

#include<stdio.h>
int main()
{
int buffer[];
int *pbuffer = buffer;
for(int i = ; i < sizeof(buffer); i++) {
*(pbuffer++) = ;
}
return ;
}

改为:i < sizeof(buffer) / sizeof(int);

7、有界指针

有界指针是指指针的使用被限制在有效的区域内,C没有对这类指针提供直接的支持,但是可以自己显示地确保。如下所示:

#define SIZE 32
char name[SIZE];
char *p = name;
if(name != NULL) {
if(p >= name && p < name + SIZE) {
//有效指针,继续
} else {
//无效指针,错误分支
}
}

一种有趣的变化是创建一个指针检验函数;

下面的代码定义一个函数消除无效指针:

int valid(void *ptr) {
return (ptr != NULL);
}

下面的代码依赖于_etext的地址,定义于很多的类linux操作系统,在windows上无效:

#include <stdio.h>
#include <stdlib.h> int valid(void *p) {
extern char _etext;
return (p != NULL) && ((char*) p > &_etext);
} int global; int main(void) {
int local; printf("pointer to local var valid? %d\n", valid(&local));
printf("pointer to static var valid? %d\n", valid(&global));
printf("pointer to function valid? %d\n", valid((void *)main)); int *p = (int *) malloc(sizeof(int));
printf("pointer to heap valid? %d\n", valid(p));
printf("pointer to end of allocated heap valid? %d\n", valid(++p));
free(--p);
printf("pointer to freed heap valid? %d\n", valid(p));
printf("null pointer valid? %d\n", valid(NULL)); return ;
}

在linux平台运行结果如下:

pointer to local var valid? 1
pointer to static var valid? 1
pointer to function valid? 0
pointer to heap valid? 1
pointer to end of allocated heap valid? 1
pointer to freed heap valid? 1
null pointer valid? 0
另一种方法是利用ANSI-C和C++的边界检查工具(CBMC)
 

C安全问题与指针误用的更多相关文章

  1. 《深入理解C指针》

    <深入理解C指针> 基本信息 原书名:Understanding and using C pointers 作者: (美)Richard Reese 译者: 陈晓亮 丛书名: 图灵程序设计 ...

  2. 深入理解C指针----学习笔记

      深入理解C指针     第1章 认识指针   理解指针的关键在于理解C程序如何管理内存,指针包含的就是内存地址.     1.1 指针和内存   C程序在编译后,以三种方式使用内存: 1. 静态. ...

  3. 部分博文目录索引(C语言+算法)

    今天将本博客的部分文章建立一个索引,方便大家进行阅读,当然每一类别中的文章都会持续的添加和更新(PS:博文主要使用C语言) 博客地址:http://www.cnblogs.com/archimedes ...

  4. C++类型引用浅析

    C++类型引用浅析 引言 从最早被Bjarne Stroustrup 发明,作为C语言的扩展,到广为人知C++98标准,再到最新的C++11.C++14和C++17标准,C++一直在不断地进步.演化. ...

  5. c/c++和java达到swap不同功能

    首先我们来看看c/c++实施swap性能 void swap ( int & a, int & b) { int Temp; temp = a; a = b; b = temp; } ...

  6. 2 Java对象的创建过程

    JAVA中创建对象直接new创建一个对象,对么对象的创建过程是怎样的呢? 程序运行过程中有许多的对象被创建出来.那么对象是如何创建的呢? 一 对象创建的步骤 1 遇到new指令时,检查这个指令的参数是 ...

  7. 面经 cisco

    1. 优先级反转问题及解决方法 (1)什么是优先级反转 简单从字面上来说,就是低优先级的任务先于高优先级的任务执行了,优先级搞反了.那在什么情况下会生这种情况呢? 假设三个任务准备执行,A,B,C,优 ...

  8. javascript 误用this指针 的情况

    理解了this指针后,我们再来看看一些很容易误用this指针的情况. 示例1——内联式绑定Dom元素的事件处理函数 <script type="text/javascript" ...

  9. C++智能指针简单剖析

    导读 最近在补看<C++ Primer Plus>第六版,这的确是本好书,其中关于智能指针的章节解析的非常清晰,一解我以前的多处困惑.C++面试过程中,很多面试官都喜欢问智能指针相关的问题 ...

随机推荐

  1. 【cs229-Lecture16】马尔可夫决策过程

    之前讲了监督学习和无监督学习,今天主要讲“强化学习”. 马尔科夫决策过程:Markov Decision Process(MDP) 价值函数:value function 值迭代:value iter ...

  2. Win10系统下编译GDAL1.9.2版本

    环境说明: 1.Win10企业版.64位: 2.VS2012旗舰版: 3.GDAL1.9.2 GADL编译 1.解压GDAL压缩包至F:\GDAL\gdal-1.9.2: 2.设置GDAL编译后安装目 ...

  3. IOS雕虫小技

    1,你所不知道的Mac截图的强大 2,抓包工具WireShark开发必备,需要装X11插件 3,Mac远程控制Windows桌面-CoRD.或者TeamViewer 4,Mac下解压缩BetterZi ...

  4. Drupal 7.23版本升级笔记

    转载请注明出处:http://blog.csdn.net/panjunbiao/article/details/9860849 今天将尝试将Drupal网站从7.22升级到7.23,下面是升级笔记. ...

  5. Unity3D 敌人AI 和 动画( Animator )系统的实例讲解

    在这个实例中,我们要做一些敌人AI的简单实现,其中自动跟随和动画是重点,我们要达到的目标如下: 1.敌人能够自动跟随主角 2.敌人模型一共有四个动作:Idle(空闲) Run(奔跑) Attack(攻 ...

  6. Linux - Yum的常用方法总结

    简述 rpm是由红帽公司开发的软件包管理方式,使用rpm可以方便的进行软件的安装.查询.卸载.升级等工作. 但是rpm软件包之间的依赖性问题往往会很繁琐,尤其是软件由多个rpm包组成时. Yum(全称 ...

  7. 《精通Linux内核必会的75个绝技》知识杂记

    http://www.ibm.com/developerworks/cn/linux/l-cn-utrace/ utrace是为运行态的进程提供trace和debug支持. utrace能做如下事情: ...

  8. 一些有用的UtilityExtend小方法

    public static bool StartBy(this string thisValue, params string[] startBy) { foreach (string item in ...

  9. angular设置title

    Javascript框架在处理seo方面存在问题,因为爬虫在检索seo信息的时候会读不了js给其赋的值,导致搜索引擎收录不了或者收录了无效的信息,比如收录的可能是title={{title}}这样的, ...

  10. Scrum 3.2 多鱼点餐系统开发进度(页面优化&下单详细信息页面)

    Scrum 3.2 多鱼点餐系统开发进度(页面优化&下单详细信息页面)  1.团队名称:重案组 2.团队目标:长期经营,积累客户充分准备,伺机而行 3.团队口号:矢志不渝,追求完美 4.团队选 ...