我看到的一些使用(void*)在printf().
如果我想打印变量的地址,我可以这样做:
int a = 19;
printf("%d", &a);
Run Code Online (Sandbox Code Playgroud)
&a是a地址只是一个整数,对吧?我读过的很多文章都是这样的:
printf("%p", (void*)&a);
Run Code Online (Sandbox Code Playgroud)%p代表什么?(一个指针?)(void*)?我不能用(int)&a吗?Kei*_*son 17
指针不是数字.它们通常以内部方式表示,但它们在概念上是不同的.
void*被设计为通用指针类型.任何指针值(函数指针除外)都可以转换void*回来然后再返回而不会丢失信息.这通常意味着void*至少与其他指针类型一样大.
printfs "%p"格式需要一个类型的参数void*.这就是为什么int*应该void*在这种背景下施展.(没有隐式转换,因为它是一个可变参数函数;没有声明的参数,所以编译器不知道将它转换为什么.)
像打印指针与草率的做法"%d",或穿行int*于printf同一个"%p"格式,是事情,你也许可以蒙混过关的最新系统,但他们使你的代码不可移植.(请注意,这是常见的64位系统void*,并int具有不同的尺寸,因此与印刷指针%d"是真的不可移植,而不仅仅是理论上).
顺便提一下,输出格式"%p"是实现定义的.十六进制是常见的(大写或小写,有或没有前导"0x"或"0X"),但它不是唯一的可能性.所有你可以指望的是,假设一个合理的实现,它将是一种以人类可读形式表示指针值的合理方式(并且scanf将理解输出printf).
你读过的文章是完全正确的.打印int*值的正确方法是
printf("%p", (void*)&a);
Run Code Online (Sandbox Code Playgroud)
不要把懒惰的方式拿出来; 要做到这一点并不困难.
建议阅读:comp.lang.c FAQ的第4部分.(进一步建议阅读:所有其他部分.
编辑:
针对Alcott的问题:
还有一件事我不太明白.
int a = 10; int *p = &a;,所以p的值是mem中的地址,对吗?如果正确,那么p的值将在0到2 ^ 32-1之间(如果cpu是32位),并且在32位操作系统上整数是4字节,对吧?那么p的值和整数之间有什么区别?p的值可以超出范围吗?
不同之处在于它们的类型不同.
假定一个系统在其上int,int*,void*,和float都是32位(这是典型的当前32位系统).float32位的事实是否意味着它的范围是0到2 32 -1?或-2 31到2 31 -1?当然不是; 浮点范围(假设IEEE表示)约为-3.40282e + 38到+ 3.40282e + 38,在整个范围内具有广泛变化的分辨率,加上外来值,如负零,次标准化数,非规范化数,无穷大和NaN(非-一个号码). int并且float都是32位,你可以取一个float对象的32位并将其视为一个int表示,但结果将不会与该值的任何直接关系float.int例如,a 的第二个低位具有特定含义; 如果它为0,它对该值贡献0;如果它为1,则对该值贡献2; a的相应位float有一个含义,但它有很大不同(它提供的值取决于指数的值).
指针的情况非常相似.指针值有一个含义:它是某个对象的地址(或其他几个东西的任何一个,但我们暂时将它放在一边).在大多数当前系统中,将指针对象的位解释为整数,可以为您提供在机器级别上有意义的东西.但语言本身并不能保证甚至暗示这种情况.
指针不是数字.
一个具体的例子:几年前,我遇到了一些代码,试图通过转换为整数来计算两个地址之间的字节差异.它是这样的:
unsigned char *p0;
unsigned char *p1;
long difference = (unsigned long)p1 - (unsigned long)p0;
Run Code Online (Sandbox Code Playgroud)
如果你假设指针只是数字,表示线性单片地址空间中的地址,那么这段代码是有意义的.但该语言不支持该假设.实际上,有一个系统可以运行该代码(Cray T90),它根本就不会运行.T90有64位指针指向64位字.通过在指针对象的3个高位中存储偏移量,在软件中合成字节指针.以上述方式减去两个指针,如果它们都有0个偏移量,则会给出地址之间的字数而不是字节数.如果他们有非0偏移量,它会给你毫无意义的垃圾.(从指针到整数的转换只会复制这些位;它可以完成工作以给你一个有意义的字节索引,但它没有.)
解决方案很简单:删除强制转换并使用指针算法:
long difference = p1 - p0;
Run Code Online (Sandbox Code Playgroud)
其他寻址方案也是可能的.例如,地址可能包含一个描述符(可能是间接地)引用一块内存,加上该块内的偏移量.
您可以假设地址只是数字,地址空间是线性的和单一的,所有指针都是相同的大小并具有相同的表示,指针可以安全地转换为int,或返回long,然后再返回而不会丢失信息.您根据这些假设编写的代码可能适用于大多数当前系统.但是完全有可能某些未来的系统将再次使用不同的内存模型,并且您的代码将会中断.
如果您避免做出超出语言实际保证的任何假设,那么您的代码将更加面向未来.即使把可移植性问题放在一边,它也可能更清洁.
这里有如此多的精神错乱......
%p如果您只想打印出指针的表示,通常是正确的格式说明符.永远不要使用%d.
a的长度int和指针的长度(void*或其他)没有关系.i386上的大多数数据模型碰巧都有32位int和32位指针 - 其他平台,包括x86-64,都不一样!(这在历史上也被称为"世界上所有的VAX综合症".)http://en.wikipedia.org/wiki/64-bit#64-bit_data_models
如果由于某种原因想要在整数变量中保存内存地址,请使用正确的类型!intptr_t和uintptr_t.他们在stdint.h.见http://en.wikipedia.org/wiki/Stdint.h#Integers_wide_enough_to_hold_pointers