a[1][2]
由编译器扩展,如下所示:*( *(a+1) +2 )
。因此,如果a
有这样的原型:int **a
,
上述表达应该像这样解释:
从符号表中获取a
的地址。注意它是a
指针
指针
现在我们按1
添加它,然后它指向旁边的某处
a
指向的地方。
然后我们取消引用它。我认为这是未定义的行为,
因为我们不知道a+1
是否有效,我们可以随意访问它。
好的,如果我们足够幸运,我们成功获得了价值
*(a+1)
。我们通过2
添加此内容。
完成此步骤后,我们取消引用(*(a+1) +2 )
。我们现在幸运吗?
我在第10章中的专家C编程中读到了这一点。它是否正确?
答案 0 :(得分:3)
编辑问题之后的新答案:
要使a[1][2]
有效,假定a
定义为int **a;
,则这两者都必须为真:
a
必须指向两个连续int *
个对象中的第一个; int *
个对象中的第二个必须指向三个连续int
个对象中的第一个。安排这个的最简单方法是:
int x[3];
int *y[2] = { 0, x };
int **a = y;
原始答案:
如果表达式a[1][2]
有效,那么a
的类型有很多不同的可能性(甚至忽略const
之类的限定词:
type **a;
(指向 type 的指针)type *a[n];
(指向 type 的n个指针数组)type (*a)[n];
(指向n type 的数组的指针)type a[m][n];
(n type 的m个数组的数组)确切地说,如何评估表达式取决于a
实际上具有哪种类型。
首先计算a + 1
。如果a
本身是指针(情况1或情况3),则直接加载a
的值。如果a
是一个数组(情况2或情况4),则加载a
的第一个元素的地址(与a
本身的地址相同)。
此指针现在由它指向的类型的1个对象偏移。在情况1和情况2中,它将被1“指向 type ”对象的指针偏移;在案例3和案例4中,它将被1“n type ”对象的数组偏移,这与n 类型对象的设置相同。
现在取消引用计算的(偏移)指针。在情况1和2中,结果具有类型“指向类型的指针”,在情况3和4中,结果具有类型“n type ”的数组。
计算下一个*(a + 1) + 2
。与第一种情况一样,如果*(a + 1)
是指针,则直接使用该值(此时,情况1和2)。如果*(a + 1)
是一个数组(情况3和4),则采用该数组的第一个元素的地址。
结果指针(此时始终具有类型“指向 type 的指针”)现在由2个类型对象偏移。现在解除引用最终偏移量指针,并检索类型对象。
答案 1 :(得分:0)
假设a
的定义如下所示:
int a[2][2] = {{1, 2}, {3, 4}};
以下是符号a
的存储内容:
[ 1 ][ 2 ][ 3 ][ 4 ]
在C中,当您对指针执行算术运算时,指针值递增或递减的实际数量取决于存储在数组中的类型的大小。 a
的第一个维度中包含的类型是int[2]
,因此当我们要求C计算指针值(a + 1)时,它会获取a
命名的位置并递增它通过int[2]
的大小,这导致指针指向包含整数值[3]的内存位置。所以是的,当你取消引用这个指针然后向它添加2时,结果就是整数值5.当你试图取消引用那个整数值时,它就没有意义了。
现在让我们说数组包含指针:
char const * one = "one",
two = "two",
three = "three",
four = "four";
char const * a[2][2] = {{one, two}, {three, four}};
将1添加到a然后取消引用它,并获得引用字符串“three”的char指针。添加两个,你会得到一个指针,指向一个现在较短的字符串“ree”。取消引用,你得到char值'r',但只有纯粹的运气才能避免内存保护错误。