int *p = ....
这里,p 是int * 类型的变量,也就是说,变量p 是一个指针,可以保存int 类型的地址。
p 指向的已分配和初始化内存的内存视图如下所示:
p ______
|
\|/
--------------------------
| 55 | 66 | 77 | 88 | 99 |
--------------------------
你对scanf("%d", p+i);的说法是正确的-
// as p already holds address, I get that we don't have to provide the &.....
问题一:
当我使用此代码打印时,
printf("%d", *p+i); // De-referencing the values in location p by using asterics, + the value of iteration,i //
我得到的值和 55,56,57,58,59 一样疯狂
一元*(间接)运算符的precedence高于二元+运算符。
因此,表达式 *p+i 将被评估为 - (*p)+i。
请注意,运算符优先级是将不同类型的运算符及其操作数分组的优先级。
当你在循环体中有printf("%d", *p+i); 语句,它将给定的分配数组从i = 0 迭代到i < 5 -
在第一次迭代中:(i = 0 和指针 p 指向数组的第一个元素 55):
(*p) + 0 -> 55 + 0 -> 55
在第二次迭代中:(i = 1 和指针 p 指向数组的第一个元素 55):
(*p) + 1 -> 55 + 1 -> 56
.....等等
.....
在第五次迭代中:(i = 4 和指针 p 指向数组的第一个元素 55):
(*p) + 4 -> 55 + 4 -> 59
因此你得到输出 - 55,56,57,58,59。
要获得预期的输出,请将运算符与操作数显式分组 - *(p + i)。
这样,首先将i 的值添加到指针中,然后取消引用结果指针(地址)。
问题二:
我尝试了这样的 printf 代码,它的工作原理非常棒,但我什至不必取消引用。这到底是怎么回事?
printf("%d", p[i]); // No asterics used. How does the compiler know I want the value and not the address? as p only should hold the addresses, and *p should give us the values in those addresses //
来自 C 标准#6.5.2.1:
下标运算符[]的定义是E1[E2]等同于(*((E1)+(E2)))。
通过这个下标运算符的定义-
p[i] -> *((p) + (i)) -> *(p + i)
这意味着,p[i] 和 *(p + i) 是等价的。
我已经在上面的答案中向您展示了 - 为什么*(p + i) 会给出预期的输出。
因此,当使用 p[i] 时,您会得到预期的输出。
问题三:
另外,如果我这样做 printf("%d", *p++); 那么它也可以工作。为什么我不了解 C 的工作原理?
再次查看运算符优先级表,您会发现++(后缀增量)运算符的优先级高于一元*(间接)运算符。
因此,表达式*p++ 将被评估为*(p++)。
后自增运算符将操作数的值增加1,但表达式的值是自增操作之前操作数的原始值。
当您在循环体中有 printf("%d", *p++); 语句时,它正在迭代给定的分配数组 -
在第一次迭代中:指针p 指向数组的第一个元素,即0th 索引处的元素。
*(p++) -> p will be incremented but the value of p in the expression will be its value prior to the increment operation which is location of element at `0`th index
-> *(0th location address) -> 55
在第二次迭代中:指针p 指向数组的第二个元素,即1st 索引处的元素。
*(p++) -> p will be incremented but the value of p in the expression will be its value prior to the increment operation which is location of element at `1`st index
-> *(1st location address) -> 66
……等等
......
在第五次迭代中:指针p 指向数组的第五个元素,即4th 索引处的元素。
*(p++) -> p will be incremented but the value of p in the expression will be its value prior to the increment operation which is location of element at `4`th index
-> *(4th location address) -> 99
而p 现在指向数组末尾之后的一个元素。
您将获得预期的输出 - 55,66,77,88,99。
问题四:
同样,如果我尝试这样做但这次使用scanf("%d", p[i]);,则会出现语法错误。我是说为什么?
在答案中重新审视上述下标运算符的定义,并回想一下您知道scanf() 期望变量(指针)的地址作为参数的事实。
p[i] -> *((p) + (i)) -> *(p + i)
*(p + i) ==> 取消引用指针p + i,这将在该位置给出值。
所以,p[i] 不是数组的ith 位置的地址,而是数组的ith 位置的值。因此,将p[i] 作为参数传递给scanf() 时会出错。
要获取ith位置的地址,只需在p[i]之前添加&操作-
scanf("%d", &p[i]);
这将按预期工作。又是一头雾水,为什么?
检查这个 -
&p[i] -> &(p[i]) -> &(*((p) + (i)) -> ((p) + (i)) -> p + i
| |
Precedence of operator |
[] is higher than |
& operator |
|
The operator & is used to get the address and the operator * is used for dereferencing.
These operators cancel the effect of each other when used one after another.
因此,声明
scanf("%d", &p[i]);
等价于这个语句
scanf("%d", p + i);
你很清楚为什么p + i 在scanf() 中作为参数给出时可以正常工作。
如果您有任何其他问题或困惑,请告诉我。