应用于数组类型的 sizeof

sizeof applied to array types

c11 标准说 sizeof,

"when applied to an operand that has array type, the result is the total number of bytes in the array"

脚注 (103) 说:

"When applied to a parameter declared to have array or function type, the sizeof operator yields the size of the adjusted (pointer) type".

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

我的问题:

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

我觉得在某些情况下我无法信任 sizeof 运算符而不了解这一点。

谢谢。

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

拥有一个不产生指针大小的数组类型的对象很简单:不要在函数参数上这样做:

const int foo[32];

printf("hey, foo is %zu bytes\n", sizeof foo);

不会打印 sizeof (int *)

这是正常用法,您引用的文字指出,当将数组传递给函数时,它会衰减为指针,即使函数的原型指定了数组大小。

所以这个:

static void printsize(int x[100])
{
  printf("the argument is %zu bytes\n", sizeof x);
}

int main(void)
{
  const int foo[100];
  printsize(foo);
  return 0;
}

打印sizeof (int *).

只是为了澄清您的疑问,下面的代码可能会有所帮助:

void func(int a[])
{
  sizeof(a) is not equal to sizeof(int) * 10 but equal to sizeof(pointer)
  Because int a[] is adjusted to int * 
}

int main()
{
int a[10];
int *p = a;
//Initialize
//sizeof(a) = sizeof(int) * 10 

//sizeof(p) = sizeof(pointer)

func(a);
}

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

void foo(int p[42]);

调整

void foo(int* p);

这两个函数声明是等价的。所以p的类型是int*sizeof(int*) 始终是指针的大小。

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

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

sizeof(a); // gives the size of type int[42]

这里,a的类型确实是"size 42 array of int"。 sizeof 运算符可以访问此(编译时)信息,因此可以为该类型提供正确的大小。

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

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

int* p = a; // same phenomenon

因此,adjustment 更改函数签名,decay 允许您将数组传递给需要指针的函数。

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

#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;
}

输出:

sizeof(a) = 168
sizeof(a+1) = 8

脚注适用于(函数)参数。

例如

void foo(int param_arr[32])
{
     int local_arr[32];
}

param_arr 是函数的参数 - 虽然它看起来像一个数组,但它实际上是一个指针(int *)。所以 sizeof param_arr 产生 int *.

的大小

local_arr 不是参数。所以 sizeof 产生该数组的大小。

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

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

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

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.

含义:

  • 直接在数组变量(sizeof array_var)上使用sizeof
  • 解除对数组指针的引用(sizeof *(&array_var)注意:这也适用于将数组指针传递给另一个函数的情况,但这并不总是最好的方法(见下面的例子)
  • 字符串文字(如 char foo[] = "foobar"; => sizeof("foobar"); 中的右值)

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

  • 数组运算 => 指针运算 (sizeof (array_var +1 ))
  • 将数组传递给函数(退化为指针)
  • ...

将数组传递给函数

因此,使用一元 & 运算符, 可以将指向数组的指针传递给函数,但很少有人这样做。不过,还是举个例子:

void pointer_to_array(char (*ptr)[]);//pointer to array type
void error_p_to_arr(char (*ptr)[]);

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

后面的sizeof *ptr会报错("invalid application of ‘sizeof’ to incomplete type ‘char[]’")。因为这种传递数组的方式很容易出错(必须在所有地方定义正确的大小),所以更常见的做法是简单地让数组衰减,并传递第二个参数:

void common_usage(const char *ptr, size_t arr_len);
int main ( void )
{
    char str[] = "some random string";
    common_usage(str, sizeof str/sizeof *str);
    return 0;
}

它看起来更简洁,更常见,也更容易维护。

查看示例 here

随着数组衰减为指针,当数组作为参数传递给函数时,我们可以将其说明为将参数声明为数组,如下所示,

void function (char a[])
{ ... }

现在上面的声明被编译器以不同的方式解释为指针声明,因为函数实际上接收到指向类型 T 的指针,如下所示:

void function(char *a)
{ ... }

因此,编译器假装声明为指针的数组参数(类型char *)和sizeof实际上会给出指针的大小而不是数组的大小。

示例:

void function (char a[10])
{
    int i = sizeof(a);
    printf("%d\n", i);
}

输出实际上是 4 而不是 10