取消引用包含对象地址(数组数组)的出界指针
对于不同的REF
值,以下是否定义明确?
#include #define REF 1 #define S 1 int main(void) { int a[2][S] = {{1},{2}}; int *q = REF ? a[1] : 0; int *p = a[0] + S; memcpy (&q, &p, sizeof q); printf ("q[0] = %d\n", q[0]); return 0; }
注意, p
指向a[0]
的最后一个元素之后,而不指向数组a[0]
的元素,因此不能解引用。 但存储在p
中的地址a[1][0]
的地址。 p
语义(故意?)指向“到”(嗯,出) a[0]
但物理上指向a[1]
。
当原始物理上只有一个指针的位模式的副本可以在语义上指向一个对象吗?
也可以看看
我用不同的“角度”问了基本相同的C / C ++问题:
- 指针变量只是与某些运算符整数还是“神秘”?
- 指针的memcpy与赋值相同吗?
- 用相同类型的对象覆盖对象 (仅限C ++)
当原始物理上只有一个指针的位模式的副本可以在语义上指向一个对象吗?
没有这样的区别,因为a[0] + S
与a[1]
相同,假设内部数组声明为S
大小。
下列:
int a[2][S];
声明两元素数组,其中每个元素是一个int
类型的S
元素数组。 数组是有条理地存储的,并且在其元素之前/之间/之后没有填充。
我们将certificatea[0] + S == a[1]
成立。 它可以改写为:
*(a + 0) + S == *(a + 1)
通过指针算法,RHS将1 * sizeof(*a)
字节添加到a
,这与内部数组的大小相同。 LHS稍微复杂一点,因为在取消引用a
之后执行添加,因此它增加了:
S * sizeof(**a)
字节,
当它们指向相同类型的相同对象(相同的存储器位置)时,保证两侧相等。 因此,您可以将其重写为“绝对”单字节forms,如下所示:
(char *)a + S * sizeof(**a) == (char *)a + sizeof(*a)
这减少为:
S * sizeof(**a) == sizeof(*a)
我们知道,子数组*a
具有类型为**a
(即int
)的S
元素,因此两个偏移都是相同的。 QED
特定
int blah(int x, int y) { int a[2][5]; a[1][0] = x; a[0][y] = 9; return a[1][0]; }
标准中的任何内容都不会禁止编译器将其重新编码为int blah(int x, int y) { return x; }
当y>=5
时,也不会陷阱(或做任何事情),因为a[0]
和a[1]
是每个五个元素的不同数组。 在间接访问的结构的最后一个元素是单元素数组的情况下,编译器通常包括允许该数组上的指针算术产生指向结构外部存储的指针的代码。 虽然标准禁止这种指针算法,但它能够实现有用的结构,这些结构在C99之前不能以任何符合标准的方式实际实现。
注意,向a[0]
添加5将产生与a[1]
相同的int*
,但是“one-past”指针比较等于识别内存中下一个对象的指针的事实并不意味着它可以安全地用于访问后一个对象。 这样的访问通常可以工作,但这并不意味着编译器必须让它们这样做。