每次访问数组时,C 都会复制元素吗?

Posted

技术标签:

【中文标题】每次访问数组时,C 都会复制元素吗?【英文标题】:Does C copy the element every time you access an array? 【发布时间】: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 ... 数组成员访问没有什么不同 【参考方案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

【讨论】:

以上是关于每次访问数组时,C 都会复制元素吗?的主要内容,如果未能解决你的问题,请参考以下文章

每次需要访问特定索引处的元素时是不是可以重新排序数组?

在 C++ 中访问列表列表的元素

访问 useRef 中的任何元素都会抛出“未定义”

如何使用指针表达式访问 C 中的二维数组的元素?

C中的二维数组和指针 - 如何访问元素?

当我尝试删除数组时,C++ 访问冲突读取位置 0xDDDDDDCD 已更新