你必须知道的指针基础-4.sizeof计算数组长度与strcpy的安全性问题
一、使用sizeof计算数组长度
1.1 sizeof的基本使用
如果在作用域内,变量以数组形式声明,则可以使用sizeof求数组大小,下面一段代码展示了如何使用sizeof:
int nums[] = {,,,,,};
int i;
// sizeof(nums) 计算nums数组的总字节数
// sizeof(int) 计算int类型所占用的字节数
int length = sizeof(nums)/sizeof(int);
for(i=;i<length;i++)
{
printf("%d ",nums[i]);
}
其中sizeof(nums)代表计算nums数组的总字节数,而sizeof(int)则代表计算int类型所占用的字节数(32位系统下是4个字节,64位下可能不同,因此这里使用sizeof(int)可以向程序员屏蔽这个差异),运行结果为:
1.2 sizeof只能在编译时计算
假如我们将上面的代码做一个抽象,将数组的遍历及打印封装为一个方法,代码如下:
void printEach(int* nums)
{
// sizeof(nums)在这里是计算指针的字节数
int length = sizeof(nums)/sizeof(int);
printf("The length of nums is %d\n",length);
int i;
for(i=;i<length;i++)
{
printf("%d ",nums[i]);
}
}
我们定义了一个printEach方法,其参数是一个指针,在方法内部通过sizeof计算数组长度。但是,运行结果并没有同上面的结果一致:
我们发现,虽然我们使用了指针,但由于sizeof是编译器在编译的时候计算的,无法动态计算。因此对于int *或者将数组传递给函数,那么就无法使用sizeof获取大小了。即使函数声明中写着int[]也不行(为了避免误解,不要在参数中声明数组类型)。这里,sizeof(nums)只是计算了指针的字节数(这里指针指向了数组的首元素的地址,一个int占4个字节,所以最后length变成了1)。
那么,为了避免出现无法计算长度的情况,我们一般都会在方法定义时增加一个长度的参数,让调用者传递过来,函数内部不再计算长度。看看如下的代码:
void printEachWithLen(int* nums,int length)
{
int i;
for(i=;i<length;i++)
{
printf("%d ",nums[i]);
}
}
这时候,我们就可以在main函数中调用该printEachWithLen()函数:
int length = sizeof(nums)/sizeof(int);
printEachWithLen(nums,length);
这下看看结果:
因此,一般给函数传递数组/字符串的时候都要求额外传递“长度”参数,因为函数内部也不知道“有多长”。例如:memcpy(void * restrict, const void * restrict, size_t),第三个参数size_t就是长度。又例如在.NET中,要进行数组的复制,可以使用 Array.Copy 、Buffer.BlockCopy 、Array.ConstrainedCopy等方法,通过查看其方法定义,都要求传递了数组长度。
const int INT_SIZE = ;
int[] arr = { , , , , , , , , , };
Buffer.BlockCopy(arr, * INT_SIZE, arr, * INT_SIZE, * INT_SIZE);
foreach (int value in arr)
Console.Write("{0} ", value);
// The example displays the following output:
// 8 10 12 14 10 12 14 16 18 20
二、strcpy的安全性问题
2.1 使用strcpy复制字符串
一个简单的场景,将一个字符串复制到另一个字符串中,在C语言课本中,最长出现的就是strcpy了。我们可以轻易地写出下面的代码来实现字符串复制:
char sourceStr[] = "hello edison";
char destStr[];
strcpy(destStr,sourceStr);
printf("%s",destStr);
运行结果如下图所示:
但是,我们常常听人说strcpy是不安全的函数,为什么呢?先看看strcpy内部的循环判断条件:
while ((*strDest++ = *strSrc++) != '\0')
这个循环会一直执行,直到循环条件为空,即'\0',也就是说,如果strDest所指的存储空间不够大的话,这个函数会将strSrc中的部分内容拷贝到strDest所指内存空间后面的内存中。而strDest所指空间后面的内存却是不可知的,有可能已经被其他资源占用了,这样就会破坏原先存储的内容,导致系统崩溃。
因为strcpy在执行字符串拷贝的时候,会从strSrc所指位置开始,检测当前内存单元中存储的数据是否为'\0'。如果不为'\0',则将这个内存单元中的数据拷贝到strDest所指向的内存中。如果strSrc中存储的字符串长度大于dst所申请的内存空间的话,就会产生越界,造成不可预知的后果。
PS:strlen根据'\0'判断字符串结束,那么恶意攻击者可以构造一个不包含'\0'的字符串,然后让数据写入数组之外的程序内存空间,从而进行破坏。
2.2 使用strncpy代替strcpy
(1)strncpy函数定义:
char *strncpy(char *dest, const char *src,int count)
将字符串src中的count个字符拷贝到字符串dest中去,最后返回指向dest的指针。
(2)strncpy用法解析:
这个函数和strcpy类似,当src的长度大于dst申请的空间的时候,情况和strcpy一样;
如果第3个参数count的值大于src中字符串的长度的话,就会将字符串src拷贝到dst中,返回函数。
注意:如果源串长度大于n,则strncpy不复制最后的'\0'结束符,所以是不安全的,复制完后需要手动添加字符串的结束符才行。
char sourceStr[] = "hello edison";
char destStr[]; int len = sizeof(sourceStr)/sizeof(char);
printf("%d\n",len);
strncpy(destStr,sourceStr,len-);
// 保证安全的字符串复制
destStr[len-]='\0';
printf("%s",destStr);
运行结果如下图所示:
参考资料
如鹏网,《C语言也能干大事(第三版)》
你必须知道的指针基础-4.sizeof计算数组长度与strcpy的安全性问题的更多相关文章
- 通过sizeof获得数组长度的方法
int a[20]; int len = sizeof(a)/sizeof(*a); //值为20,即数组长度为20 注:sizeof是一个操作符,sizeof的值在编译时确定
- strlen()和sizeof()求数组长度
在字符常量和字符串常量的博文里有提: 求字符串数组的长度 标准库函数strlen(s)可以返回字符串s的长度,在头文件<string.h>里. strlen(s)的判断长度的依据是(s[i ...
- sizeof()计算
本节包含sizeof()计算结构体,位域,数组,字符串,指针,c++中的class等类型的大小,sizeof()计算的大小都是以字节为单位. 一 计算基本类型的长度 sizeof(char): 1 s ...
- sizeof() 之 数组
在平时的编程中,我们会经常用到数组,并且需要知道数组的长度,有时我们可以明确的知道数组的长度,但有时并不,这时,可以借用sizeof(),来获得数组的长度,如下: arrayLength = size ...
- 【C++基础】sizeof 数组 指针 空NULL
笔试遇到很多sizeof的小题,博主基础堪忧,怒总结如下,还是要巩固基础啊啊啊! sizeof操作符 对象所占 栈内存空间的大小,单位是字节 关键词:char 数组 指针 结构体 class [注意 ...
- C:指针基础
内存概述 内存 内存含义: 存储器:计算机的组成中,用来存储程序和数据,辅助CPU进行运算处理的重要部分. 内存:内部存贮器,暂存程序/数据--掉电丢失 SRAM.DRAM.DDR.DDR2.DDR3 ...
- C基础题-sizeof
sizeof C语言中判断数据类型或者表达式长度符:关键字:字节数的计算在程序编译时进行,而不是在程序执行的过程中才计算出来! 一.关于sizeof简单的总结 1.sizeof的使用形式:sizeo ...
- C语言指针基础
新手在C语言的学习过程中遇到的最头疼的知识点应该就是指针了,指针在C语言中有非常大的用处.下面我就带着问题来写下我对于指针的一些理解. 指针是什么? 指针本身是一个变量,它存储的是数据在内存中的地址 ...
- C语言 指针基础篇 数组,函数与指针的运用 2 14
下面看看如何在函数中运用指针吧 下面是往函数传入指针的简单操作,不是传入数组的.判断一个a是否大于b是的话给,是的话对其进行操作,不是的话就直接返回. #include <stdio.h> ...
随机推荐
- 【Java EE 学习 54】【OA项目第一天】【SSH事务管理不能回滚问题解决】【struts2流程回顾】
一.SSH整合之后事务问题和总结 1.引入问题:DAO层测试 假设将User对象设置为懒加载模式,在dao层使用load方法. 注意,注释不要放开. 使用如下的代码块进行测试: 会报错:no sess ...
- MVVM页面跳转 技巧性标记
刚学MVVM 百度了很多概念性的东西 也参考了网上的例子 基本有了了解 但是我发现 我做了一个登录页面以后 我跳转咋办呢? VM里面咋做跳转? 问了一下其他的群友得到了一些启发.感谢“上海*松” 我仅 ...
- UWP Composition API - PullToRefresh
背景: 之前用ScrollViewer 来做过 PullToRefresh的控件,在项目一些特殊的条件下总有一些问题,比如ScrollViewer不会及时到达指定位置.于是便有了使用Compositi ...
- IDEA快捷键+使用小技巧
一 常用快捷键 Alt+回车 导入包,自动修正,当引入的类需要异常捕获的时候 Ctrl+Shift+Space 自动补全代码,"new"字符,还可以引入强制转换的 Ctrl-Alt ...
- Android自定义组件
[参考的原文地址] http://blog.csdn.net/l1028386804/article/details/47101387效果图: 实现方式: 一:自定义一个含有EditText和Butt ...
- CVE-2010-3654分析及利用
三年前分析的一个漏洞,最近又温习一遍,这个flash中混淆漏洞的鼻祖,10年最经典的漏洞. 漏洞触发原因 该漏洞主要因为avm对返回的类没有进行校验,通过修改swf文件,实现Ref类和Origin类的 ...
- 《UML大战需求分析》阅读随笔(五)
在处理复杂事物的时候,用到一种基本手段就是抽象.抽象的目的是区别事物之间的本质和不同,面向对象编程(OOP)的实质就是利用 类和对象来建立抽象模型. 类表示对象的类别,是创建对象的蓝本.建立一个事物的 ...
- 【APICloud】APICloud基础学习与快速入门
前言:回顾这几天学习情况,总的来说APICloud官网它的学习资料和社区还是足够了,但是我必须吐槽一句,实在是过于混乱了,视频资料文档资料它一股脑地都堆在了那里,这几天基本处于在各个地方跳转,然后现在 ...
- 我理想中的父母(The Ideal Parents In My Heart)
Parents are the first teachers in children's life, and people all know the great importance exactly ...
- [资料分享]2016 黑马 Java 19期视频+Hadoop大数据实战视频
下载链接: 链接:http://pan.baidu.com/s/1bToXK6 密码:7k43 解压密码: www.lthack.com 或者 2cifang.com 或者 2cifang.com_2 ...