C指针之七:安全问题和指针误用

博客聚焦于C指针的安全问题和指针误用情况,虽未给出具体内容,但可知围绕C指针在使用过程中可能出现的安全隐患以及错误使用方式展开,属于信息技术中C语言编程相关内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//本篇列举了很多可能出错的地方,本篇重点在于避错
/*
本章分为四个部分:
1、指针声明和初始化
2、指针的使用问题
3、内存释放问题
4、使用静态分析工具
*/
#include<iostream>
#include <windows.h>
using namespace std;

/*
	一、指针声明和初始化
	(一)、不恰当的指针声明
*/
int *ptr1, ptr2;
/*
	上述声明,会把ptr1声明为整数指针
	ptr2声明为整数
*/

// 正确写法
int *ptrU1, *ptrU2;

// 更好的写法
int *ptrB1;
int *ptrB2;

/*
	(二)、使用指针前未初始化
	未初始化的指针,是野指针,内含垃圾变量
	int *pi;
	...
	printf("%d\n",*pi); // 这样写不对
*/

/*
	(三)、处理未初始化指针
	1、用NULL
	2、Assert
	3、第三方工具

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

	assert(NULL != pi);// 表达式为真什么都不会发生,表达式为假,程序终止
*/



/*
	二、指针的使用问题
	缓冲区栈溢出,覆写对象边界以外的内存就会导致缓冲区溢出
	可能触发缓冲区栈溢出的原因:
	1、访问数组元素时候,没有检查索引值
	2、对数组指针做指针运算符的时候不够小心
	3、用gets这样的函数从标准输入读取字符串
	4、误用strcpy和strcat这样的函数
*/

/*
	(一)、测试NULL,用malloc这样的函数一定要检查返回值,否则会导致程序非正常终止
下面说明一般的方法
*/
void TestNULL()
{
	float *vector = (float *)malloc(20 * sizeof(float));
	if (NULL == vector)
	{
		// malloc分配内存失败
	}
	else
	{
		// 处理vector
	}
}
/*
	(二)、错误使用解引用操作
	// 常用方法
	int num;
	int *pi = &num;

	// 错误用法
	int num2;
	int *pi2;
	*pi2 = &num2;


	// 更正
	int num3;
	int *pi3;
	pi3 = &num3;
*/

/*
	(三)、米兔指针

	释放指针后却仍然在引用原来的内存,就会产生迷途指针
*/

/*
	(四)、越过数组边界访问指针(有可能程序崩溃)
	char firstName = "1234567";
	char middleName = "1234567"
	char lastName = "1234567";

	firstName[-2] = 'X';
	middleName[0] = 'X';
	lastName[8] = 'X';

*/

void Transboundary()
{
	char firstName[8] = "1234567";
	char middleName[8] = "1234567";
	char lastName[8] = "1234567";

	firstName[-2] = 'X';
	middleName[0] = 'X';
	lastName[8] = 'X';

	printf("%s\n", firstName);
	printf("%s\n", middleName);
	printf("%s\n", lastName);

}

/*
	(五)、错误计算数组长度
将数组传递给指针的时候,一定要同时传递数组长度,这样做能够帮助函数
避免数组越界
*/

void replace(char buffer[],char replacement,size_t size)
{
	size_t count = 0;
	while(*buffer != NULL&& count++ <size)
	{
		*buffer = replacement;
		buffer++;
	}
	*buffer = '\0';
}

/*
name数组最多装7个字符,一个NULL
*/
void TestTranArraySize()
{
	char name[8];
	strcpy(name, "Alexand"); 
	
	replace(name, '+', sizeof(name));
	printf("%s\n", name);
}
/*
	(六)、错误使用sizeof操作符

*/
/*
	下面这段函数出错原因是,iSize = 20*4
	正确计算方式是,iSize = siezeof(buffer)/sizeof(int)
*/
void errorSizeofUse()
{
	int buffer[20];
	int *pBuffer = buffer;
	int iSize = sizeof(buffer);

	for(int i = 0;i <iSize;i++)
	{
		*(pBuffer++) = 0;
	}
}
/*
	(七)、一定要匹配指针类型
	short的十进制输出 %hd
	short的十六进制输出 %hx 或者 %hX
*/

void matchPointerType()
{
	int num = 2147483647;
	int *pi = &num;

	short *ps = (short *)pi;

	printf("%p , %x, %d\n",pi,*pi,*pi);

	printf("%p, %hx, %hd\n",ps,(unsigned short)*ps,(unsigned short)*ps);
}
/*
	(八)、有界指针
	有界指针是指指针的使用被限制在有效的区域内。比如说现在有一个32个元素的
数组,禁止对这数组使用的指针,访问数组前面或者后面的任何内存。
(方法有点麻烦,实际操作的时候,考虑静态分析)

*/

void PointerBoundary()
{
	#define D_SIZE 32
	char name[D_SIZE];

	char *p = name;
	if(name != NULL)
	{
		if(p >= name && p< name +D_SIZE)
		{
			// 有效指针 ,继续
		}
		else
		{
			// 无效指针,错误分支
		}
	}
}
/*
	(九)、字符串安全问题
	两种方法:
	1、用户提供的格式化字符串传递给函数的时候,要谨慎
	2、用strcpy_s这一类的函数,能够限制传递的字符串大小
	(下面的函数是个反例)
*/
void StringSafe()
{
	char firstName[8];
	int iResult;
	iResult = strcpy_s(firstName, sizeof(firstName), "Alexander");

}

/*
	(十)、指针运算符和结构体
	数组的内存连续,结构体的内存不连续,所以可以对数组使用算术运算,对结构体不能使用内存运算

	误用对齐的指针可能会导致程序取到错误数据,或者程序异常终止。
	如果编译器需要生成额外的机器码来弥补不恰当的对齐,那么指针访问也可能变慢
*/

typedef struct _employee
{
	char name[10];
	int age;

}Employee;

// 反面教材
void PointerStruct()
{
	Employee employee;

	// 不建议这样用
	char * ptr = employee.name;
	ptr += sizeof(employee.name);

}

typedef struct _item
{
	int partnumber;
	int quantity;
	int bindNumber;

} Item;

void PointerStrTest()
{
	Item part = {12345,35,107};
	int *pi = &part.partnumber;
	
	/*
		指针偏移的方式访问
	*/
	cout << "PartNumber = "<<*pi << endl;

	pi++;
	cout << "Quantity = " << *pi << endl;

	pi++;
	cout << "Bindnumber = " << *pi << endl;
	
	/*
		直接取地址
	*/

	int *pi2 = &part.partnumber;
	cout << "2 PartNumber = " << *pi2 << endl;

	pi2 = &part.quantity;
	cout << "2 Quantity = " << *pi2 << endl;

	pi2 = &part.bindNumber;
	cout << "2 Bindnumber = " << *pi2 << endl;

	/*
		根据对象直接取
	*/

	int iPartNumber = part.partnumber;
	int iQuality = part.quantity;
	int iBindnumber = part.bindNumber;

	cout <<"3 iPartNumber" << iPartNumber << endl;

	cout << "3 iQuality" << iQuality << endl;

	cout << "3 iBindnumber" << iBindnumber << endl;

}
/*
	(十一)、函数指针的问题
	函数和函数指针用来控制程序的执行顺序,但是它们可能会被误用
导致不可预期的行为
*/

/*
三、内存释放问题
(一)、重复释放
(二)、清除敏感数据
char name[32];
memset(name,0,sizeof(name));
free(name);
name = NULL;
*/

void ClearInfo()
{
	char name[32];
	memset(name, 0, sizeof(name));
	//free(name); // 注意,free是和malloc一起用的

	char *pwd = (char*)malloc(sizeof(5));
	//memset(pwd, 0, sizeof(pwd)); // 小心溢出
	free(pwd);
	pwd = NULL;

	//name = NULL;
}
/*
	四、使用静态分析工具
	gcc编译器加上-Wall启用 编译器警告
*/

int main()
{
	//TestTranArraySize();
	//errorSizeofUse();
	
	//matchPointerType();
	//StringSafe();
	//PointerStrTest();
	//Transboundary();

	ClearInfo();
	system("pause");
	return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值