我知道它是一个整数类型,可以在不丢失数据的情况下强制转换为指针,但为什么我要这样做呢?使用整数类型有什么优势void*可以保存指针和THE_REAL_TYPE*指针算术?
编辑
标记为"已被询问"的问题没有回答这个问题.问题是如果使用intptr_t作为一般的替换void*是一个好主意,那里的答案似乎是"不要使用intptr_t",所以我的问题仍然有效:什么是一个很好的用例intptr_t?
Sou*_*osh 15
主要原因是,你不能对a进行按位操作void *,但你可以做同样的事情intptr_t.
在许多场合,您需要对地址执行按位操作,您可以使用intptr_t.
但是,对于按位运算,最好的方法是使用unsigned对应的,uintptr_t.
正如@chux在另一个答案中所提到的,指针比较是另一个重要方面.
另外,FWIW,按照C11标准,§7.20.1.4,
这些类型是可选的.
intptr_t有什么用?
使用示例:订单比较.
比较指向相等的指针不是问题.
其他比较操作>, <=可能是UB.C11dr§6.5.8/ 5关系运算符.
所以转换为intptr_t第一个.
[编辑]新示例:按指针值对指针数组进行排序.
int ptr_cmp(const void *a, const void *b) {
intptr_t ia = (intptr) (*((void **) a));
intptr_t ib = (intptr) (*((void **) b));
return (ia > ib) - (ia < ib);
}
void *a[N];
...
qsort(a, sizeof a/sizeof a[0], sizeof a[0], ptr_cmp);
Run Code Online (Sandbox Code Playgroud)
[原实例]使用示例:测试指针是否为指针数组.
#define N 10
char special[N][1];
// UB as testing order of pointer, not of the same array, is UB.
int test_special1(char *candidate) {
return (candidate >= special[0]) && (candidate <= special[N-1]);
}
// OK - integer compare
int test_special2(char *candidate) {
intptr_t ca = (intptr_t) candidate;
intptr_t mn = (intptr_t) special[0];
intptr_t mx = (intptr_t) special[N-1];
return (ca >= mn) && (ca <= mx);
}
Run Code Online (Sandbox Code Playgroud)
由@MM评论,上述代码可能无法按预期工作.但至少它不是UB. - 只是非便携式功能.我希望用它来解决这个问题.
(u)intptr_t当您想要对指针进行算术运算时使用,特别是按位运算。但正如其他人所说,您几乎总是想要使用,uintptr_t因为按位运算最好在无符号中完成。但是,如果您需要进行算术右移,则必须使用intptr_t1。通常用于存储数据的指针,通常称为标记指针
在 x86-64 中,您可以使用地址的高 16/7 位作为数据,但您必须手动进行符号扩展以使指针规范,因为它没有像 ARM 2那样的用于忽略高位的标志。因此,例如,如果您有char* tagged_address那么您需要在取消引用之前执行此操作
char* pointer = (char*)((intptr_t)tagged_address << 16 >> 16);
Run Code Online (Sandbox Code Playgroud)
32位Chrome V8引擎使用smi(小整数)优化,其中低位表示类型
|----- 32 bits -----|
Pointer: |_____address_____w1| # Address to object, w = weak pointer
Smi: |___int31_value____0| # Small integer
Run Code Online (Sandbox Code Playgroud)
因此,当指针的最低有效位为 0 时,它将被右移以检索原始的 31 位有符号 int
int v = (intptr_t)address >> 1;
Run Code Online (Sandbox Code Playgroud)
欲了解更多信息,请阅读
另一种用法是当您传递一个有符号整数时,void*这通常在简单的回调函数或线程中完成
void* my_thread(void *arg)
{
intptr_t val = (intptr_t)arg;
// Do something
}
int main()
{
pthread_t thread1;
intptr_t some_val = -2;
int r = pthread_create(&thread1, NULL, my_thread, (void*)some_val);
}
Run Code Online (Sandbox Code Playgroud)
1当然,当实现对有符号类型进行算术移位时
2非常新的 x86-64 CPU 可能有UAI/LAM支持
在编写内存管理代码时,uintptr_t类型非常有用.这种代码想要通用通用指针(void*)与客户端进行通信,但内部对地址进行各种算术运算.
你可以通过char*而不是所有的东西来做同样的事情,结果看起来像前Ansi C.
并非所有内存管理代码都使用uintptr_t - 例如,BSD内核代码定义了具有类似属性的vm_offset_t.但是,如果您正在编写一个调试malloc包,为什么要创建自己的类型呢?
当你的printf中有%p可用时,它也很有用,并且正在编写需要在各种体系结构上以十六进制格式打印指针大小的整数变量的代码.
我发现intptr_t相当不太有用,除了可能作为转换时的方式站,以避免在同一个强制转换中改变签名和整数大小的恐惧警告.(编写在所有相关体系结构上传递-Wall -Werror的可移植代码可能有点困难.)
还有一个语义考虑因素.
A void*应该指向某种东西.尽管具有现代实用性,但指针不是存储器地址.好吧,它通常/可能/总是(!)拥有一个,但它不是一个数字.这是一个指针.它指的是一件事.
A intptr_t没有.它是一个整数值,可以安全地转换为指针/从指针转换,因此您可以将它用于古老的API,将其打包成pthread函数参数,就像这样.
这就是为什么你可以做一些intptr_t比你更多的数字和小事void*,为什么你应该通过使用适当的工作类型自我记录.
最终,几乎所有内容都可以是整数(请记住,您的计算机可以处理数字!).指针可能是整数.但他们不是.它们是指针,因为它们用于不同的用途.而且,从理论上讲,它们可能不是数字.
| 归档时间: |
|
| 查看次数: |
19021 次 |
| 最近记录: |