【问题标题】:Does C copy the element every time you access an array?每次访问数组时,C 都会复制元素吗?
【发布时间】:2017-07-23 10:07:25
【问题描述】:

与 C++ 不同,C 不能返回引用。这是否意味着如果您访问A[i],它将在评估表达式时复制元素?

例如,如果A 是一个由 10 个ints 组成的数组,那么A[5]+=1; 是否只增加在评估A[5] 时构建的临时副本,这与 C++ 的向量不同,它会增加实际的第六个元素本身?

【问题讨论】:

  • A[5] 直接取消引用数组元素,不涉及临时副本。
  • A[5] += 1 递增数组A 的第一个从零开始的元素。如果它不这样做,则编译器有一个错误。不清楚你在问什么。
  • A[5]+=1A[5] = A[5] + 1 相同,优化编译器将尽可能少地复制,如果有的话,机器语言可以表达。
  • 确保您了解a = 3; 不读取a ... 数组成员访问没有什么不同

标签: c++ c arrays


【解决方案1】:

C 有一个称为“左值”的概念,它有点像引用。它不是类型(不是类型系统的一部分),但某些 C 表达式是“左值”,并且只有这样的表达式可以是赋值(包括操作赋值)或递增/递减的左操作数。

由于 C 没有运算符重载,因此无需在类型系统中进行区分,因为永远不需要将事物声明为右值与左值。正是在 C++ 中添加运算符重载的愿望导致引入了引用类型,以使重载运算符清楚地区分右值/左值。

【讨论】:

    【解决方案2】:

    如果是这样,您将根本无法修改数组,表达式 A[5]+=1; 将无效!

    相反,当您将标量参数传递给函数时,它的行为就像一个副本,因为它在返回时不会被修改。但是实际上作为指针传递的数组不会被复制(这将是难以承受的代价)并且在返回时可能会有所不同。

    【讨论】:

      【解决方案3】:

      A[5]+=1;仅在评估 A[5] 时增加构建的临时副本

      没有。 A[5]+=1; 将导致 A 的第 6 个元素增加 1。

      这很可能通过将A[5] 复制到 CPU 中的寄存器中,将其递增并将值复制回来来实现。

      这是一个程序:

      int main(){
          int A[10];
          A[5]+=1;
          return 0;
      }
      

      这是 gcc 为A[5]+=1; 生成的 x86-64 汇编程序

          mov     eax, DWORD PTR [rbp-28]
          add     eax, 1
          mov     DWORD PTR [rbp-28], eax
      

      这会将DWORD PTR [rbp-28] 移动到 32 位 EAX 累加器中,将其加 1 并将其移回同一位置。

      DWORD PTR [rbp-28] 通过其相对于堆栈帧(rbp)的位置(结束)来标识A 的第 6 个元素。

      关于引用的观点是红鲱鱼。 C 和 C++ 都编译为机器代码(可能通过汇编程序)。这些语言还有哪些其他功能不会影响 A[5]+=1; 的解释或编译方式。

      【讨论】:

        【解决方案4】:

        从带有A[i] 的数组中读取 时,C 总是复制一个元素,也就是说,当表达式A[i] 是一个“右值”时。然而,在考虑 writes 时,C 有一个“左值”表达式的概念,它本质上是表达式语法的一个受限子集,可以作为赋值的目标出现:

        X    = Y
        *X   = Y
        X[i] = Y
        X.n  = Y
        

        在这些情况下,“表达式”*XX[i]X.n 实际上并不计算为值——为方便起见,它们具有与表达式相同的语法,但不一样的语义。您可以将这些视为更像以下内容:

        memcpy(&X,    &Y, sizeof(Y));
        memcpy(&*X,   &Y, sizeof(Y));
        memcpy(&X[i], &Y, sizeof(Y));
        memcpy(&X.n,  &Y, sizeof(Y));
        

        或者,或者,将C视为具有多个不同的赋值运算符:

        _    = _  // direct assignment
        *_   = _  // indirect assignment
        _[_] = _  // special case of indirect assignment
        _._  = _  // special case of indirect assignment
        

        无论哪种方式,A[5] += 1 之类的赋值都会增加 A 的第六个元素的值,正如您所期望的那样,您可以像这样验证:

        int A[1] = { 1 };
        A[0] += 5;
        printf("%d\n", A[0]);  // 6
        

        【讨论】:

          【解决方案5】:

          不,i 数组索引只是一个指针,因此只传递一个位置而不是整个数组,并且实际内存位置的值会受到影响。

          试试下面的代码:

          #include<stdio.h>
          void function(int[]);
          int main()
          {
              int a[] = {0,1,2,3,4,5,6,7,8,9};
              int i;
              for(i = 0; i < 10; i++)
                  printf("%d, ",a[i]);    
              function(a);
              printf("\n");
              for(i = 0; i < 10; i++)
                  printf("%d, ",a[i]);
              return 0;
          }
          
          void function(int b[])
          {
              int i;
              for(i = 0; i < 10; i++)
                  b[i] *= 10;
          }
          

          OUTPUT

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-08-26
            • 2018-03-08
            • 1970-01-01
            • 2021-11-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多