一道C语言安全编码题目
2017-06-17 12:01
447 查看
1、前言
最近在网上看到一道C语言题目,用C语言实现一个函数,给定一个int类型的整数,函数输出逆序的整数,例如输入123,则输出字符串"321",,输入-123,则输出字符串"-321"。题目要求,不使用标准库,不得分配内存。当时觉得蛮简单的,这不就是类似字符串逆转嘛,自己尝试做了一下,测试发现,还是有很多地方考虑不周全。今天在此整理一下基础知识,作为一名安全开发人员,时刻需要注意代码的安全,防止有任何漏洞。题目给出的函数如下:
2、思考过程
写代码最怕的就是没有想好,一上来就写,在写的过程中不断的测试修改,这样很浪费时间。因此需要先好好想一下,这个题目到底考些什么呢?
(1)int类型的整数分为正数、0、负数,如何处理这些边界值
(2)整数与字符串之间的转换,如何将一个整数转换为一个字符
(3)如何返回一个const char * 类型的字符串
(4)当输入的整数超过int的范围如何处理
3、编码过程
开始写代码的思路如下:定义一个char类型的数组,用于保存结果。使用对10取余和除法操作依次获取每一位的数字,然后根据ASSIC码转换为字符。将字符拼接起来,返回字符串数组结果。编码实现如下:
当初没有考虑那么多,编译发现出现如下错误:
一看编译错误,才意识到自己掉入坑中。题目要求返回一个字符串,而且不用分配内存。当时就想直接定义一个字符数组进行返回,而定义的str属于函数局部变量。
一个函数的局部变量都是存在stack中的,当这个函数调用过程结束时,这个局部变量都是要释放掉的,所以就会产生这样的warning,这个是和变量的life time相关的,所以解决方法有:
1.将char result[16]改为static型
2.使用malloc向heap申请,这些是需要caller用free去释放的
于是使用static 类型字符串,代码改进如下:
int main()
{
printf("%s\n", parseInt(123));
printf("%s\n", parseInt(12345678));
printf("%s\n", parseInt(-89790));
return 0;
}
测试结果如下:
改为static之后,编译成功,看输出的结果上看,前面两个输出是正确的,而第三个输出的结果是错误的。尼玛,再次掉入坑中,对static变量的应用不精通啊。为什么每次到看到结果后才想起来?
虽然在函数中定义了static局部变量,使得变量的变为静态stack存储区域,生命周期从函数中变成了这个程序的范围。但是static局部变量在函数第一次调用的时候会初始化,后面调用就不会了,直接使用了。因此导致了刚才的结果输出不对,复用了上次遗留的结果。
static静态局部变量属于静态存储方式,它具有以下特点:
(1)静态局部变量在函数内定义 它的生存期为整个源程序,但是其作用域仍与自动变量相同,只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。
(2)允许对构造类静态局部量赋初值 例如数组,若未赋以初值,则由系统自动赋以0值。
(3)对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。 根据静态局部变量的特点, 可以 看出它是一种生存期为整个源程序的量。虽然离开定义它的函数后不能使用,但如再次调用定义它的函数时,它又可继续使用, 而且保存了前次被调用后留下的 值。 因此,当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有时会造成 意外的副作用,因此仍以采用局部静态变量为宜。
第一次调用函数,static变量,初始化。
第二次,及以后,调用函数,static变量,不会初始化。
继续改进代码,在函数中将static变量每次使用for循环进行初始化,改进代码如下:
这次输出结果如下:
终于得到了正确答案,看似很简单的题目,折腾的这么久,才搞出来。扩展一下,大家看看如下这个输出什么呢:
这个结果是什么呢?为什么会这样呢?
这个结果是什么呢?为什么会这样呢?
int 类型4个字节,32位组成。int的最高位作为符号位,需要特殊处理。
实际运行结果如下:
最近在网上看到一道C语言题目,用C语言实现一个函数,给定一个int类型的整数,函数输出逆序的整数,例如输入123,则输出字符串"321",,输入-123,则输出字符串"-321"。题目要求,不使用标准库,不得分配内存。当时觉得蛮简单的,这不就是类似字符串逆转嘛,自己尝试做了一下,测试发现,还是有很多地方考虑不周全。今天在此整理一下基础知识,作为一名安全开发人员,时刻需要注意代码的安全,防止有任何漏洞。题目给出的函数如下:
#include <stdio.h> const char * parseInt(int data) { return "321"; } int main() { printf("%s\n", parseInt(123)); return 0; }
2、思考过程
写代码最怕的就是没有想好,一上来就写,在写的过程中不断的测试修改,这样很浪费时间。因此需要先好好想一下,这个题目到底考些什么呢?
(1)int类型的整数分为正数、0、负数,如何处理这些边界值
(2)整数与字符串之间的转换,如何将一个整数转换为一个字符
(3)如何返回一个const char * 类型的字符串
(4)当输入的整数超过int的范围如何处理
3、编码过程
开始写代码的思路如下:定义一个char类型的数组,用于保存结果。使用对10取余和除法操作依次获取每一位的数字,然后根据ASSIC码转换为字符。将字符拼接起来,返回字符串数组结果。编码实现如下:
const char * parseInt(int data) { char str[16] = {0}; int i = 0; if (data < 0) { data = -data; str[i++] = '-'; } int tmp = data; while (tmp / 10) { char ch = tmp % 10 + 48; tmp = tmp / 10; str[i++] = ch; } str[i++] = tmp % 10 + 48;return str; }
当初没有考虑那么多,编译发现出现如下错误:
一看编译错误,才意识到自己掉入坑中。题目要求返回一个字符串,而且不用分配内存。当时就想直接定义一个字符数组进行返回,而定义的str属于函数局部变量。
一个函数的局部变量都是存在stack中的,当这个函数调用过程结束时,这个局部变量都是要释放掉的,所以就会产生这样的warning,这个是和变量的life time相关的,所以解决方法有:
1.将char result[16]改为static型
2.使用malloc向heap申请,这些是需要caller用free去释放的
于是使用static 类型字符串,代码改进如下:
const char * parseInt(int data) { static char str[16] = {0}; int i = 0; if (data < 0) { data = -data; str[i++] = '-'; } int tmp = data; while (tmp / 10 != 0) { char ch = tmp % 10 + 48; tmp = tmp / 10; str[i++] = ch; } str[i++] = tmp % 10 + 48; return str; }
int main()
{
printf("%s\n", parseInt(123));
printf("%s\n", parseInt(12345678));
printf("%s\n", parseInt(-89790));
return 0;
}
测试结果如下:
改为static之后,编译成功,看输出的结果上看,前面两个输出是正确的,而第三个输出的结果是错误的。尼玛,再次掉入坑中,对static变量的应用不精通啊。为什么每次到看到结果后才想起来?
虽然在函数中定义了static局部变量,使得变量的变为静态stack存储区域,生命周期从函数中变成了这个程序的范围。但是static局部变量在函数第一次调用的时候会初始化,后面调用就不会了,直接使用了。因此导致了刚才的结果输出不对,复用了上次遗留的结果。
static静态局部变量属于静态存储方式,它具有以下特点:
(1)静态局部变量在函数内定义 它的生存期为整个源程序,但是其作用域仍与自动变量相同,只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。
(2)允许对构造类静态局部量赋初值 例如数组,若未赋以初值,则由系统自动赋以0值。
(3)对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。 根据静态局部变量的特点, 可以 看出它是一种生存期为整个源程序的量。虽然离开定义它的函数后不能使用,但如再次调用定义它的函数时,它又可继续使用, 而且保存了前次被调用后留下的 值。 因此,当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有时会造成 意外的副作用,因此仍以采用局部静态变量为宜。
第一次调用函数,static变量,初始化。
第二次,及以后,调用函数,static变量,不会初始化。
继续改进代码,在函数中将static变量每次使用for循环进行初始化,改进代码如下:
#include <stdio.h> const char * parseInt(int data) { static char str[16] = {0}; int i = 0; 8 int t = 0; 9 for (; t < 16; t++) { 10 str[t] = 0; 11 } 12 if (data < 0) { data = -data; str[i++] = '-'; } int tmp = data; while (tmp / 10 != 0) { char ch = tmp % 10 + 48; tmp = tmp / 10; str[i++] = ch; } str[i++] = tmp % 10 + 48; return str; } int main() { printf("%s\n", parseInt(123)); printf("%s\n", parseInt(12345678)); printf("%s\n", parseInt(-89790)); return 0; }
这次输出结果如下:
终于得到了正确答案,看似很简单的题目,折腾的这么久,才搞出来。扩展一下,大家看看如下这个输出什么呢:
printf("%s, %s, %s\n", parseInt(98989),parseInt(-4567), parseInt(123456));
这个结果是什么呢?为什么会这样呢?
printf("%s\n", parseInt(0x8FFFFFFF)); printf("%s\n", parseInt(0xFFFFFFFF));
这个结果是什么呢?为什么会这样呢?
int 类型4个字节,32位组成。int的最高位作为符号位,需要特殊处理。
实际运行结果如下:
相关文章推荐
- 一道C语言安全编码题目
- 一道C语言的内存管理题目
- 一道C语言题目
- 一道简单C语言题目的优化
- C语言测试题目解答:微软一道笔试题,2005年华为招聘
- 学生问的一道C语言题目
- 一道校招笔试的C语言题目
- 好文摘抄 [C语言]关于指针和int型的一道题目
- 一道C语言访存题目的引申(From林健的BLOG)
- 每天一道C语言题目7/5
- 每天一道C语言题目
- 【C语言/C++】算术移位和逻辑移位以及一道移位的题目
- C语言安全编码之数组索引位的合法范围
- C语言 数据结构题目一道 在线等答案~快~试写一符合上述要求的LocateNode运算的算法。
- 同学复试的一道C语言题目
- 【C语言】一道给力的题目
- 一道关于二维数组和指针数组的C语言笔试题目
- 关于c语言的一道题目,适合新手看哦
- 分享C语言中的unsigned类型的一道题目
- C语言安全编码之数值中的sizeof操作符