因为一个“数组”或数组名只是一个指向数据块的“起始点”的指针,sizeof操作符如何返回数组的大小?我知道这是在运行时确定的。 int int n [10]; //分配足够的内存来存储10个int,并将 * n * 作为指向第一个地址的指针。 这个问题的出现是因为我们不能使用操作符来确定一个malloc-ed内存块的大小。我的理解是,一个“正常”的数组和malloc-ed内存是一样的,除了堆的使用。 我无法理解为什么非堆数组和堆数组的行为不同。
因为一个“数组”或数组名只是一个指向数据块“起始点”的指针,... 这个前提是假的。The name of an array designates the array. 对于固定长度的数组,在sizeof array中,编译器知道数组的大小并将其用作表达式的值。对于可变长度的数组,大小将在程序执行期间确定,编译器将生成使用该大小作为表达式的值的代码。 当一个数组在一个表达式中被使用,而不是作为sizeof的操作数,作为一元&的操作数,或者作为用于初始化数组的字符串字面量时,它会自动转换为指针。这意味着数组可以在其他上下文中用作指针。例如:
void *p = malloc(10 * sizeof (int));
int (*pA)[10] = p; // Make a pointer to an array at p.
int *pE = &(*pA)[0]; // Make a pointer to the first element of pA.
T name[count]和T *name都引用了一个地址,主要区别是第一个在编译时被当作一个众所周知的区域,在运行时被分配,值被当作它指向的地址,而第二个只是一个指针,没有在编译时分配的内存区域。编译器不关心T *name的范围;所以sizeof,在编译时计算,行为不同,因为它不是编译器任务来确定它将指向什么。
3条答案
按热度按时间fwzugrvs1#
虽然数组名在指针算术中用作指针,当它是函数参数时,数组 * 不是 * 指针。它是它自己的类型。粗略地说,
sizeof
运算符不能告诉你在编译时你不知道的对象的大小。(请参阅末尾关于VLA的注解。)C数组的大小,如在字符串
sizeof
操作符可以报告arr
的大小,因为arr
被设置为一个连续的10个整数的块。它是它的“类型”的一部分,再次,宽松地说“类型”。当你
malloc
一个数组时,你得到的是一个指向一个尚未确定的堆内存块的指针。在编译时,如果不分析malloc
的参数,你显然不可能知道它的大小。编译器唯一能做的就是深入分析你的程序并弄清楚它。当然,这通常是完全不可能的,因为动态分配取决于用户。VLA是一种特殊情况。
sizeof
运算符将在运行时计算大小(我相信是C99和更高版本)。关于here,您可以阅读更多的警告。lndjwyie2#
因为一个“数组”或数组名只是一个指向数据块“起始点”的指针,...
这个前提是假的。The name of an array designates the array.
对于固定长度的数组,在
sizeof array
中,编译器知道数组的大小并将其用作表达式的值。对于可变长度的数组,大小将在程序执行期间确定,编译器将生成使用该大小作为表达式的值的代码。当一个数组在一个表达式中被使用,而不是作为
sizeof
的操作数,作为一元&
的操作数,或者作为用于初始化数组的字符串字面量时,它会自动转换为指针。这意味着数组可以在其他上下文中用作指针。例如:array[i]
将array
转换为指针,执行地址运算以向其添加i
元素,并解引用结果地址。array + i
将array
转换为指针,并执行地址运算以产生指向数组元素i
的指针。foo(array);
将array
转换为指针,并将该指针传递给函数foo
。此外,当函数参数声明为数组时,声明会自动调整为声明指针。
我的理解是,一个“正常”的数组与malloc-ed内存是一样的,除了堆的使用。
数组无论何时何地出现都是一个数组。然而,对于所谓的“普通”数组,您有一个名称。
int array[10];
定义了一个数组(包括为它保留内存)并声明array
为它的名称。int *p = malloc(10 * sizeof *p);
也为数组保留足够的内存,您可以使用该内存存储数组,p
不是数组的名称;它只是一个指向内存的指针。在
sizeof array
中,sizeof
的操作数是一个数组,因为array
命名了数组。所以sizeof
产生数组的大小。在sizeof p
中,sizeof
的操作数是一个指针,而不是数组。所以sizeof p
产生指针的大小。你可以创建一个左值来指定动态分配内存中的数组,如下所示:
字符串
现在
pA
指向数组(与指向第一个元素的指针“相同”的值,但其类型不同),因此*pA
指定数组。sizeof *pA
将产生数组的大小。并且您可以使用(*pA)[i]
访问数组元素。但使用pE[i]
更方便,这是一样的。还要注意的是,
int *pE = *pA;
将等价于上面定义的pE
。*pA
指定数组,因此它会自动转换为指向其第一个元素的指针,即&(*pA)[0]
。或者我们可以使用int *pE = p;
。kgsdhlau3#
当你将一个数组声明为
type name[count]
时,它将被转换为分配的空间,这取决于上下文,name
的值被视为该字节块的第一个地址。例如,全局声明的int32_t V[10]
将被转换为40字节,以分配给动态内存。而在函数内部,当调用函数时,在堆中保留40个字节。运算符sizeof
在编译时计算,因此,编译器能够识别需要利用的空间,并且sizeof(V)
返回40
。当您将指针声明为type *name
时,编译时唯一已知的信息是:time是sizeof(void *)
字节,用于保存它指向的地址,因此编译器无法知道该变量的范围,它可能只是存储一个特定的地址,而不是引用固定大小的内存。T name[count]
和T *name
都引用了一个地址,主要区别是第一个在编译时被当作一个众所周知的区域,在运行时被分配,值被当作它指向的地址,而第二个只是一个指针,没有在编译时分配的内存区域。编译器不关心T *name
的范围;所以sizeof
,在编译时计算,行为不同,因为它不是编译器任务来确定它将指向什么。