加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c – 当printf是变量的地址时,为什么要使用void *?

发布时间:2020-12-16 03:23:03 所属栏目:百科 来源:网络整理
导读:我在printf()中看到一些使用(void *). 如果我想打印一个变量的地址,可以这样做: int a = 19;printf("%d",a); 我想, a是一个只是一个整数的地址,对吧? 阅读的许多文章都是这样使用的: printf("%p",(void*)a); %p代表什么? (指针?) 为什么使用(void *)?
我在printf()中看到一些使用(void *).

如果我想打印一个变量的地址,可以这样做:

int a = 19;
printf("%d",&a);

>我想,& a是一个只是一个整数的地址,对吧?
阅读的许多文章都是这样使用的:

printf("%p",(void*)&a);

>%p代表什么? (指针?)
>为什么使用(void *)?我不能使用(int)& a?

解决方法

指针不是数字.他们往往是以这种方式进行内部代表性的,但在概念上是不同的.

void *被设计为通用指针类型.任何指针值(函数指针除外)可能会被转换为void *,而不会丢失信息.这通常意味着void *至少与其他指针类型一样大.

printfs“%p”格式需要一个类型为void *的参数.这就是为什么一个int *应该被转换为void *在这个上下文中. (没有隐式转换,因为它是一个可变的函数;没有声明的参数,所以编译器不知道该转换为什么.)

像“%d”打印指针或使用“%p”格式传递一个int *到printf的软盘做法是您可以在大多数当前系统上消除的东西,但它们使您的代码不可移植. (请注意,在64位系统中,void *和int是不同的大小,所以打印具有%d“的指针是非常便携的,而不仅仅是理论上的).

顺便提及,“%p”的输出格式是实现定义的.十六进制是常见的(大写或小写,带或不带前缀“0x”或“0X”),但这不是唯一的可能性.所有你可以指望的是,假设一个合理的实现,将以一种合理的方式来以人类可读的形式表示指针值(并且scanf将会理解printf的输出).

你阅读的文章是完全正确的.打印int *值的正确方法是

printf("%p",(void*)&a);

不要懒惰的出路;正确的做法并不困难.

建议阅读:comp.lang.c FAQ的第4部分(进一步建议阅读:所有其他部分.

编辑:

为了回应阿尔科特的问题:

There is still one thing I don’t quite understand. int a = 10; int *p = &a;,so p’s value is a’s address in mem,right? If right,then p’s value will range from 0 to 2^32-1 (if cpu is 32-bit),and an integer is 4-byte on 32-bit OS,right? then What’s the difference between the p’s value and an integer? Can p’s value go out of the range?

不同之处在于它们是不同的.

假设一个int,int *,void *和float都是32位的系统(这对于当前的32位系统是典型的).浮点数是32位的事实意味着它的范围是0到232-1吗?或-231到231-1?当然不是浮动范围(假设IEEE表示)约为-3.40282e 38至3.40282e 38,在整个范围内具有广泛变化的分辨率,加上异常值,如负零,次正规化数,非正规化数,无穷大和NaN(Not-a-数). int和float都是32位,你可以把一个float对象的32位视为一个int表示,但结果与float的值不会有任何直接的关系.例如,int的第二个低位具有特定的含义;如果为0,则为该值贡献0,如果为1则为2;浮点数的相应位有一个含义,但它是完全不同的(它贡献一个取决于指数值的值).

指针的情况非常相似.指针值有一个含义:它是一些对象的地址(或者其他几个东西的地址,但是我们现在将其放在一边).在大多数当前的系统中,将指针对象的位解释为整数可以让您在机器级别上有意义.但语言本身并不能保证甚至暗示,就是这样.

指针不是数字.

一个具体的例子:几年前,我碰到一些试图通过转换为整数来计算两个地址之间的字节差异的代码.这是这样的:

unsigned char *p0;
unsigned char *p1;
long difference = (unsigned long)p1 - (unsigned long)p0;

如果您假设指针只是数字,表示线性单片地址空间中的地址,则此代码是有意义的.但这种假设不被语言所支持.实际上,有一个系统,该代码旨在运行(Cray T90),它根本不起作用. T90有64位指针指向64位字.通过在指针对象的3个高位中存储偏移量,在软件中合成字节指针.以上述方式减去两个指针,如果它们都具有0个偏移量,则会给出地址之间的字数,而不是字节数.如果它们有非0的偏移量,那会给你无意义的垃圾. (从指针到整数的转换只是复制位;它可以完成工作,给你一个有意义的字节索引,但没有.)

解决方法很简单:放弃cast并使用指针算术:

long difference = p1 - p0;

其他寻址方案是可能的.例如,地址可能包含一个描述符(可能间接地)引用一个内存块,加上该块内的一个偏移量.

您可以假设地址只是数字,地址空间是线性和单片的,所有指针的大小相同,并且具有相同的表示形式,指针可以安全地转换为int,或者长时间再次返回,而不会丢失的信息.并且您根据这些假设编写的代码可能适用于大多数当前系统.但完全可能的是,一些未来的系统将再次使用不同的内存模型,并且您的代码将会中断.

如果您避免超出语言实际保证的任何假设,您的代码将会更加面向未来.甚至把便携性问题放在一边,它可能会更加清洁.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读