欢迎关注我的个人博客: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. php 使用zendstudio 生成webservice文件 wsdl

    首先新建一个项目 在项目中新建下面这些文件 php类文件 test.php <?php class test { public function __construct() { } public ...

  2. VS错误 error LNK1158: 无法运行“cvtres.exe”

    1.删除文件 2.如果删除不掉 右键属性 - 安全 - 高级 - 所有者 - 确定 右键属性 - 安全 - 编辑 - 开通权限

  3. Vectoroid

    Use cases Drawing (sketch, illustrations, cartooning, etc). Diagramming (any sort of chart with obje ...

  4. MyBatis知多少(18)MyBatis系统

    小型.简单系统 小型应用程序通常只涉及单个数据库,只有一些相当简单的用户界面和领域模型.它的业务逻辑非常简单,甚至对一些简单的CRUD (Create, Read, Update, Delete:增删 ...

  5. java中反射机制通过字节码文件对象获取字段和函数的方法

    pclass = Class.forName("get_class_method.Person"); //Field ageField = pclass.getField(&quo ...

  6. Tools - Notepad++

    NotePad++ https://notepad-plus-plus.org/ 修改主题 依次点击设置---语言格式设置---选择主题,在显示界面中修改相关设置(背景色.前景色.字体等). 双文本横 ...

  7. 推荐35个新鲜出炉的响应式 Web 设计实例

    响应式设计的准则在于根据用户使用的屏幕的分辨率来改变网站的的布局.因此,视频或图像的大小和文本的数量,可以被视为是一个明显的变化.让你即使从智能手机浏览一个网站的时候能轻松地看到网站上的重要内容.今天 ...

  8. 另一个 OleDbParameterCollection 中已包含 OleDbParameter 错误分析及解决办法

    程序非常简单,就是从一个表中取出一个符合要求的数据,如果取到,就把该数据对应的计数加1.也就是执行不同的两个SQL语句操作同一个表,并且这两个SQL的参数是一样的.在一个函数里完成这个调用.执行第二个 ...

  9. 在VS中使用TinyFox调试OWIN应用(转)

    在很早一段时间之前,我曾经写过一篇关于Katana的使用方法的文章<如何安装并简单的使用OwinHost——Katana>,上面就有介绍如何在VS中调试使用Katana作为Host的App ...

  10. Dictionary序列化和反序列化

    public class SerializeHelper { public static string XmlSerialize(List<CustomSearchEntity> obj) ...