欢迎关注我的个人博客: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. gulpfile.js 合并压缩 requirejs 的配置文件

    var gulp = require("gulp"); // var babel = require("gulp-babel"); // 用于ES6转化ES5 ...

  2. main函数读取jar包外部的配置文件properties

    首先,Java的main方法有个初始化入参args,如下所示: public static void main(String[] args) {} 然后,在linux下执行jar包引入外部配置文件的命 ...

  3. HANA Studio打开系统显示Secure storage is locked

    之前一直用着好好的,今天打开HANA Studio突然发现一个系统都不在了: 提示:‘Secure storage is locked’ 我点旁边的Unlocked没有任何反应,右键也没有功能了.如下 ...

  4. `cocos2dx非完整` 开始自己的FW模块

    上一篇的文章中说到了一些个人习惯的东西以及一些简单的项目配置,这一篇文章我们来进一步完善一些东西.首先,打开编译以后的客户端执行,会看到一大堆的fileutils加载luac文件的提示,在终端显示一大 ...

  5. 移动安全时代,如何保护你的app

    Android系统的安全性历来备受诟病,在强大的反编译工具下,APK中的代码逻辑一览无余:重打包技术使得各种盗版软件层出不穷,充斥着Android市场,特别是对于金融.电商.游戏等产品的盗版应用,严重 ...

  6. Gamma Gamma~!!!

    左图是没有进行gamma矫正的,右图是进行了gamma矫正的.以前一直以为是Tone Map的公式计算有问题,后来看PBR的paper时候,终于明白了gamma的重要性,一改,果然发现颜色不想以前那么 ...

  7. Web程序员开发App系列 - 开发我的第一个App,源码下载

    Web程序员开发App系列 Web程序员开发App系列 - 认识HBuilder Web程序员开发App系列 - 申请苹果开发者账号 Web程序员开发App系列 - 调试Android和iOS手机代码 ...

  8. 数据可视化(2)--Justgage

    JustGage 是一个 JavaScript 插件来生成很漂亮的仪表盘,基于 Raphaël 库做向量图绘制. JustGage 完全基于 SVG,因此支持几乎所有浏览器,包括:IE6+, Chro ...

  9. PowerMock 简介--转载

    原文地址:https://www.ibm.com/developerworks/cn/java/j-lo-powermock/ EasyMock 以及 Mockito 都因为可以极大地简化单元测试的书 ...

  10. 【转载】Linux动态库搜索路径的技巧

    转自:http://soft.chinabyte.com/os/232/11488732_2.shtml 众所周知,Linux动 态库的默认搜索路径是/lib和/usr/lib.动态库被创建后,一般都 ...