一、使用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'结束符,所以是不安全的,复制完后需要手动添加字符串的结束符才行。

  (3)strncpy用法实例:
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语言也能干大事(第三版)》

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

你必须知道的指针基础-4.sizeof计算数组长度与strcpy的安全性问题的更多相关文章

  1. 通过sizeof获得数组长度的方法

    int a[20]; int len = sizeof(a)/sizeof(*a); //值为20,即数组长度为20 注:sizeof是一个操作符,sizeof的值在编译时确定

  2. strlen()和sizeof()求数组长度

    在字符常量和字符串常量的博文里有提: 求字符串数组的长度 标准库函数strlen(s)可以返回字符串s的长度,在头文件<string.h>里. strlen(s)的判断长度的依据是(s[i ...

  3. sizeof()计算

    本节包含sizeof()计算结构体,位域,数组,字符串,指针,c++中的class等类型的大小,sizeof()计算的大小都是以字节为单位. 一 计算基本类型的长度 sizeof(char): 1 s ...

  4. sizeof() 之 数组

    在平时的编程中,我们会经常用到数组,并且需要知道数组的长度,有时我们可以明确的知道数组的长度,但有时并不,这时,可以借用sizeof(),来获得数组的长度,如下: arrayLength = size ...

  5. 【C++基础】sizeof 数组 指针 空NULL

    笔试遇到很多sizeof的小题,博主基础堪忧,怒总结如下,还是要巩固基础啊啊啊! sizeof操作符 对象所占 栈内存空间的大小,单位是字节 关键词:char  数组 指针 结构体 class [注意 ...

  6. C:指针基础

    内存概述 内存 内存含义: 存储器:计算机的组成中,用来存储程序和数据,辅助CPU进行运算处理的重要部分. 内存:内部存贮器,暂存程序/数据--掉电丢失 SRAM.DRAM.DDR.DDR2.DDR3 ...

  7. C基础题-sizeof

    sizeof  C语言中判断数据类型或者表达式长度符:关键字:字节数的计算在程序编译时进行,而不是在程序执行的过程中才计算出来! 一.关于sizeof简单的总结 1.sizeof的使用形式:sizeo ...

  8. C语言指针基础

    新手在C语言的学习过程中遇到的最头疼的知识点应该就是指针了,指针在C语言中有非常大的用处.下面我就带着问题来写下我对于指针的一些理解. 指针是什么?  指针本身是一个变量,它存储的是数据在内存中的地址 ...

  9. C语言 指针基础篇 数组,函数与指针的运用 2 14

    下面看看如何在函数中运用指针吧 下面是往函数传入指针的简单操作,不是传入数组的.判断一个a是否大于b是的话给,是的话对其进行操作,不是的话就直接返回. #include <stdio.h> ...

随机推荐

  1. js对象克隆方法

    方法1: function clone(obj){ var o; switch(typeof obj){ case 'undefined': break; case 'string' : o = ob ...

  2. Linux中SysRq的使用(魔术键)

    转:http://www.chinaunix.net/old_jh/4/902287.html 魔术键:Linux Magic System Request Key Hacks 当Linux 系统不能 ...

  3. Jquery判断变量是否为空

    var aaa=''; if(aaa) { //aaa不为空也不是不可识别对象时执行 } else { //aaa为空或不可识别时执行 } aaa必须是变量,对象的属性好像是不行,

  4. 解决“添加远程依赖方式没有效果”的bug

    1.添加远程依赖dependencies的compile方式

  5. python 学习第三天

    一,Python的数据结构-集合 1,集合的定义 Python中集合是以{}括起来的,例如x=set({1,2,3,4,5}),这就是一个集合,集合的特点有:(1),去重的(2),无序的,集合的作用用 ...

  6. NMAP分布式扫描工具dnmap

    NMAP分布式扫描工具dnmap   NMAP是一款知名的网络扫描工具.它提供丰富和强大的网络扫描功能.但很多时候,需要渗透测试人员从多个终端发起扫描任务,以快速扫描大型网络,或规避IP限制等安全策略 ...

  7. iOS drewRect方法

    You do not need to override this method if your view sets its content in other ways. By the time thi ...

  8. POJ 2774 Long Long Message ——后缀数组

    [题目分析] 用height数组RMQ的性质去求最长的公共子串. 要求sa[i]和sa[i-1]必须在两个串中,然后取height的MAX. 利用中间的字符来连接两个字符串的思想很巧妙,记得最后还需要 ...

  9. wpf 切换资源字典的2中方式

    var _1200RDUri = new Uri(String.Format(@"/aa;Component/Themes/1200Theme.xaml"), UriKind.Re ...

  10. 第一天ci框架开发商城1

    ci框架开发商城1 1/28/2016 9:43:52 PM userguide删除 system application controllers 控制器 models 模型 views 视图 模板 ...