C程序如何确定数组的大小?

fhg3lkii  于 9个月前  发布在  其他
关注(0)|答案(3)|浏览(84)

因为一个“数组”或数组名只是一个指向数据块的“起始点”的指针,sizeof操作符如何返回数组的大小?我知道这是在运行时确定的。
int int n [10]; //分配足够的内存来存储10个int,并将 * n * 作为指向第一个地址的指针。
这个问题的出现是因为我们不能使用操作符来确定一个malloc-ed内存块的大小。我的理解是,一个“正常”的数组和malloc-ed内存是一样的,除了堆的使用。
我无法理解为什么非堆数组和堆数组的行为不同。

fwzugrvs

fwzugrvs1#

虽然数组名在指针算术中用作指针,当它是函数参数时,数组 * 不是 * 指针。它是它自己的类型。粗略地说,sizeof运算符不能告诉你在编译时你不知道的对象的大小。(请参阅末尾关于VLA的注解。)C数组的大小,如在

...
int arr[10];
...

字符串
sizeof操作符可以报告arr的大小,因为arr被设置为一个连续的10个整数的块。它是它的“类型”的一部分,再次,宽松地说“类型”。
当你malloc一个数组时,你得到的是一个指向一个尚未确定的堆内存块的指针。在编译时,如果不分析malloc的参数,你显然不可能知道它的大小。编译器唯一能做的就是深入分析你的程序并弄清楚它。当然,这通常是完全不可能的,因为动态分配取决于用户。
VLA是一种特殊情况。sizeof运算符将在运行时计算大小(我相信是C99和更高版本)。关于here,您可以阅读更多的警告。

lndjwyie

lndjwyie2#

因为一个“数组”或数组名只是一个指向数据块“起始点”的指针,...
这个前提是假的。The name of an array designates the array.
对于固定长度的数组,在sizeof array中,编译器知道数组的大小并将其用作表达式的值。对于可变长度的数组,大小将在程序执行期间确定,编译器将生成使用该大小作为表达式的值的代码。
当一个数组在一个表达式中被使用,而不是作为sizeof的操作数,作为一元&的操作数,或者作为用于初始化数组的字符串字面量时,它会自动转换为指针。这意味着数组可以在其他上下文中用作指针。例如:

  • array[i]array转换为指针,执行地址运算以向其添加i元素,并解引用结果地址。
  • array + iarray转换为指针,并执行地址运算以产生指向数组元素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产生指针的大小。
你可以创建一个左值来指定动态分配内存中的数组,如下所示:

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.

字符串
现在pA指向数组(与指向第一个元素的指针“相同”的值,但其类型不同),因此*pA指定数组。sizeof *pA将产生数组的大小。并且您可以使用(*pA)[i]访问数组元素。但使用pE[i]更方便,这是一样的。
还要注意的是,int *pE = *pA;将等价于上面定义的pE*pA指定数组,因此它会自动转换为指向其第一个元素的指针,即&(*pA)[0]。或者我们可以使用int *pE = p;

kgsdhlau

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,在编译时计算,行为不同,因为它不是编译器任务来确定它将指向什么。

相关问题