C 代码的未知输出
Unknown Output of C code
下面的代码如何给出2036、2036、2036的答案。
以下 C 代码的输出是什么?假设 x 的地址是 2000(十进制),一个整数需要四个字节的内存。
int main()
{
unsigned int x[4][3] = {{1, 2, 3}, {4, 5, 6},
{7, 8, 9}, {10, 11, 12}};
printf("%u, %u, %u", x+3, *(x+3), *(x+2)+3);
}
2000 4 8 12 16 20 24 28 32 36 40 44 48
+---+---+---+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12|
+---+---+---+---+---+---+---+---+---+---+---+---+
x { } size = 48
{ x[0] } { x[1] } { x[2] } { x[3] } size = 12 * 4
x = 2000
x + 3 = &x[3] = 2000 + 3 * ( 3 * 4) = 2036
^ ^-----^ size of int[3]
index
*(x+2) + 3 = &x[2] + 3 off = 2000 + 2 * (3 * 4) + 3 * 4 = 2036
尽管 x
和 x[0]
的地址相同,但类型(因此大小)不同。
您应该在 main
函数的末尾添加 return 0
。此外,您的打印语句应如下所示:
printf("%p, %p, %p\n", (void *)(x+3), (void *)*(x+3), (void *)(*(x+2)+3));
/* ^^ ^^ ^^^^ ^^^^^^^^^ ^ ^^^^^^^^ ^^^^^^^^^ ^ */
对于定义明确的行为。
正如您刚刚发现的那样,如果您不 post 精心编写代码,人们可能会对 SO 有点苛刻。要打印地址,请使用 %p
或来自 <inttypes.h>
的宏,例如 PRIXPTR
并强制转换为 uintptr_t
.
仅使用 %p
,您的代码可能应该是:
#include <stdio.h>
int main(void)
{
unsigned int x[4][3] =
{
{ 1, 2, 3 }, { 4, 5, 6 },
{ 7, 8, 9 }, { 10, 11, 12 }
};
printf("%p\n", (void *)x);
printf("%p, %p, %p\n", (void *)(x+3), (void *)(*(x+3)), (void *)(*(x+2)+3));
return 0;
}
现在您已经定义了行为,尽管这些值不像 2000 和 2036 那样方便。
但是,x+3
是数组开始后第4个数组的地址int
,所以假设sizeof(int) == 4
如上所说,也就是开始后36个字节x
— 2036 如果 x
是 2000.
*(x+3)
是第四个数组的起始地址;在相同的假设下为 2036。
*(x+2)+3
将 3 添加到 3 int
的第三个数组的地址,然后再向其添加 3。如果 x
是 2000,那么它也会在 x
开始后 36 个字节结束,或者在 2036 结束。
在我的 64 位机器上,输出为:
0x7fff5800a440
0x7fff5800a464, 0x7fff5800a464, 0x7fff5800a464
十六进制 0x24 当然是十进制的 36。
编辑:正如 Jonathan Leffler 指出的那样,我应该在打印 %p
之前转换为 (void*)
也许这会阐明内存寻址方面的情况:
#include <stdio.h>
int main() {
unsigned int x[4][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}};
// Output: Address, 1
printf("%p, %u\n", (void*)(x), **x);
// Output: Address + 12, 4
printf("%p, %u\n", (void*)(x+1), **(x+1));
// Output: Address + 24, 7
printf("%p, %u\n", (void*)(x+2), **(x+2));
// Output: Address + 4, 2
printf("%p, %u\n", (void*)((*x)+1), *(*(x)+1));
return 0;
}
**x
应该用来得到 x[0][0]
**(x+1)
应该用来得到 x[1][0]
**(x+2)
应该用来得到 x[2][0]
*(*(x)+1)
应该用来得到 x[0][1]
为了更加清楚:
#include <stdio.h>
int main() {
unsigned int x[4][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}};
printf("%p, %u\n", (void*)(x), **x); // Output: Address, 1
printf("%u\n", x[0][0]); // Output: 1
printf("%p, %u\n", (void*)(x+1), **(x+1)); // Output: Address + 12, 4
printf("%u\n", x[1][0]); // Output: 4
printf("%p, %u\n", (void*)(x+2), **(x+2)); // Output: Address + 24, 7
printf("%u\n", x[2][0]); // Output: 7
printf("%p, %u\n", (void*)((*x)+1), *(*(x)+1)); // Output: Address + 4, 2
printf("%u\n", x[0][1]); // Output: 2
return 0;
}
下面的代码如何给出2036、2036、2036的答案。
以下 C 代码的输出是什么?假设 x 的地址是 2000(十进制),一个整数需要四个字节的内存。
int main()
{
unsigned int x[4][3] = {{1, 2, 3}, {4, 5, 6},
{7, 8, 9}, {10, 11, 12}};
printf("%u, %u, %u", x+3, *(x+3), *(x+2)+3);
}
2000 4 8 12 16 20 24 28 32 36 40 44 48
+---+---+---+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12|
+---+---+---+---+---+---+---+---+---+---+---+---+
x { } size = 48
{ x[0] } { x[1] } { x[2] } { x[3] } size = 12 * 4
x = 2000
x + 3 = &x[3] = 2000 + 3 * ( 3 * 4) = 2036
^ ^-----^ size of int[3]
index
*(x+2) + 3 = &x[2] + 3 off = 2000 + 2 * (3 * 4) + 3 * 4 = 2036
尽管 x
和 x[0]
的地址相同,但类型(因此大小)不同。
您应该在 main
函数的末尾添加 return 0
。此外,您的打印语句应如下所示:
printf("%p, %p, %p\n", (void *)(x+3), (void *)*(x+3), (void *)(*(x+2)+3));
/* ^^ ^^ ^^^^ ^^^^^^^^^ ^ ^^^^^^^^ ^^^^^^^^^ ^ */
对于定义明确的行为。
正如您刚刚发现的那样,如果您不 post 精心编写代码,人们可能会对 SO 有点苛刻。要打印地址,请使用 %p
或来自 <inttypes.h>
的宏,例如 PRIXPTR
并强制转换为 uintptr_t
.
仅使用 %p
,您的代码可能应该是:
#include <stdio.h>
int main(void)
{
unsigned int x[4][3] =
{
{ 1, 2, 3 }, { 4, 5, 6 },
{ 7, 8, 9 }, { 10, 11, 12 }
};
printf("%p\n", (void *)x);
printf("%p, %p, %p\n", (void *)(x+3), (void *)(*(x+3)), (void *)(*(x+2)+3));
return 0;
}
现在您已经定义了行为,尽管这些值不像 2000 和 2036 那样方便。
但是,x+3
是数组开始后第4个数组的地址int
,所以假设sizeof(int) == 4
如上所说,也就是开始后36个字节x
— 2036 如果 x
是 2000.
*(x+3)
是第四个数组的起始地址;在相同的假设下为 2036。
*(x+2)+3
将 3 添加到 3 int
的第三个数组的地址,然后再向其添加 3。如果 x
是 2000,那么它也会在 x
开始后 36 个字节结束,或者在 2036 结束。
在我的 64 位机器上,输出为:
0x7fff5800a440
0x7fff5800a464, 0x7fff5800a464, 0x7fff5800a464
十六进制 0x24 当然是十进制的 36。
编辑:正如 Jonathan Leffler 指出的那样,我应该在打印 %p
(void*)
也许这会阐明内存寻址方面的情况:
#include <stdio.h>
int main() {
unsigned int x[4][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}};
// Output: Address, 1
printf("%p, %u\n", (void*)(x), **x);
// Output: Address + 12, 4
printf("%p, %u\n", (void*)(x+1), **(x+1));
// Output: Address + 24, 7
printf("%p, %u\n", (void*)(x+2), **(x+2));
// Output: Address + 4, 2
printf("%p, %u\n", (void*)((*x)+1), *(*(x)+1));
return 0;
}
**x
应该用来得到x[0][0]
**(x+1)
应该用来得到x[1][0]
**(x+2)
应该用来得到x[2][0]
*(*(x)+1)
应该用来得到x[0][1]
为了更加清楚:
#include <stdio.h>
int main() {
unsigned int x[4][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}};
printf("%p, %u\n", (void*)(x), **x); // Output: Address, 1
printf("%u\n", x[0][0]); // Output: 1
printf("%p, %u\n", (void*)(x+1), **(x+1)); // Output: Address + 12, 4
printf("%u\n", x[1][0]); // Output: 4
printf("%p, %u\n", (void*)(x+2), **(x+2)); // Output: Address + 24, 7
printf("%u\n", x[2][0]); // Output: 7
printf("%p, %u\n", (void*)((*x)+1), *(*(x)+1)); // Output: Address + 4, 2
printf("%u\n", x[0][1]); // Output: 2
return 0;
}