简体   繁体   English

C 和 C++ 中的二维数组差异

[英]2D array difference in C and C++

I have these two lines of code which I thought would compile both on C and C++.我有这两行代码,我认为它们可以在 C 和 C++ 上编译。

int a[3][3] = {{10,20,30},{40,50,60},{70,80,90}};
int *p[3] = {a+0, a+1, a+2};

C compiler compiles it fine. C编译器编译得很好。 On Visual Studio C++ compiler I get this error: error C2440: 'initializing': cannot convert from 'int (*)[3]' to 'int *'在 Visual Studio C++ 编译器上,我收到此错误:错误 C2440: 'initializing': cannot convert from 'int (*)[3]' to 'int *'

I'm trying to understand what is the difference between these two cases.我试图了解这两种情况之间的区别。

Arrays vs. pointers is probably one of the harder topics of C (and C++ which inherited that from C).数组与指针可能是 C(以及从 C 继承而来的 C++)较难的主题之一。 It's actually easy once you understood the concept behind but that concept might be unexpected by starters – I never saw anything similar in other programming languages.一旦你理解了背后的概念,这实际上很容易,但这个概念可能是初学者意想不到的——我从未在其他编程语言中看到过类似的东西。

Borgleader told in his comment: int a[3][3] decays to int* but that's false! Borgleader 在他的评论中说: int a[3][3]衰减为int*但那是错误的! (If it were true the issue of OP wouldn't exist.) (如果这是真的,那么 OP 的问题就不存在了。)

The trueth is:真相是:

  1. a is of type int [3][3] . aint [3][3]
  2. a may decay to int (*)[3] ( a pointer to array 3 of int ) a可能衰减为int (*)[3]指向 int 数组 3 的指针

Hence, the definition of OP has type mismatch errors:因此,OP 的定义存在类型不匹配错误:

int *p[3] = {a+0, a+1, a+2};

The elements of p have type int* but a+0 (as well as a+1 , a+2 ) provide an expression of int (*)[3] . p的元素具有int*类型,但a+0 (以及a+1a+2 )提供int (*)[3]的表达式。

This is exactly what clang tells in the Live Demo of Bob__ on Wandbox :这正是Clang 在 Wandbox 上 Bob__现场演示中所说的

prog.c:7:18: warning: incompatible pointer types initializing 'int *' with an expression of type 'int (*)[3]' [-Wincompatible-pointer-types]
    int *p[3] = {a+0, a+1, a+2};
                 ^~~

Bob__ used C with -std=c11 and -pedantic . Bob__ 使用 C 与-std=c11-pedantic

I changed it to C++ with -std=c++17 and no -pedantic .我使用-std=c++17将其更改为 C++ 并且没有-pedantic C++ reports this as error because it's by default much stricter concerning type compatibility. C++ 将此报告为错误,因为默认情况下它在类型兼容性方面更为严格。


Actually, I was confused by a comment on Quora which had this example.实际上,我对 Quora 上有这个例子的评论感到困惑。

Considering that C has ever been quite tolerant concerning non-matching types, the example might have worked.考虑到 C 对非匹配类型一直非常宽容,这个例子可能有效。 To illustrate this, I made a slightly extended example on godbolt.org:为了说明这一点,我在 Godbolt.org 上做了一个稍微扩展的例子:

#include <stdio.h>

int main()
{
  int a[3][3] = {{10,20,30},{40,50,60},{70,80,90}};
  int *p[3] = { a + 0, a + 1, a + 2 };
  int *p1[3] = { *(a + 0), *(a + 1), *(a + 2) };
  int *p2[3] = { a[0], a[1], a[2] };
  return 0;
}

For int *p[3] = { a + 0, a + 1, a + 2 };对于int *p[3] = { a + 0, a + 1, a + 2 }; it compiled:它编译:

  mov rcx, qword ptr [rbp - 168] # load rcx with address of a
  mov qword ptr [rbp - 80], rcx  # store rcx to p[0] 
  mov rcx, qword ptr [rbp - 168] # load rcx with address of a
  add rcx, 12                    # add 12 to rcx (1 * 3 * sizeof (int))
  mov qword ptr [rbp - 72], rcx  # store rcx to p[1]
  mov rcx, qword ptr [rbp - 168] # load rcx with address of a
  add rcx, 24                    # add 24 to rcx (2 * 3 * sizeof (int))
  mov qword ptr [rbp - 64], rcx  # store rcx to p[2]

for int *p1[3] = { *(a + 0), *(a + 1), *(a + 2) };对于int *p1[3] = { *(a + 0), *(a + 1), *(a + 2) }; :

  mov rcx, qword ptr [rbp - 168] # load rcx with address of a
  mov qword ptr [rbp - 112], rcx # store rcx to p1[0]  
  add rcx, 12                    # add 12 to rcx (1 * 3 * sizeof (int))
  mov qword ptr [rbp - 104], rcx # store rcx to p1[1] 
  mov rcx, qword ptr [rbp - 168] # load rcx with address of a
  add rcx, 24                    # add 24 to rcx (2 * 3 * sizeof (int))
  mov qword ptr [rbp - 96], rcx  # store rcx to p1[2]

for int *p2[3] = { a[0], a[1], a[2] };对于int *p2[3] = { a[0], a[1], a[2] }; :

  mov rcx, qword ptr [rbp - 168] # load rcx with address of a
  mov qword ptr [rbp - 144], rcx # store rcx to p2[0]  
  add rcx, 12                    # add 12 to rcx (1 * 3 * sizeof (int))
  mov qword ptr [rbp - 136], rcx # store rcx to p2[1] 
  mov rcx, qword ptr [rbp - 168] # load rcx with address of a
  add rcx, 24                    # add 24 to rcx (2 * 3 * sizeof (int))
  mov qword ptr [rbp - 128], rcx # store rcx to p2[2]

Live Demo on godbolt Godbolt 上的现场演示

Without going into too much depth, nearly the same code has been produced for all three lines.在不深入的情况下,所有三行都生成了几乎相同的代码。 (The only differences are the addresses after mov qword ptr [rbp - ... as the initializations are stored into variables which have different addresses on stack, of course.) (唯一的区别是mov qword ptr [rbp - ... 之后的地址,当然,初始化存储在堆栈上具有不同地址的变量中。)

It's not that surprising that *(a + 0) and a[0] result in equivalent code because according tocppreference: Subscript : *(a + 0)a[0]产生等效的代码并不奇怪,因为根据cppreference: Subscript

By definition, the subscript operator E1[E2] is exactly identical to *((E1)+(E2)) .根据定义,下标运算符E1[E2]*((E1)+(E2))完全相同。

but even the initialization with pointers of wrong types didn't make a difference.但即使使用错误类型的指针进行初始化也没有任何区别。

IMHO, this is good for two lessons:恕我直言,这有两个教训:

  1. Using correct types by introducing the necessary dereference operators prevents warnings (in C), errors (in C++).通过引入必要的解引用运算符来使用正确的类型可以防止警告(在 C 中)、错误(在 C++ 中)。

  2. Optimizing away dereference operators (at the cost of warnings) doesn't improve the generated binary code.优化取消引用运算符(以警告为代价)不会改进生成的二进制代码。


In another comment, the OP stated that在另一条评论中,OP 表示

To my understanding "a" is a pointer to the whole array...据我了解,“a”是指向整个数组的指针......

That's wrong.那是错误的。 a is an array. a是一个数组。 It may decay to a pointer if required.如果需要,它可能会衰减为指针。

That's a difference, and it's easy to illustrate by an example:这是一个区别,很容易通过一个例子来说明:

#include <stdio.h>

void printSizes(int a[3][3], int (*p)[3])
{
  puts("when a and p passed to a function:");
  printf("sizeof a: %u\n", (unsigned)sizeof a);
  printf("sizeof p: %u\n", (unsigned)sizeof p);
}

int main()
{
  int a[3][3] = {{10,20,30},{40,50,60},{70,80,90}};
  int (*p)[3] = { a + 0, a + 1, a + 2 };
  printf("sizeof a: %u\n", (unsigned)sizeof a);
  printf("sizeof p: %u\n", (unsigned)sizeof p);
  return 0;
}

Output:输出:

sizeof a: 36
sizeof p: 8
when a and p passed to a function:
sizeof a: 8
sizeof p: 8

Live Demo on ideone ideone 上的现场演示

The confusion about arrays and pointers comes probably from the fact that arrays decay in most cases to pointers.关于数组和指针的混淆可能来自这样一个事实,即数组在大多数情况下会衰减为指针。 Even the subscript operator ( operator[] ) is defined for pointers but not for arrays.甚至下标运算符 ( operator[] ) 也是为指针定义的,而不是为数组定义的。 The sizeof operator is one of the few exceptions and shows the difference. sizeof运算符是为数不多的例外之一,它显示了差异。

As arrays may not be used as arguments, there is no such difference anymore in function printSize() .由于数组可能不能用作参数,因此函数printSize()不再有这种区别。 Even with giving the array type the compiler uses the pointer type resulting from array decay.即使给出数组类型,编译器也会使用数组衰减产生的指针类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM