【问题标题】:sizeof applied to array typessizeof 应用于数组类型
【发布时间】:2023-03-05 13:36:02
【问题描述】:

c11 标准规定 sizeof,

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

  • (6.5.3.4,项目符号 4)。

脚注 (103) 说:

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

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

我的问题:

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

我觉得在某些情况下我不知道这一点就无法信任 sizeof 运算符。

谢谢。

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

【问题讨论】:

  • 基本上你只需要注意数组在作为参数传递时会衰减为指针,所以不要在作为参数传递给你的数组上使用sizeof
  • 只是重申void f(char p[]) 声明参数p 具有指针类型。

标签: c arrays sizeof c11


【解决方案1】:

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

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 的类型实际上是“大小为 42 的 int 数组”。 sizeof 运算符可以访问此(编译时)信息,因此可以为该类型提供正确的大小。

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

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

int* p = a; // same phenomenon

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

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

#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

【讨论】:

    【解决方案2】:

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

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

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

    除非它是 sizeof 运算符或一元 & 运算符的操作数,或者是用于初始化数组的字符串字面量,否则类型为 ''array of type'' 的表达式将转换为类型为 ' 的表达式'pointer to type'' 指向数组对象的初始元素,不是左值。

    意思:

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

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

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

    将数组传递给函数

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

    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 会导致错误(“‘sizeof’对不完整类型‘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

    【讨论】:

      【解决方案3】:

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

      例如

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

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

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

      【讨论】:

        【解决方案4】:

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

        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 *)

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

          • sizeof(a)sizeof(int*) 不是因为参数在调用时衰减,而是因为 int a[]调整int* a。数组衰减意味着你可以调用func(array),即使array是一个数组。
          • 函数内部一个指针,因为函数的参数已经调整为指针。因此,当您将数组传递给函数时,就会发生衰减。我在回答中添加了一些解释。
          • @juanchopanza 是的,我明白你在说什么。谢谢
          【解决方案6】:

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

          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

          【讨论】:

            猜你喜欢
            • 2010-09-06
            • 2016-10-22
            • 2015-06-27
            • 1970-01-01
            • 2018-04-23
            • 2017-10-09
            • 2015-10-29
            • 1970-01-01
            • 2018-07-17
            相关资源
            最近更新 更多