-3 c pointers casting dereference
这是我运行的程序:
#include <stdio.h>
int main(void)
{
int y = 1234;
char *p = &y;
int *j = &y;
printf("%d %d\n", *p, *j);
}
Run Code Online (Sandbox Code Playgroud)
我对输出有点困惑.我所看到的是:
-46 1234
Run Code Online (Sandbox Code Playgroud)
我把这个程序写成了一个实验,不知道它会输出什么.我期待可能有一个字节y
.
这里发生了什么"幕后"?解除引用如何p
给我-46
?
正如其他人指出的那样,我必须进行明确的施法才能导致UB.我没有改变这一行char *p = &y;
,char *p = (char *)&y;
所以我没有使下面的答案无效.
此程序不会导致此处指出的任何UB行为.
Sur*_*ain 14
如果你有类似的东西,
int x = 1234;
int *p = &x;
Run Code Online (Sandbox Code Playgroud)
如果您取消引用指针,p
那么它将正确读取整数字节.因为你声明它是指针int
.它将知道sizeof()
运营商要读取多少字节.通常大小int
为4 bytes
(对于32/64位平台),但它取决于机器,这就是为什么它将使用sizeof()
运算符来知道正确的大小并将读取.
为了你的代码
int y = 1234;
char *p = &y;
int *j = &y;
Run Code Online (Sandbox Code Playgroud)
现在pointer p
指向y
但我们已经声明它是一个指向a的指针,char
所以它只会读取一个字节或字符char的任何字节.
1234
在二进制中将表示为
00000000 00000000 00000100 11010010
现在,如果你的机器是小端,它将存储反转它们的字节
11010010 00000100 00000000 00000000
11010010
是address 00
Hypothetical address
,00000100
是在address 01
等.
BE: 00 01 02 03
+----+----+----+----+
y: | 00 | 00 | 04 | d2 |
+----+----+----+----+
LE: 00 01 02 03
+----+----+----+----+
y: | d2 | 04 | 00 | 00 |
+----+----+----+----+
(In Hexadecimal)
Run Code Online (Sandbox Code Playgroud)
所以,现在如果取消引用pointer p
它只会读取第一个字节,结果应该是(-46
在的情况下,signed char
并210
在以下情况下unsigned char
,根据C标准的char的签署岬是"实现定义.)作为读取的字节是11010010
(因为我们指出signed char
(在这种情况下是signed char
).
在您的PC上,负数表示为2的补码,因此most-significant bit
是符号位.第一位1
表示符号.11010010 = –128 + 64 + 16 + 2 = –46
如果你取消引用pointer j
它将完全读取所有字节,int
因为我们声明它是指针int
和输出将1234
如果你声明指针j,int *j
那么这里*j
将读取sizeof(int)
4个字节(取决于机器).与char
指向它们的指针相同或任何其他数据类型将读取大小为char
1字节的字节数.
正如其他人指出的那样,你需要明确地转换char*
为char *p = &y;
违反约束 - char *
并且int *
不是兼容类型,而是写入char *p = (char *)&y
.
Joh*_*ode 10
编写的代码有几个问题.
首先,您通过尝试使用转换说明符打印对象的数字表示来调用未定义的行为:char
%d
在线C 2011草案,§7.21.6.1,第9节:
如果转换规范无效,则行为未定义.282)如果任何参数不是相应转换规范的正确类型,则行为未定义.
是的,当传递给可变函数时,类型的对象char
被提升为int
; printf
是特殊的,如果你想要明确定义输出,那么参数的类型和转换说明符必须匹配.要使用,, 或打印char
with %d
或unsigned char
参数的数值,必须使用长度修改器作为转换规范的一部分:%u
%o
%x
hh
printf( "%hhd ", *p );
Run Code Online (Sandbox Code Playgroud)
第二个问题是该行
char *p = &y;
Run Code Online (Sandbox Code Playgroud)
是违反约束 - char *
并且int *
不是兼容类型,并且可能具有不同的大小和/或表示2.因此,您必须显式地将源转换为目标类型:
char *p = (char *) &y;
Run Code Online (Sandbox Code Playgroud)
当其中一个操作数是; 时,会发生此规则的一个例外void *
; 那么演员阵容是没有必要的.
说了这么多,我拿了你的代码并添加了一个实用程序来转储程序中对象的地址和内容.下面介绍一下y
,p
和j
看起来像我的系统(SLES 10,GCC 4.1.2)上:
Item Address 00 01 02 03
---- ------- -- -- -- --
y 0x7fff1a7e99cc d2 04 00 00 ....
p 0x7fff1a7e99c0 cc 99 7e 1a ..~.
0x7fff1a7e99c4 ff 7f 00 00 ....
j 0x7fff1a7e99b8 cc 99 7e 1a ..~.
0x7fff1a7e99bc ff 7f 00 00 ....
Run Code Online (Sandbox Code Playgroud)
我在x86系统上,它是little-endian,因此它存储多字节对象,从最低地址的最低有效字节开始:
BE: A A+1 A+2 A+3
+----+----+----+----+
y: | 00 | 00 | 04 | d2 |
+----+----+----+----+
LE: A+3 A+2 A+1 A
Run Code Online (Sandbox Code Playgroud)
在小端系统上,寻址字节是最不重要的字节,在这种情况下是0xd2
(210
无符号,有-46
符号).
简而言之,您将打印该单字节的带符号十进制表示.
作为用于更广泛的问题,类型表达 *p
是char
与类型表达 *j
为int
; 编译器只是按表达式的类型.编译器在将源转换为机器代码时跟踪所有对象,表达式和类型.因此,当它看到表达式时*j
,它知道它正在处理整数值并适当地生成机器代码.当它看到表达式时*p
,它知道它正在处理一个char
值.