数组名不等于指针 sizeof()函数求数组大小错误问题及解决
作者:Z小旋
前言
今天在项目中需要求采样点的数量并且遍历,采样点用数组存储,自定义了一个函数想要用sizeof求其长度,然后遍历,结果失败了,查阅之后发现以下问题:
在main函数中,sizeof是可以正常工作的
#include <stdio.h> int Number[10]; int main() { int size = sizeof(Number); printf("数组大小为:%d\n",size); int len = sizeof(Number)/sizeof(int); printf("数组共有%d个数据\n",len); return 0; }
输出:
但是在自定义函数中就不可以了,如下:
#include <stdio.h> int Number[10]; void print_1(int n[]) { int size = sizeof(n); printf("数组大小为:%d\n",size); int len = sizeof(n)/sizeof(int); printf("数组共有%d个数据\n",len); } int main() { print_1(Number); return 0; }
那么我们首先要知道sizeof函数的功能:
sizeof是获取数据在内存中所占用的存储空间,以字节为单位来计数。
那么这个时候有的同学就会有问题了,两次传入的都是数组的首地址,为什么主函数中就可以,自定义函数中就不行呢?
得益于谭老爷子的C语言书籍普及量和销量,很多人认为数组名就是指向数组首地址的一个指针,但其实这个说法是错误的!
我们用一个最简单的例子,假设数组名是一个指针,那么:
#include <stdio.h> int Number[10]; int *Number2; int main() { int a=sizeof(Number); int b=sizeof(Number2); printf("a的大小为:%d \n b的大小为 %d\n",a,b); return 0; }
- Number是一个指针,Number2也是一个指针,正常情况下 大小都应该为8
- 但是实际的输出确实 a=40 b=8
- 也就是说数组名在某些情况下是不等于指针的,只是在一些情况下会退化为指针
首先我们要知道,单纯的数组名,不是指针
数组名是一个标识符,它标识出我们之前申请的一连串内存空间,而且这个空间内的元素类型是相同的——即数组名代表的是一个内存块及这个内存块中的元素类型 。
只是在大多数情况下数组名会“退化”(C标准使用的decay和converted这两个词)为指向第一个元素的指针。
而指针不是一种聚合类的数据结构,它保存着某一种类型的对象的地址(void*除外),也说它指向这个对象。
我们可以通过这个地址访问这个对象。用一个图来解释,其中a代表了整个我们声明的内存块,p仅仅指向了一个char类型的对象
char a[] = {'h' 'e' 'l' 'l' 'o'}; char b[] = {'w' 'o' 'r' 'l' 'd'}; char *p=b;
这是怎么一回事呢?
我们看一下C99标准:
C99 6.3.2.1 Lvalues, arrays, and function designators 中第三段是这样说的:
Except when it is the operand of the sizeof operator or the unary & operator, or is a
string literal used to initialize an array, an expression that has type ‘‘array of type’’ is
converted to an expression with type ‘‘pointer to type’’ that points to the initial element of
the array object and is not an lvalue. If the array object has register storage class, the
behavior is undefined.
这段话的意思是: 数组名只有在
- sizeof 运算符
- 取址 & 运算符
- 字符串常量初始化的数组 Str[]=“abcdef”
这三种情况下不会发生退化(array decay)
其余情况下调用数组名,都会退化成指向数组首地址的指针
再深入的话,就是要了解指针的sizeof
- 指针是用来记录另一个对象的地址,所以指针的内存大小就等于计算机内部地址总线的宽度。
- 对一个地址来取大小呢,如果是32位系统的话即为4,如果是64位系统的话为8,所以呢,在函数中sizeof获取的是指针的长度而不是数组的长度
- 指针变量的sizeof值与指针所指的对象没有任何关系。
结论
也就是说在c语言中,数组名在函数的调用中退化成了一个指针,对函数的参数使用Sizeof,sizeof获取的结果就是指针的大小,而不是数组本身的大小
再了解一下Sizeof的处理时间
sizeof是C语言的一种单目操作符(但有人也不这么以为,认为它是一种特殊的宏),如C语言的其他操作符++、–等一样。它并不是函数。
sizeof操作符以字节形式给出了其操作数的存储大小。操作数可以是一个表达式或括在括号内的类型名。操作数的存储大小由操作数的类型决定,简单的说其作用就是返回一个对象或者类型所占的内存字节数。
也就是说,Sizeof是一个C语言的操作符,那么他的处理阶段在编译阶段,也就是说你程序没有运行前,sizeof(arr)就被替换成了一个固定的常量,那么对于动态生成的数组大小是不能用sizeof来算出来的。
解决方法
在函数中多加一个参数,表示数组的长度
#include <stdio.h> int Number[10]; void print_1(int n[], int len) { printf("数组大小为:%d\n",len); printf("数组共有%d个数据\n",len/sizeof(int)); } int main() { print_1(Number,sizeof(Number)); return 0; }
以上就是问题的总结。仅为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。