一、使用sizeof计算数组长度
1.1 sizeof的基本使用
如果在作用域内,变量以数组形式声明,则可以使用sizeof求数组大小,下面一段代码展示了如何使用sizeof:
int nums[] = {11,22,33,44,55,66}; int i; // sizeof(nums) 计算nums数组的总字节数 // sizeof(int) 计算int类型所占用的字节数 int length = sizeof(nums)/sizeof(int); for(i=0;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 ",length); int i; for(i=0;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=0;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 = 4; int[] arr = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 }; Buffer.BlockCopy(arr, 3 * INT_SIZE, arr, 0 * INT_SIZE, 4 * 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[30]; strcpy(destStr,sourceStr); printf("%s",destStr);
运行结果如下图所示:
但是,我们常常听人说strcpy是不安全的函数,为什么呢?先看看strcpy内部的循环判断条件:
while ((*strDest++ = *strSrc++) != '