当 printf 是变量的地址时,为什么要使用 void*? [英] When printf is an address of a variable, why use void*?

查看:21
本文介绍了当 printf 是变量的地址时,为什么要使用 void*?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 printf() 中看到了一些 (void*) 的用法.

I saw some usage of (void*) in printf().

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

If I want to print a variable's address, can I do it like this:

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

  1. 我认为,&aa 的地址,它只是一个整数,对吗?
  2. 我读过的许多文章都使用这样的内容:

  1. I think, &a is a's address which is just an integer, right?
  2. Many articles I read use something like this:

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

  1. %p 代表什么?(指针?)
  2. 为什么要使用(void*)?我不能用 (int)&a 代替吗?
  1. What does %p stand for? (A pointer?)
  2. Why use (void*)? Can't I use (int)&a instead?

推荐答案

指针不是数字.它们通常在内部以这种方式表示,但它们在概念上是不同的.

Pointers are not numbers. They are often internally represented that way, but they are conceptually distinct.

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

void* is designed to be a generic pointer type. Any pointer value (other than a function pointer) may be converted to void* and back again without loss of information. This typically means that void* is at least as big as other pointer types.

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

printfs "%p" format requires an argument of type void*. That's why an int* should be cast to void* in that context. (There's no implicit conversion because it's a variadic function; there's no declared parameter, so the compiler doesn't know what to convert it to.)

草率的做法,例如使用 "%d" 打印指针,或使用 "%p> 将 int* 传递给 printf" 格式,是您在大多数当前系统上可能可以摆脱的东西,但它们使您的代码不可移植.(请注意,在 64 位系统上,void*int 的大小不同是很常见的,因此使用 %d" 打印指针是 真的不可移植,不仅仅是理论上.)

Sloppy practices like printing pointers with "%d", or passing an int* to printf with a "%p" format, are things that you can probably get away with on most current systems, but they render your code non-portable. (Note that it's common on 64-bit systems for void* and int to be different sizes, so printing pointers with %d" is really non-portable, not just theoretically.)

顺便说一下,"%p" 的输出格式是实现定义的.十六进制很常见(大写或小写,有或没有前导 "0x""0X"),但这不是唯一的可能性.您可以指望的是,假设一个合理的实现,它将是一种以人类可读形式表示指针值的合理方式(并且 scanf 将理解 printf).

Incidentally, the output format for "%p" is implementation-defined. Hexadecimal is common, (in upper or lower case, with or without a leading "0x" or "0X"), but it's not the only possibility. All you can count on is that, assuming a reasonable implementation, it will be a reasonable way to represent a pointer value in human-readable form (and that scanf will understand the output of printf).

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

The article you read is entirely correct. The correct way to print an int* value is

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

不要偷懒;做对了一点也不难.

Don't take the lazy way out; it's not at all difficult to get it right.

建议阅读:comp.lang.c 常见问题解答的第 4 部分.(进一步建议阅读:所有其他部分.

Suggested reading: Section 4 of the comp.lang.c FAQ. (Further suggested reading: All the other sections.

回应奥尔科特的问题:

还有一件事我不太明白.int a = 10;int *p = &a;,所以p的值就是a在mem中的地址,对吧?如果正确,那么 p 的值将在 0 到 2^32-1 之间(如果 cpu 是 32 位),并且整数在 32 位操作系统上是 4 字节,对吗?那么 p 的值和整数之间有什么区别呢?p 的值会超出范围吗?

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?

区别在于它们的类型不同.

The difference is that they're of different types.

假设一个系统 intint*void*float 都是 32 位(这是当前 32 位系统的典型情况).float 是 32 位的事实是否意味着它的范围是 0 到 232-1?或者 -231 到 231-1?当然不是;浮点数的范围(假设 IEEE 表示)大约是 -3.40282e+38 到 +3.40282e+38,在整个范围内具有广泛变化的分辨率,加上奇异值,如负零、次规范化数字、非规范化数字、无穷大和 NaN(不是-一个号码).intfloat 都是 32 位,你可以取一个 float 对象的 32 位并处理它作为 int 表示,但结果与 float 的值没有任何直接关系.例如,int 的第二个低位具有特定的含义;如果它是 0,则对值贡献 0,如果是 1,则对值贡献 2;float 的相应位具有意义,但完全不同(它提供的值取决于指数的值).

Assume a system on which int, int*, void*, and float are all 32 bits (this is typical for current 32-bit systems). Does the fact that float is 32 bits imply that its range is 0 to 232-1? Or -231 to 231-1? Certainly not; the range of float (assuming IEEE representation) is approximately -3.40282e+38 to +3.40282e+38, with widely varying resolution across the range, plus exotic values like negative zero, subnormalized numbers, denormalized numbers, infinities, and NaNs (Not-a-Number). int and float are both 32 bits, and you can take the 32 bits of a float object and treat it as an int representation, but the result won't have any straightforward relationship to the value of the float. The second low-order bit of an int, for example, has a specific meaning; it contributes 0 to the value if it's 0, and 2 to the value if it's 1; the corresponding bit of a float has a meaning, but it's quite different (it contributes a value that depends on the value of the exponent).

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

The situation with pointers is quite similar. A pointer value has a meaning: it's the address of some object (or any of several other things, but we'll set that aside for now). On most current systems, interpreting the bits of a pointer object as if it were an integer gives you something that makes sense on the machine level. But the language itself does not guarantee, or even hint, that that's the case.

指针不是数字.

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

A concrete example: some years ago, I ran across some code that tried to compute the difference in bytes between two addresses by casting to integers. It was something like this:

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

如果你假设指针只是数字,代表线性单体地址空间中的地址,那么这段代码就有意义了.但是该语言不支持该假设.事实上,有一个该代码打算在其上运行的系统(Cray T90),但它根本无法运行.T90 有指向 64 位字的 64 位指针.通过在指针对象的 3 个高位中存储偏移量,字节指针是在软件中合成的.以上述方式减去两个指针,如果它们都具有 0 偏移量,则会为您提供地址之间的数,而不是字节数.如果他们有非 0 偏移量,它会给你毫无意义的垃圾.(从指针到整数的转换只会复制这些位;它可以已经完成了为您提供有意义的字节索引的工作,但它没有.)

If you assume that pointers are just numbers, representing addresses in a linear monolithic address space, then this code makes sense. But that assumption is not supported by the language. And in fact, there was a system on which that code was intended to run (the Cray T90) on which it simply would not have worked. The T90 had 64-bit pointers pointing to 64-bit words. Byte pointers were synthesized in software by storing an offset in the 3 high-order bits of a pointer object. Subtracting two pointers in the above manner, if they both had 0 offsets, would give you the number of words, not bytes, between the addresses. And if they had non-0 offsets, it would give you meaningless garbage. (Conversion from a pointer to an integer would just copy the bits; it could have done the work to give you a meaningful byte index, but it didn't.)

解决方案很简单:放弃强制转换并使用指针算法:

The solution was simple: drop the casts and use pointer arithmetic:

long difference = p1 - p0;

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

Other addressing schemes are possible. For example, an address might consist of a descriptor that (perhaps indirectly) references a block of memory, plus an offset within that block.

您可以假设地址只是数字,地址空间是线性的和整体的,所有指针的大小和表示形式都相同,指针可以安全地转换为 int, 或到 long,然后再返回而不会丢失信息.您基于这些假设编写的代码可能适用于大多数当前系统.但是完全有可能某些未来的系统会再次使用不同的内存模型,并且您的代码会崩溃.

You can assume that addresses are just numbers, that the address space is linear and monolithic, that all pointers are the same size and have the same representation, that a pointer can be safely converted to int, or to long, and back again without loss of information. And the code you write based on those assumptions will probably work on most current systems. But it's entirely possible that some future systems will again use a different memory model, and your code will break.

如果您避免做出超出语言实际保证范围的任何假设,您的代码将更加面向未来.即使将便携性问题放在一边,它可能会更干净.

If you avoid making any assumptions beyond what the language actually guarantees, your code will be far more future-proof. And even leaving portability issues aside, it will probably be cleaner.

这篇关于当 printf 是变量的地址时,为什么要使用 void*?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆