sizeof 应用于数组类型

Jac*_*ack 4 c arrays sizeof c11

c11 标准规定 sizeof,

“当应用于具有数组类型的操作数时,结果是数组中的字节总数”

  • (6.5.3.4,第 4 点)。

脚注(103)说:

“当应用于声明为数组或函数类型的参数时,sizeof 运算符会生成调整后(指针)类型的大小”。

我从中得出,当应用于数组类型时, sizeof 给出数组的大小(元素数量 x 元素大小),但应用于声明为数组类型的参数时,它给出指针的大小。

我的问题:

由于脚注,如何可能有一个不产生指针大小的数组类型对象?

我觉得在某些情况下,如果不知道这一点,我就无法相信操作符的大小。

谢谢。

编辑:我想我应该澄清我的担忧,如果定义了“int a[4]”,那么我从响应中看到 sizeof a==4*sizeof(int) ,但是 sizeof(a+0) 呢?看来 sizeof(a+1) 必须被评估为指针。我关心的是除了函数调用之外的数组衰减为指针的情况。

jua*_*nza 5

引用的关键点是“声明为数组类型的参数”和“调整后的(指针)类型”。这里指的是将“数组类型”的函数参数调整为指针类型。一旦进行了调整,类型就是指针,并且它的大小必须是指针的大小。不可能是其他任何东西。它是这样工作的:

void foo(int p[42]);
Run Code Online (Sandbox Code Playgroud)

调整为

void foo(int* p);
Run Code Online (Sandbox Code Playgroud)

这两个函数声明是等效的。所以 的类型pint*. 并且sizeof(int*)始终是指针的大小。

然而,在不同的上下文中,没有类型调整:

int a[42]; // no adjustment. a is an array of size 42;

sizeof(a); // gives the size of type int[42]
Run Code Online (Sandbox Code Playgroud)

这里,实际上的类型a是“size 42 array of int”。sizeof 运算符可以访问此(编译时)信息,因此可以给出该类型的正确大小。

请注意,这与数组衰减有关,在某些情况下,数组可以“衰减”为指向其第一个元素的指针。这种衰减将允许您foo使用数组参数进行调用:

int a[26];
foo(a);     // foo(int*): a decays to int*

int* p = a; // same phenomenon
Run Code Online (Sandbox Code Playgroud)

因此,调整会更改函数签名,并且衰减允许您将数组传递给需要指针的函数。

更新关于您的更新,二元算术运算符的应用是数组衰减为指向其第一个元素的指针的许多情况之一。例如

#include <stdio.h>

int main(void)
{
    int a[42];
    printf("sizeof(a) = %zu\n", sizeof(a));
    printf("sizeof(a+1) = %zu\n", sizeof(a+1));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

sizeof(a) = 168
sizeof(a+1) = 8
Run Code Online (Sandbox Code Playgroud)


Eli*_*gem 5

响应您的更新(关注sizeof(foo+1)类型情况:

\n\n

是的,在数组在这些情况下衰减为指针的基础上,sizeof应用于array_name + int相当于。sizeof &(array_name[int]);同样,要从数组中获取实际值,您不需要编写arr_name + 1,而是编写*(arr_name + 1).

\n\n

因此,考虑到脚注,何时sizeof产生实际的数组大小(以字节为单位)?为此,请查看标准中关于数组衰减为指针的内容:

\n\n
\n

除非它是 sizeof 运算符或一元 & 运算符的操作数,或者是用于初始化数组的字符串文字,否则类型为 \xe2\x80\x98\xe2\x80\x98array 类型为 \xe2\x80 的表达式\x99\xe2\x80\x99 转换为类型为 \xe2\x80\x98\xe2\x80\x98 的表达式,指向类型\xe2\x80\x99\xe2\x80\x99 的指针,该指针指向数组的初始元素对象并且不是左值。

\n
\n\n

意义:

\n\n
    \n
  • sizeof直接在数组变量上使用(sizeof array_var( )
  • \n
  • 取消引用指向数组的指针 ( sizeof *(&array_var))注意:当您将此指向数组的指针传递给另一个函数时,这也适用,但并不总是最好的方法(请参见下面的示例)
  • \n
  • 字符串文字(如char foo[] = "foobar";=>中的右值)sizeof("foobar");
  • \n
\n\n

在所有其他情况下(AFAIK),数组会衰减为指针,并且sizeof产生指针的大小:

\n\n
    \n
  • 数组算术 => 指针算术 (sizeof (array_var +1 ) )
  • \n
  • 将数组传递给函数(衰减为指针)
  • \n
  • ...
  • \n
\n\n

将数组传递给函数

\n\n

所以使用一元&运算符,就是将指向数组的指针传递给函数,但很少这样做。尽管如此,这里还是一个例子:

\n\n
void pointer_to_array(char (*ptr)[]);//pointer to array type\nvoid error_p_to_arr(char (*ptr)[]);\n\nint main ( void )\n{\n    char str[] = "some random string";//array of 18 bytes\n    printf(\n        "Sizeof array %zu\\n",\n        sizeof str\n    );\n    pointer_to_array(&str);\n    return 0;\n}\n//we need to specify the exact type, including size!\n//replace 18 with 10, you\'re fine, but use 20 and you\'re in trouble\nvoid pointer_to_array(char (*ptr)[18])\n{\n    printf(\n        "sizeof argument: %zu\\nsizeof array %zu",\n        sizeof ptr,//4 or 8\n        sizeof *ptr//18!! YaY\n    );\n}\n//if we don\'t specify the array size here\nvoid error_p_to_arr(char (*ptr)[])\n{\n    printf(\n        "sizeof argument: %zu\\nsizeof array %zu",\n        sizeof ptr,//4 or 8\n        sizeof *ptr//ERROR!\n    );\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

后者sizeof *ptr将导致错误(“\xe2\x80\x98sizeof\xe2\x80\x99 到不完整类型\xe2\x80\x98char[]\xe2\x80\x99 的无效应用”)。因为这种传递数组的方式很容易出错(必须在任何地方定义正确的大小),所以更常见的是简单地让数组衰减,并随之传递第二个参数:

\n\n
void common_usage(const char *ptr, size_t arr_len);\nint main ( void )\n{\n    char str[] = "some random string";\n    common_usage(str, sizeof str/sizeof *str);\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

它看起来更干净,更常见,而且更容易维护。

\n\n

请参阅此处的示例

\n