最近经常遇到sizeof 运算符在计算数组大小出错的问题,究其原因还是学艺不精,废话不多说,上代码。
首先看下面这段代码:
#include<iostream>
using namespace std;
void fun1(int *P)
{
cout<<"在函数1中"<<sizeof(P)<<endl;
}
void fun2(int P[])
{
cout<<"在函数2中"<<sizeof(P)<<endl;
}
int main()
{
int A[10];
int* B=new int[10];
cout<<"数组名"<<sizeof(A)<<endl;
cout<<"指针"<<sizeof(B)<<endl;
fun1(A);
fun2(A);
}
结果输出:
数组名40
指针4
在函数1中4
在函数2中4
这段代码,可以很明确的看出几个问题:
1.数组名和指针是有区别的
2.数组名在参数传递中会以指针形式传递
那么为什么呢,我们可以查看sizeof 运算符的定义http://zh.cppreference.com/w/cpp/language/sizeof
这里的解释是:
sizeof解释
1) 返回 类型 的对象表示的字节数。
2) 返回当 表达式 求值时所返回的类型的对象表示的字节数。
注解
依赖于计算机架构,字节可能具有八个或更多位,精确的位数记录于 CHAR_BIT 中。
sizeof(char)、sizeof(signed char)、sizeof(unsigned char) 总是返回 1。
不能对函数类型、不完整类型或位域泛左值使用 sizeof。
当应用于引用类型时,其结果是被引用类型的大小。
当应用于类类型时,其结果是该类的对象的大小与这种对象放入数组时所需的额外填充的大小的总和。
当应用于空类时,总是返回 1。
当应用于某个表达式时,sizeof 并不对表达式进行求值,并且即便表达式所代表的是某个多态对象,其结果也是该表达式的静态类型的大小。不进行左值向右值、数组向指针和函数向指针转换。不过,其在形式上对纯右值实参实施临时对象的实体化: sizeof 确定其结果对象的大小。 (C++17 起)
由此,我们可以看出sizeof 运算符得出的值都是已分配好的固定长度,比如sizeof(结构体) 得出来的值,就是结构体的大小。
由此,我们就可以知道了,为什么sizeof(A) 与 sizeof(B)的结果不一样了,因为一个是数组名,一个是指针类型。
那么,最关键的问题来了,为什么会出现这样的差异,不是应该按照很多人说的,数组名 == 指针吗。
我们看上面的例子fun1(A); 这个函数,很多人说,是数组名退化成指针,但是,我说这是扯犊子。
A++; 这个能成立吗?
由此,数组名,可以看做一个指针常量,但是它又多了指针不具备的数组长度,个人看法是 类似于结构体名 或者就是一个特殊的结构体。
但是,这结束了吗,NO!!!!!
我们看 fun2(A); 这个函数,又会有个困惑,不是数组名的传递吗?
这里我觉得是编译器挖的坑。
前面不是已经说明了吗,数组名是常量,但是常量在传递后就不是常量了,可以进行运算了,此时已经变成指针了。
不信的话,大家可以在void fun2(int P[]) 这个函数中,进行P++;
这个操作,在这里是可以成立的!!!
再举个例子
int main()
{
foo(10);
}
void foo(int i)
{
i++;
}
但是,常量变成变量,只是值的传递,其内涵和地址还是丢失了,于是fun2(A); 这个结果是正常的。
在深度解析一点sizeof就是,sizeof 只对具体对象的实例化后有效,例如:
struct test
{
int a,b,c,d,e,f,g,h;
};
int main()
{
test &r=*new test;
cout<<sizeof(test)<<endl;//32
cout<<sizeof(r)<<endl;//也是32
system("PAUSE");
}
r 引用的是整个的 test 对象而不是指向 test 的指针,所以 sizeof r 的结果和 sizeof test 完全相同。
虽然是个小问题,但是大家的共识与老师教的却常常是反过来的。
最后,在下再次表达深深的希望,愿我和我的同道中人能够真正以谨慎的研究态度来认真思考开发中的问题,这样才能在我们中间产生大师级的程序员,顶级的开发书籍。