此代码示例正确打印数组.
int b[2] = {1, 2};
int *c = &b;
int i, j,k = 0;
for (i = 0;i < 2; i++) {
printf("%d ", *(c+i));
}
Run Code Online (Sandbox Code Playgroud)
而这个打印两个垃圾值.
int b[2] = {1, 2};
int i, j,k = 0;
for (i = 0;i < 2; i++) {
printf("%d ", *(&b+i));
}
Run Code Online (Sandbox Code Playgroud)
为什么两个代码示例的行为不同?
声明:
int b[2] = {1, 2};
Run Code Online (Sandbox Code Playgroud)
创建两个数组int与值1,2.
假设int的系统大小是4字节,那么数组b[]应该存储在内存中,如下所示:
first ele +----------+
(b + 0) ---?| 1 | 0xbf5c787c <----- &b , (c + 0)
next ele +----------+
(b + 1) ---?| 2 | 0xbf5c7880 <------------- (c + 1)
+----------+
(b + 2) ---?| ? | 0xbf5c7884 <----- (&b + 1) next array
+----------+
---?| ? | 0xbf5c7888
+----------+
---?| ? | 0xbf5c788c <----- (&b + 2) next array
+----------+
---?| ? | 0xbf5c7890
+----------+
? means garbage value
b[] array in memory from 0xbf5c787c to 0xbf5c7880
each cell is four bytes
Run Code Online (Sandbox Code Playgroud)
在上图中,具有值的存储器单元? 意味着垃圾值而未分配(存储器来自0xbf5c7884未分配给我们的阵列).的值1,2在地址存储在存储器中0xbf5c787c和0xbf5c7880,即在阵列分配b[].
让我们来代替印刷价值观,我们打印您使用您的代码访问的内存地址(c + i)和(&b + i),对于这个考虑下面的程序:
#include<stdio.h>
int main(){
int b[2] = {1, 2};
int i = 0;
int *c = &b; //Give warning: "assignment from incompatible pointer type"
printf("\n C address: "); // outputs correct values
for (i = 0; i < 2; i++) {
printf("%p ", (void*)(c + i));
}
printf("\n B address: "); // outputs incorrect values/ and behaving differently
for (i = 0; i < 2; i++) {
printf("%p ", (void*)(&b + i)); // Undefined behavior
}
return 1;
}
Run Code Online (Sandbox Code Playgroud)
输出:
C address: 0xbf5c787c 0xbf5c7880
B address: 0xbf5c787c 0xbf5c7884
Run Code Online (Sandbox Code Playgroud)
检查此代码是否正常工作@ Codepade
通知,(c + i)打印带有值的单元格的正确地址1,2因此第一个代码中的输出是正确的.而(&b + i)打印地址值未分配给数组b[](在数组之外b[])而访问此内存会在运行时给出未定义的行为(不可预测).
实际上b和之间存在差异&b.
b是一个数组,它的类型是int[2],b衰变为int*大多数表达式中第一个元素的地址(读取:一些例外,其中数组名称不会衰减成指向第一个元素的指针?).并b + 1指向int数组中的下一个元素(注意图表).
&b是完整数组的地址,其类型是int(*)[2], (&b + 1)指向int[2]未在程序中分配的下一个类型数组(在图中注意其中的(&b + 1)点).
要知道与其他一些有趣的差异b和&b阅读:什么sizeof(&array)回报?
在第一个代码snipe中,当你这样做时c = &b,你将分配数组的地址int*(在我们的例子中0xbf5c787c).使用GCC编译器,此语句将发出警告:"从不兼容的指针类型分配".
因为c 是指针int,所以 *(c + i)打印存储在地址的整数(c + i).对于 数组中第二个元素i = 1 的值(c + 1)(在我们的示例中0xbf5c7880)因此正确*(c + 1)打印2.
关于int *c = &b;第一个代码中的赋值,我强烈建议阅读@ AndreyT的答案如下.使用指针访问数组元素的正确和简单方法如下:
int b[2] = {1, 2};
int *c = b; // removed &, `c` is pointer to int
int i;
for (i = 0; i < 2; i++){
printf("%d ", *(c + i));
// printf("%d ", c[i]); // is also correct statement
}
Run Code Online (Sandbox Code Playgroud)
在你的第二个代码,添加i到&b使其指向外面分配的内存和printf语句您使用访问内存的*引用操作原因在运行时这些代码无效的内存访问和行为是未定义的.这就是第二段代码在不同执行时表现不同的原因.
您的代码编译因为语法正确,但在运行时,OS内核可以检测到未分配的内存.这可能导致操作系统内核向导致异常的进程发送信号核心转储(有趣的是:由于OS检测到进程的内存权限违规 - 对有效内存的无效访问给出:SIGSEGV,并且访问无效地址给出:SIGBUS).值得一提的是,您的程序可以在没有任何失败的情况下执行并产生垃
关于第二个代码,使用'指向数组的指针'打印数组的正确方法如下:
#include<stdio.h>
int main(){
int b[2] = {1, 2};
int i;
int (*c)[2] = &b; // `c` is pointer to int array of size 2
for(i = 0; i < 2; i++){
printf(" b[%d] = (*c)[%d] = %d\n", i, i, (*c)[i]); // notice (*c)[i]
}
return 1;
}
Run Code Online (Sandbox Code Playgroud)
输出:
b[0] = (*c)[0] = 1
b[1] = (*c)[1] = 2
Run Code Online (Sandbox Code Playgroud)
检查@ codepade.*c由于[]操作符的优先级高于* 取消引用操作符(而如果使用指向int的指针,则不需要像上面的代码那样使用括号),需要注意周围的括号.
这是因为ptr+i应用指针算术运算的指针类型:
i指针int,这与索引数组相同.由于指向数组的指针与指向其第一个元素的指针相同,因此代码可以正常工作.i指向两个数组的指针int.因此,添加会使您超出分配的内存,从而导致未定义的行为.以下是这一点的快速说明:
int b[2] = {1,2};
printf("%p\n%p\n%p\n", (void*)&b, (void*)(&b+1), (void*)(&b+2));
Run Code Online (Sandbox Code Playgroud)
在具有32位ints 的系统上,这将打印以8个字节分隔的地址 - 大小为int[2]:
0xbfbd2e58
0xbfbd2e60
0xbfbd2e68
Run Code Online (Sandbox Code Playgroud)
int *c = &b;
Run Code Online (Sandbox Code Playgroud)
这实际上是无效的。你需要一个演员阵容。
int *c = (int *) &b;
Run Code Online (Sandbox Code Playgroud)
两个表达式:
*(c+i)
and
*(&b+i)
Run Code Online (Sandbox Code Playgroud)
不一样。在第一个表达式中i添加到 a int *,在第二个表达式中i添加到 a int (*)[2]。随着int *c = (int *) &b;您将 a 转换int (*)[2]为int *. c + i指向but的i第-个int元素,指向将指针值移动到实际数组对象之外的元素。c&b+iint [2]b
| 归档时间: |
|
| 查看次数: |
424 次 |
| 最近记录: |