众所周知,如果您在C中以arr[i]
的形式访问数组元素,您也可以将该元素作为i[arr]
来访问,因为这些元素只能归结为*(arr + i)
和加法是可交换的。我的问题是为什么这适用于大于char
的数据类型,因为sizeof(char)
是1,而对我而言,这应该只用一个char来推进指针。
也许这个例子更清楚:
#include <string.h>
#include <stdio.h>
struct large { char data[1024]; };
int main( int argc, char * argv[] )
{
struct large arr[4];
memset( arr, 0, sizeof( arr ) );
printf( "%lu\n", sizeof( arr ) ); // prints 4096
strcpy( arr[0].data, "should not be accessed (and is not)" );
strcpy( arr[1].data, "Hello world!" );
printf( "%s, %s, %s\n", arr[1].data, 1[arr].data, (*(arr+1)).data );
// prints Hello world!, Hello world!, Hello world!
// I expected `hold not be accessed (and is not)' for #3 at least
return 0;
}
那么为什么在数组指针中添加一个按sizeof( struct large )
推进呢?
答案 0 :(得分:11)
在C中,定义指针算法以便写入
ptr + k
不会将指针推进k 字节,而是通过k 对象。因此,如果你有一个指向整数数组的指针并编写
*(myIntArrayPointer + 3)
您正在取消引用指向数组中索引3处元素的指针,而不是在对象开头之后开始三个字节的整数。
类似地,如果减去两个指针,则会获得它们之间的逻辑元素数,而不是总字节数。因此写作
(myIntArrayPointer + 3) - myIntArrayPointer
产生值3,即使它们之间有3 * sizeof(int)
个字节。
希望这有帮助!
答案 1 :(得分:6)
那是pointer arithmetic。当你写
some_pointer + i
编译为
some_pointer + (i * sizeof(*my_pointer))
(i
就此而言是int
:))
答案 2 :(得分:1)
键入数组/指针。编译器知道“事物”的大小,在这种情况下是你的结构。
C就是这样定义的;通过“一”推进指针将转到数组中的下一个事物。编译器知道要走多远。这适用于任何相关和等效的语法:
*(arr + i)
arr[i]
i[arr]
对于编译器知道的任何类型。
这称为“指针算术”,它至少还有一个有趣的属性:如果你有两个指向数组中项目的指针,你可以减去它们以获得它们之间的项目数,在对象中(即不个字节)。
答案 3 :(得分:1)
通过阅读C规范可以总是回答这类问题。试试吧!
§6.5.6加法运算符,第8段:
添加或减去具有整数类型的表达式时 从指针开始,结果具有指针操作数的类型。如果 指针操作数指向数组对象的元素和数组 足够大,结果指向一个偏离的元素 原始元素使得下标的差异 结果和原始数组元素等于整数表达式。
答案 4 :(得分:0)
arr被声明为4个结构的数组,每个结构包含1024个字符的char数组。
arr被解析为指向此结构数组的第一个元素的指针。当你将arr增加1时,这个新指针将跳过一个整个结构(它指向的类型),然后指向数组中的下一个元素。
这一切都归结为编译器知道指针指向的类型,然后如果递增数组,它将指向连续内存位置中相似类型的下一个元素。
在这种情况下,如果数组的基址是XYZ,那么arr + i = XYZ + i * sizeof(arr)/ sizeof(struct large)