提示:文章
文章目录
前言
前期疑问:
本文目标:
一、背景
最近
二、
2.1 strncpy规范中的表述
看代码规范提到,输入参数,可能会使用没有结束符的字符串,导致strlen函数越界读书数据,使程序产生未定义的行为。
代码规范的描述是使用strnlen计算字符串长度,再使用strnlen计算字符串长度。将两个长度进行对比,就能判断出输入字符串是否是没有结束符的字符串。
下面编写代码验证一下strlen和strnlen函数的区别。
void StrnlenAndStrlen()
{
// 这个函数是验证strlen和strnlen区别,并且在检验输入参数中的作用
char strArray1[] = {'h', 'e', 'l', 'l', 'o'};
char strArray2[] = {"hello"};
int len1 = strlen(strArray1);
int len2 = strlen(strArray2);
std::cout << "len1:" << len1 << std::endl;
std::cout << "len2:" << len2 << std::endl;
}
打印结果为
len1:5
len2:5
和预期的不符,strArray1的strlen长度也是5。但是strArray1没有结束符,长度不应该是5的。于是修改代码如下
void StrnlenAndStrlen()
{
// 这个函数是验证strlen和strnlen区别,并且在检验输入参数中的作用
char strTest[] = "this is s test string";
char strArray1[] = {'h', 'e', 'l', 'l', 'o'};
char strArray2[] = {"hello"};
int len1 = strlen(strArray1);
int len2 = strlen(strArray2);
std::cout << "len1:" << len1 << std::endl;
std::cout << "len2:" << len2 << std::endl;
printf("%c\n", *(strArray1 + 5));
printf("%c\n", *(strArray1 + 6));
printf("%s\n", strArray1);
}
打印结果如下:
len1:26
len2:5
t
h
1
hellothis is s test string
2.2 额外关于栈的理解
构造出问题情况。因为strArray1在栈顶,所以没有出现预期情况。于是在strArray1上面增加一个字符串局部变量。
由此引出了另外一个问题,即栈的内存分布,经过调试可以看到栈空间内存分布情况如下:
自绘表格如下
void StrnlenAndStrlen()
{
// 这个函数是验证strlen和strnlen区别,并且在检验输入参数中的作用
char strTest[] = "this is s test string";
char strArray1[] = {'h', 'e', 'l', 'l', 'o'};
char strArray2[] = {"hello"};
int len1 = strlen(strArray1);
int len2 = strlen(strArray2);
std::cout << "len1:" << len1 << std::endl;
std::cout << "len2:" << len2 << std::endl;
int len3 = strnlen(strArray1,10);
std::cout << "len3:" << len3 << std::endl;
}
打印结果为len3:10
被strnlen第二个参数无语住了
三、
3.1
再次更新
重新看了下规范,下了下面的验证代码
void StrnlenAndStrlenVerify()
{
// 这个函数是验证strlen和strnlen区别,并且在检验输入参数中的作用
char strTest[] = "this is s test string";
char strArray1[] = {'h', 'e', 'l', 'l', 'o'};
char strArray2[] = {"hello"};
int len1 = strlen(strArray1);
int len2 = strlen(strArray2);
std::cout << "len1:" << len1 << std::endl;
std::cout << "len2:" << len2 << std::endl;
int len3 = strnlen(strArray1, 10);
std::cout << "len3:" << len3 << std::endl;
/* 打印结果
* len1:26
* len2:5
* len3:10
*/
// 下面按照规范中的逻辑来构造strnlen对入参的检查。这个构造是基于传递数组要传数组长度
// 由此产生一个问题,传递字符串数组也要传递数组长度吗
ArrayParamCheck(strArray1, 5);
ArrayParamCheck(strArray2, 6);
/* 打印结果
* param error
* hello
* 验证了入参为没有结束符的字符串。感觉很鸡肋。只有在防护外部输入的时候才会用到吧
*/
}
上面的代码实现了strnlen对入参的防护和检查
总结
未完待续