简体   繁体   English

二维数组变量指针混淆

[英]2D array variable pointer confusion

I have a basic doubt in 2D arrays (C Language). 我对2D数组(C语言)有一个基本的疑问。 Consider a declaration of a 2D array as follows 考虑如下的2D数组的声明

int array[3][5];

Now when I do the following, the output of both th below printf's is the same: 现在,当我执行以下操作时,两个printf的输出都是相同的:

printf("%u\n", array);
printf("%u\n", *(array));

Now when try to to the following: 现在尝试以下内容:

printf("%u\n", array+1);
printf("%u\n", *(array)+1);

The outputs are different. 输出是不同的。 I get that the 2nd printf refers to array[0][1] and the first one to array[1][0]. 我得到第二个printf引用数组[0] [1]而第一个引用数组[1] [0]。 How does this work? 这是如何运作的? array is a pointer to what? 数组是什么指针?

Thanks in advance 提前致谢

I'll try to give you a technically-correct explanation so you'll know what's going on. 我会尝试给你一个技术上正确的解释,这样你就会知道发生了什么。 Not really complicated, but indeed counter-intuitive. 不是很复杂,但确实是反直觉的。

Intro: 介绍:

In C there are "lvalues" which basically represent "assignable" objects that have a place somewhere in memory, and "rvalues" which represent, well, "conceptual" values (not required to be placed anywhere in particular). 在C中,存在“左值”,其基本上表示在存储器中具有位置的“可分配”对象,以及“右值”,其表示“概念”值(不需要特别放置在任何地方)。

For example if you define int a = 5; 例如,如果你定义int a = 5; , then a is an lvalue of type int and value 5. Also it can be interpreted as (or rather, converted to) an rvalue of type int. ,则a是int类型和值5的左值。它也可以解释为(或者更确切地说,转换为)int类型的右值。 Such an rvalue would still be known to be equal to 5, but it would no longer contain the information about a 's location in memory. 这样一个rvalue仍将被已知是等于5,但它不再包含关于信息a在存储器中的位置。

Some expressions need lvalues (like the left hand-side of operator= because you have to assign to an object), and some need rvalues (like operator+ because you only need the integral values when you add, or the right side of operator=). 有些表达式需要左值(比如运算符的左侧=因为你必须分配给一个对象),有些需要rvalues(比如operator +因为你只需要添加时的积分值,或者运算符的右侧=) 。 If an expression needs an rvalue but you pass an lvalue, then it is converted to an rvalue. 如果表达式需要rvalue但是传递左值,则它将转换为rvalue。

Also, only rvalues are passed to functions in C (which means that C is strictly call-by-value, not call-by-reference). 此外,只有rvalues传递给C中的函数(这意味着C严格按值调用,而不是按引用调用)。

Some examples: 一些例子:

int a = 1;
a; // a is an lvalue of type int and value 1
a = a+3; // here the `a` is converted to an rvalue of type int and value 1, then after the addition there's an assignment, on the lhs there's an lvalue `a` and an rvalue `4`

Conversion from an lvalue to an rvalue is usually trivial and unnoticable (it's like taking the number 5 from a shelf labeled a ). 从左值到左值的转换通常是微不足道的,并且是不可察觉的(就像从标a a的架子上取5号)。 Arrays are basically the exception here. 数组基本上是例外。

The big thing: There are no rvalues of array type in C . 重要的是: C中没有数组类型的右值 There are pointer lvalues and rvalues, integer lvalues and rvalues, structure lvalues and rvalues etc... But only lvalue arrays. 有指针左值和右值,整数左值和右值,结构左值和右值等......但只有左值数组。 When you try to convert an lvalue of array type to an rvalue, you no longer have an array, you have a pointer to the array's first member. 当您尝试将数组类型的左值转换为右值时,您不再拥有数组,您有一个指向该数组的第一个成员的指针。 That is the root of confusion about arrays in C (and C++). 这是C(和C ++)中数组混淆的根源。

Explanation: 说明:

  • array
  • *(array)
  • array+1
  • *(array)+1

array is an lvalue of type int[3][5] (array of 3 ints of 5 ints). arrayint[3][5]类型的左值(5个整数的3个整数的数组)。 When you try to pass it to a function, it receives a pointer of type int (*)[5] (pointer to array of 5 ints) because that's what's left after lvalue-to-rvalue conversion. 当你试图将它传递给一个函数时,它会收到一个类型为int (*)[5]指针(指向5个整数的数组),因为这是在左值到右值转换后剩下的。

*(array) is tricker. *(array)是tricker。 First the lvalue-to-rvalue is performed resulting in an rvalue of type int(*)[5] , then operator* takes that rvalue and returns an lvalue of type int[5] , which then you attempt to pass to the function. 首先执行左值到右值,得到一个类型为int(*)[5] rvalue,然后operator*取该rvalue并返回一个类型为int[5]的左值,然后你尝试传递给该函数。 Hence again it's converted to an rvalue, resulting in an int* . 因此它再次转换为右值,产生一个int*

array+1 causes the array to be converted to an rvalue of type int(*)[5] and that rvalue gets incremented by one, so (according to the rules of pointer arithmetics) the pointer moves 1 * sizeof(int[5]) bytes forwards. array+1导致数组转换为int(*)[5]类型的rvalue,并且rvalue增加1,所以(根据指针算术的规则)指针移动1 * sizeof(int[5])字节转发。

*(array)+1 : see 2 points before, but the final rvalue of type int* gets incremented, again by rules of pointer arithmetics, by 1 * sizeof(int) . *(array)+1 :之前看到2个点,但int*类型的最终rvalue再次通过指针算术规则增加1 * sizeof(int)

No mystery here! 这里没有神秘感!

2D arrays in C are confusing. C中的2D数组令人困惑。
array and *array are both the same pointer, but are not the same type. array和* array都是相同的指针,但类型不同。
array is of type int[3][5] (which is an array, of size 5, of int[3] arrays). array的类型为int [3] [5](这是一个大小为5的int [3]数组的数组)。
*array is the first line of array, which is of type int[3]. * array是数组的第一行,类型为int [3]。
array+1 means array plus one element. array + 1表示数组加一个元素。 An element of array is int[3], so it's 12 bytes forward. 数组的元素是int [3],所以前进了12个字节。
*array+1 means *array plus one element. * array + 1表示*数组加一个元素。 An element of *array is int, so it's 4 bytes forward. *数组的元素是int,因此前进了4个字节。

Arrays are not pointers. 数组不是指针。 Ignore any answer, book, or tutorial that tries to tell you otherwise. 忽略任何试图告诉您的答案,书籍或教程。

An expression of array type, in most contexts, is converted (at compile time) into a pointer to the array's first element. 阵列型的表达,在大多数情况下,被转换 (在编译时)到一个指针数组的第一个元素。 The exceptions are: 例外情况是:

  • The operand of sizeof ( sizeof arr yields the size of the array, not the size of a pointer) sizeof的操作数( sizeof arr产生数组的大小,而不是指针的大小)
  • The operand of unary & ( &arr yields the address of the array, not of its first element -- same memory location, different type). 一元&&arr的操作数产生数组的地址,而不是它的第一个元素 - 相同的内存位置,不同的类型)。 This is particularly relevant to your example. 这与您的示例特别相关。
  • A string literal in an initializer used to initialize an array object ( char s[6] = "hello"; doesn't copy the address of the string literal, it copies its value) 用于初始化数组对象的初始值设定项中的字符串文字( char s[6] = "hello";不复制字符串文字的地址,它复制其值)

A 2-dimensional array is nothing more or less than an array of arrays. 二维数组不过是一个数组数组。 There are other data structures that can be used with the same x[y][z] syntax, but they're not true 2-dimensional arrays. 还有其他数据结构可以与相同的x[y][z]语法一起使用,但它们不是真正的二维数组。 Yours is. 你的是。

The [] indexing operator is defined in terms of pointer arithmetic. []索引操作符是根据指针算法定义的。 x[y] means *(x+y) . x[y]表示*(x+y)

The behavior of your code follows from these rules. 代码的行为遵循这些规则。

Read section 6 of the comp.lang.c FAQ . 阅读comp.lang.c FAQ的第6部分。 It's the best explanation of this stuff I've seen. 这是我见过的最好的解释。

And don't use "%u" to print pointer values; 并且不要使用"%u"来打印指针值; convert to void* and use "%p" . 转换为void*并使用"%p"

printf("%p\n", (void*)array);
printf("%p\n", (void*)*(array));

You can understand in this way: 你可以这样理解:

array points to an 3 rows with 5 columns each 数组指向3行,每行5列

when you do array+1, row changes so you go to 1st row. 当你做数组+ 1时,行改变,所以你去第1行。 You should try accessing with *(array + 1). 您应该尝试使用*(array + 1)进行访问。

when you do *(array), you point to 0th row and *(array)+1 moves ahead in column so element is array[0][1] 当你执行*(数组)时,你指向第0行,*(数组)+1在列中向前移动,因此元素是数组[0] [1]

指针中的单位增量与数据类型的大小一致。

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

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