K&R 不讨论它,但他们使用它。我尝试通过编写一个示例程序来了解它是如何工作的,但效果并不理想:
#include <stdio.h>
int bleh (int *);
int main(){
char c = '5';
char *d = &c;
bleh((int *)d);
return 0;
}
int bleh(int *n){
printf("%d bleh\n", *n);
return *n;
}
它可以编译,但我的 print 语句会吐出垃圾变量(每次我调用程序时它们都不一样)。有任何想法吗?
最佳答案
在考虑指针时,绘制图表会有所帮助。指针是指向内存中地址的箭头,带有指示值类型的标签。地址表示要查找的位置,类型表示要获取的内容。转换指针会更改箭头上的标签,但不会更改箭头指向的位置。
main
中的d
是指向c
的指针,类型为char
。 char
是一个字节的内存,因此当 d
被取消引用时,您将获得该内存字节中的值。在下图中,每个单元格代表一个字节。
-+----+----+----+----+----+----+-
| | c | | | | |
-+----+----+----+----+----+----+-
^~~~
| char
d
当您将 d
转换为 int*
时,您是说 d
确实指向一个 int
值(value)。在当今的大多数系统上,int
占用 4 个字节。
-+----+----+----+----+----+----+-
| | c | ?₁ | ?₂ | ?₃ | |
-+----+----+----+----+----+----+-
^~~~~~~~~~~~~~~~~~~
| int
(int*)d
当您取消引用 (int*)d
时,您会得到一个由这四个内存字节确定的值。您获得的值取决于这些标记为 ?
的单元格中的内容,以及 int
在内存中的表示方式。
PC 是 little-endian ,这意味着 int
的值是这样计算的(假设它跨越 4 个字节):
* ((int*)d) == c + ?₁ * 2⁸ + ?₂ * 2¹⁶ + ?₃ * 2²⁴
。所以你会看到虽然这个值是垃圾,但如果你以十六进制打印 (printf("%x\n", *n)
),最后两位数字将始终是 35
(这是字符 '5'
的值)。
其他一些系统是大端的,并且以另一个方向排列字节:* ((int*)d) == c * 2²⁴ + ?₁ * 2¹⁶ + ?₂ * 2⁸ + ?₃
。在这些系统上,当以十六进制打印时,您会发现该值总是开始 35
。某些系统的 int
大小不同于 4 个字节。极少数系统以不同的方式排列 int
,但您极不可能遇到它们。
根据您的编译器和操作系统,您可能会发现每次运行程序时该值都不同,或者它始终相同但即使您对源代码进行微小调整也会发生变化。
在某些系统上,int
值必须存储在 4(或 2,或 8)的倍数的地址中。这称为 alignment要求。根据 c
的地址是否恰好正确对齐,程序可能会崩溃。
与您的程序相反,当您有一个 int
值并获取指向它的指针时,会发生以下情况。
int x = 42;
int *p = &x;
-+----+----+----+----+----+----+-
| | x | |
-+----+----+----+----+----+----+-
^~~~~~~~~~~~~~~~~~~
| int
p
指针p
指向一个int
值。箭头上的标签正确描述了内存单元中的内容,因此取消引用时不会出现意外。
关于c - 在 C 中转换指针的规则是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17260527/