【问题标题】:What is the use of intptr_t?intptr_t 有什么用?
【发布时间】:2016-05-06 09:38:48
【问题描述】:

我知道它是一个整数类型,可以在不丢失数据的情况下与指针进行转换,但我为什么要这样做呢?与void* 用于保存指针和THE_REAL_TYPE* 用于指针运算相比,整数类型有什么优势?

编辑
标记为“已经被问到”的问题没有回答这个问题。问题是如果使用intptr_t 作为void* 的一般替代品是一个好主意,而且那里的答案似乎是“不要使用 intptr_t”,所以我的问题仍然有效:What would be a good use intptr_t 的情况?

【问题讨论】:

  • 尝试编写没有安全类型的操作系统的内存管理代码。
  • 根据我的经验,它的主要用途是整理遗留代码,假设将 int 转换为 void * 并稍后再返回是一种有效的技术
  • 我发现它在这里很有用:stackoverflow.com/questions/6326534/…
  • @Baruch C 和 C++ 是 2 种不同的语言并且不兼容。当你不询问差异、相似之处、extern "C" 或类似的东西时,不要同时使用这两个标签。 intptr_t 的我们可能在两种语言中都不相同,如果您知道所有相关部分在两种语言中都是相同的,但事实并非如此,那么您就不必首先问这个问题。

标签: c++ c pointers


【解决方案1】:

uintptr_t 类型在编写内存管理代码时非常有用。这类代码希望通过通用指针 (void *) 与其客户对话,但在内部对地址进行各种运算。

你可以通过 char * 操作来做一些相同的事情,但不是所有的事情,结果看起来像前 Ansi C。

并非所有内存管理代码都使用uintptr_t - 例如,BSD 内核代码定义了具有相似属性的vm_offset_t。但是,如果您正在编写例如一个调试 malloc 包,为什么要发明自己的类型?

当您在printf 中有可用的%p 并且正在编写需要在各种架构上以十六进制打印指针大小的整数变量的代码时,这也很有帮助。

我发现intptr_t 的用处不大,除了可能作为转换时的中途站,以避免在同一转换中更改符号和整数大小的可怕警告。 (在所有相关架构上编写通过-Wall -Werror 的可移植代码可能有点困难。)

【讨论】:

    【解决方案2】:

    (u)intptr_t 用于对指针进行算术运算,特别是按位运算。但正如其他人所说,您几乎总是希望使用uintptr_t,因为按位运算最好在无符号中完成。但是,如果您需要算术右移,那么您必须使用intptr_t。通常用于在指针中存储数据,通常称为tagged pointer

    • 在 x86-64 中,您可以将高 16/7 位用于数据,但您必须手动对 make the pointer canonical 进行符号扩展,因为它目前没有忽略高位 like in ARM 的标志。因此,例如,如果您有 char* address,那么您需要在取消引用之前执行此操作

      char* pointer = (char*)((intptr_t)address << 16 >> 16);
      
    • 32 位 Chrome V8 引擎使用 smi (small integer) optimization 其中the low bit denotes the type

                  |----- 32 bits -----|
      Pointer:    |_____address_____w1| # Address to object, w = weak pointer
      Smi:        |___int31_value____0| # Small integer
      

      所以当指针的最低有效位为 0 时,它将被右移以检索原始的 31 位有符号整数

      int v = (intptr_t)address >> 1;
      

    更多信息请阅读


    另一种用法是将有符号整数作为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);
    }
    

    【讨论】:

    • 关于在X86_64中使用指针高位。不要做。在任何时候,物理架构都可以添加另一个位以使用更多 RAM,如果您的代码与之冲突,那是您的问题。我强烈希望操作系统对此毫无帮助。所有文档都提到了这一点。将这些位视为正在被使用。
    • @ZanLynx 64 位地址空间是如此巨大,以至于您几乎无法使用它(至少在个人 PC 中)。只需从高位到低位使用,在可预见的未来都是安全的。当然总是更喜欢使用低位,它们总是安全的
    【解决方案3】:

    还有语义方面的考虑。

    void* 应该指向某物。尽管具有现代实用性,但指针不是内存地址。好的,它通常/可能/总是(!)持有一个,但它不是一个数字。这是一个指针。它指的是一个东西。

    intptr_t 没有。它是一个整数值,可以安全地转换为指针或从指针转换,因此您可以将它用于古董 API,将其打包到 pthread 函数参数中,诸如此类。

    这就是为什么您可以在 intptr_t 上比在 void* 上做更多的琐碎事情,以及为什么您应该使用适合工作的类型来进行自我记录。

    最终,几乎所有可能都是整数(请记住,您的计算机可以处理数字!)。指针可能是整数。但他们不是。它们是指针,因为它们用于不同的用途。而且,理论上,它们可能不是数字。

    【讨论】:

    • 很棒的解释!
    【解决方案4】:

    intptr_t有什么用?

    使用示例:订单比较。
    比较指针是否相等不是问题。
    &gt;, &lt;= 等其他比较操作可能是 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);
    

    [以前的例子] 使用示例:测试指针是否属于指针数组。

    #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);
    }
    

    正如@M.M 所评论的,上述代码可能无法按预期工作。但至少它不是UB。 - 只是不可移植的功能。我希望用这个来解决this problem

    【讨论】:

    • @M.M 我拍了一张。什么可能不起作用? special[0]..special[N-1] 的序列可能与 intptr_t 版本不匹配?还是你看到另一个陷阱?
    • @M.M:为什么不能保证?我没有发现任何问题。
    • 是的,intptr_t 值不需要与被测试范围内的指针具有相同的顺序。尽管他们很有可能会这样做。
    • 如果比较来自不同内存分配的指针是 UB 并且不能保证工作,我不知道如何将它们转换为整数首先会使其工作得更好。
    • @M.M 除非你正在编写核导弹发射控制系统
    【解决方案5】:

    主要原因,您不能对 void * 执行按位运算,但您可以对 intptr_t 执行相同操作。

    在许多需要对地址执行按位运算的情况下,可以使用intptr_t

    但是,对于按位运算,最好的方法是使用 unsigned 对应的 uintptr_t

    正如@chuxother answer 中提到的,指针比较是另一个重要方面。

    另外,FWIW,按照C11 标准,§7.20.1.4,

    这些类型是可选的。

    【讨论】:

    • 对于按位运算,我更喜欢无符号类型,即std::uintptr_t。我必须承认,我仍然需要遇到签名版本的用例。
    • @5gon12eder:我完全同意,但我知道至少有一个平台使用了实际签名的内存空间。公认的是,Transputers 现在还不是很新,但 C 也针对更奇特的平台。仅供参考:这与指令集以及文字和指令的组成方式有关。非常有趣的主题,它表明曾经有非常有趣的架构超越当前非常无聊(来自指令集)的架构。
    • 指针类型不保证 +1 实际上将值增加 1。void * 没有 operator + 尽管 gcc 支持它作为扩展。即使sizeof(char)(按标准)和sizeof(void)(通过gcc扩展)保证等于1,将1加到char *void *仍然不能保证将它们的数值增加1:示例是非字节的可寻址机器(指针的低位始终为 0)。所以intptr_t 也让+/- 更安全。
    • @user3528438 为什么要将指针增加任何数字,而不是它所指向的对象大小的倍数?
    • @baruch 举个例子:您可能想要生成一个与int 正确对齐的指针,因此您可以采用其他指针并将其增加到sizeof(int) 的下一个倍数。 (或alignof(int)
    猜你喜欢
    • 2011-07-19
    • 1970-01-01
    • 2011-09-13
    • 2012-03-18
    • 1970-01-01
    • 1970-01-01
    • 2019-04-22
    • 2014-06-02
    • 1970-01-01
    相关资源
    最近更新 更多