【问题标题】:error: cast from 'void*' to 'int' loses precision错误:从 'void*' 转换为 'int' 失去精度
【发布时间】:2010-12-11 01:22:45
【问题描述】:

我有一个带有原型void* myFcn(void* arg) 的函数,它用作pthread 的起点。我需要将参数转换为 int 以供以后使用:

int x = (int)arg;

编译器(GCC 版本 4.2.4)返回错误:

file.cpp:233: error: cast from 'void*' to 'int' loses precision

投射这个的正确方法是什么?

【问题讨论】:

  • 您使用的是 64 位系统吗?
  • 你应该做 int x = *((int *)arg);您正在从 void * 转换为 int 这就是您收到警告的原因
  • 我无法推翻我对 user384706 答案的支持,但这是错误的。 x = *((int *)arg); 是您如何在/void* 指向的位置获得int。它不会将void* 本身重新解释为int
  • int *x = (int *)arg; // 值为 *x

标签: c++ casting void-pointers


【解决方案1】:

您应该转换为size_t,而不是使用long

int val= (int)((size_t)arg);

【讨论】:

    【解决方案2】:

    我也遇到这个问题。

    ids[i] = (int) arg; // error occur here => I change this to below.
    
    ids[i] = (uintptr_t) arg;
    

    然后我可以继续编译。也许你也可以试试这个。

    【讨论】:

      【解决方案3】:

      没有“正确”的方式将 64 位指针存储在 32 位整数中。问题不在于强制转换,而在于目标类型丢失了一半的指针。存储在int 中的剩余 32 位不足以重建指向线程函数的指针。大多数答案只是试图从论点中提取 32 个无用的位。

      正如Ferruccio 所说,int 必须替换为intptr_t 才能使程序有意义。

      【讨论】:

        【解决方案4】:

        函数指针与 void*(以及任何其他非函数指针)不兼容

        【讨论】:

          【解决方案5】:

          我会创建一个结构并将其作为 void* 传递给 pthread_create

          struct threadArg {
              int intData;
              long longData;
              etc...
          };
          
          
          threadArg thrArg;
          thrArg.intData = 4;
          ...
          pthread_create(&thread, NULL, myFcn, (void*)(threadArg*)&thrArg);
          
          
          void* myFcn(void* arg)
          {
              threadArg* pThrArg = (threadArg*)arg;
              int computeSomething = pThrArg->intData;
              ...
          }
          

          请记住,thrArg 应该存在直到 myFcn() 使用它。

          【讨论】:

            【解决方案6】:

            在我的例子中,我使用了一个 32 位值,它需要作为 void * 传递给 OpenGL 函数,表示缓冲区的偏移量。

            您不能只将 32 位变量转换为指针,因为在 64 位机器上该指针的长度是原来的两倍。一半的指针将是垃圾。你需要传递一个实际的指针。

            这将为您提供一个 32 位偏移量的指针:

            int32 nOffset   = 762;       // random offset
            char * pOffset  = NULL;      // pointer to hold offset
            pOffset         += nOffset;  // this will now hold the value of 762 correctly
            glVertexAttribPointer(binding, nStep, glType, glTrueFalse, VertSize(), pOffset);
            

            【讨论】:

              【解决方案7】:

              您可以将其转换为 intptr_t 类型。这是一个int 类型,保证足够大以包含一个指针。使用#include <cstdint> 来定义它。

              【讨论】:

              • 别忘了#include <stdint.h> 获取intptr_t typedef。
              • #include <inttypes.h>。它包括 stdint.h 。
              • 如果你真的需要int(不仅仅是一个整数类型),你可以做一些简单的边界检查,然后将intptr_t向下转换为int
              【解决方案8】:

              在一般情况下,没有适当的方法将其转换为 int。 C99 标准库提供了intptr_tuintptr_t 类型定义,它们应该在需要执行这种转换时使用。如果您的标准库(即使它不是 C99)碰巧提供了这些类型 - 请使用它们。如果没有,请检查您平台上的指针大小,并自行定义这些 typedef 并使用它们。

              【讨论】:

                【解决方案9】:

                最安全的方法:

                static_cast<int>(reinterpret_cast<long>(void * your_variable));
                

                long 保证任何机器上 Linux 上的指针大小。 Windows 也只有 64 位的 32 位。因此,对于 64 位,您需要将其更改为 long long 而不是 Windows 中的 long。 所以reinterpret_cast 已将其转换为long 类型,然后static_cast 安全地将long 转换为int,如果您准备好截断数据。

                【讨论】:

                  【解决方案10】:

                  不要将您的 int 作为void* 传递,将int* 传递给您的int,这样您就可以将void* 转换为int* 并将取消引用的指针复制到您的int

                  int x = *static_cast<int*>(arg);
                  

                  【讨论】:

                  • 如果你这样做,你的线程引用了一个(希望仍然)存在于其他线程中的值。这可能会造成各种麻烦。
                  • pthread 将参数作为 void* 传递。这是一个旧的 C 回调机制,因此您无法更改它。而且您不能从另一个线程传递指向基于堆栈的对象的指针,因为它可能不再有效。
                  • 有一些方法可以防止这种情况:如果传递的线程不是静态的或者你的参数是局部变量,则传递动态分配的参数,否则没有问题。
                  • 我同意传递动态分配的指针很好(我认为最好的方法)。但是您的回答似乎暗示用户可以将 5 传递给 pthread_create ,然后执行上述强制转换以将其取回。
                  【解决方案11】:

                  代替:

                  int x = (int)arg;
                  

                  使用:

                  int x = (long)arg;
                  

                  在大多数平台上,指针和 long 的大小相同,但 int 和指针在 64 位平台上通常大小不同。如果将 (void*) 转换为 (long) 不会丢失精度,那么通过将 (long) 分配给 (int),它会正确截断数字以适合。

                  【讨论】:

                  • 在 Windows 64 上无效 - long 仍然是 32 位,但指针是 64 位。
                  • 如果原始对象(被强制转换为 void*)是一个整数,这不是一个好主意。如果大小不同,那么字节序就会起作用。
                  • @Martin York:不,它不依赖于字节序。指针整数转换中的映射是实现定义的,但目的是如果指针类型足够大并且没有强制对齐(@9​​87654327@ 没有),那么往返转换整数到指针- to-integer 应该产生原始值。指针到整数到指针的往返行程相同。
                  【解决方案12】:

                  再一次,上面的所有答案都严重错失了重点。 OP 想要将指针值转换为 int 值,相反,大多数答案都试图错误地将 arg 点的内容转换为 int 值。而且,其中大多数甚至无法在 gcc4 上运行。

                  正确的答案是,如果不介意丢失数据精度,

                  int x = *((int*)(&arg));
                  

                  这适用于 GCC4。

                  最好的方法是,如果可以,不要进行这种转换,相反,如果必须为指针和 int 共享相同的内存地址(例如为了节省 RAM),请使用 union,并确保如果 mem仅当您知道最后一次将地址设置为 int 时,地址才会被视为 int。

                  【讨论】:

                  • 此方法不适用于 64 位 Big Endian 平台,因此不必要地破坏了可移植性。
                  • 这甚至不是“正确答案”。这根本不是转换。这是记忆的重新解释——一种完全不可接受的方式来做 OP 试图做的事情。
                  • 这完全是错误的。不知道它是如何积累 27 票的?! -1
                  • 呃。可怕的解决方案。错误的。这将返回指针的前 32 位,这可能是顶部或底部,具体取决于大端还是小端,如注释 #2 所述。在最好的情况下,这将给出与原始代码相同的结果,没有任何优势,但在最坏的情况下,它将以多种灾难性方式失败(类型双关语、字节顺序、效率较低等)
                  • 对于那些有兴趣的人。我个人赞成这个答案,因为它的第一行文字帮助我理解了这个奇怪的错误消息的原因以及我,可怜的白痴,在做什么:D
                  【解决方案13】:
                  //new_fd is a int
                  pthread_create(&threads[threads_in_use] , NULL, accept_request, (void*)((long)new_fd));
                  
                  //inside the method I then have
                  int client;
                  client = (long)new_fd;
                  

                  希望对你有帮助

                  【讨论】:

                    【解决方案14】:

                    如果你这样调用你的线程创建函数

                    pthread_create(&thread, NULL, myFcn, reinterpret_cast<void*>(5));
                    

                    然后到达myFcn 内部的void* 具有您放入其中的int 的值。所以你知道你可以像这样把它扔回去

                    int myData = reinterpret_cast<int>(arg);
                    

                    即使编译器不知道您只将myFcn 与整数一起传递给pthread_create

                    编辑

                    正如 Martin 所指出的,这假定 sizeof(void*)&gt;=sizeof(int)。如果您的代码有机会被移植到某些平台上,而这不存在,这将不起作用。

                    【讨论】:

                      【解决方案15】:

                      之所以这样做,是因为您将 64 位指针转换为 32 位整数,因此您丢失了信息。

                      您可以使用 64 位整数来代替,但是我通常使用具有正确原型的函数并转换函数类型: 例如。

                      void thread_func(int arg){
                      ...
                      }
                      

                      我这样创建线程:

                      pthread_create(&tid, NULL, (void*(*)(void*))thread_func, (void*)arg);
                      

                      【讨论】:

                      • 如果函数有正确的签名,你就不需要显式地转换它。
                      • 但是你需要在你的线程函数中转换你的参数,这是非常不安全的...... cf。这个问题
                      • 您认为通过在此处投射它可以避免问题!你只是通过欺骗编译器和 ABI 让它变得更大。
                      • 在函数内转换参数更安全。您在这里所做的是未定义的行为,以及非常实用的未定义行为。
                      • 这样我将 int 转换为 void*,这比将 void* 转换为 int 要好得多。我看不出有什么不好的事情发生。请告诉我...
                      【解决方案16】:

                      将指针转换为 void* 并返回是对 reinterpret_cast 的有效使用。所以你可以这样做:

                      pthread_create(&thread, NULL, myFcn, new int(5)); // implicit cast to void* from int*
                      

                      然后在myFcn中:

                      void* myFcn(void* arg)
                      {
                          int*  data = reinterpret_cast<int*>(arg);
                          int   x    = *data;
                          delete data;
                      

                      注意:正如 sbi 指出的那样,这需要更改 OP 调用以创建线程。

                      我要强调的是,当您从一个平台移动到另一个平台时,从 int 到指针再返回的转换可能会遇到问题。但是(在任何地方)都很好地支持将指针转换为 void* 并再次返回。

                      因此,动态生成指针并使用它可能不太容易出错。 使用后记得删除指针,以免泄漏。

                      【讨论】:

                      • 但这不一样。从这个问题我认为 OP 确实是pthread_create(&amp;thread, NULL, myFcn, 5),这会让你的想法失败。
                      • 我的意思是使用 new(5) 而不是 5 并在另一端适当地处理它会更安全。
                      • 我理解,但这会引入动态内存和丑陋的生命周期问题(一个线程分配的对象必须被其他线程释放)——所有这些都只是为了将 int 传递给线程。跨度>
                      • 呃。我强烈反对。将int 转换为void* 并返回只能在sizeof(int)&gt;sizeof(void*) 所在的平台上成为问题。我不确定标准是否允许这样的平台。
                      • 绝对不能保证 sizeof(int)
                      【解决方案17】:

                      你可能想要的是

                      int x = reinterpret_cast<int>(arg);
                      

                      这允许您将void * 重新解释为int

                      【讨论】:

                      • 注意:这仅适用于您首先将int 转换为void *
                      • @Artelius:大概这正是 Joshua 所做的:pthread_create(&amp;thread, NULL, myFcn, reinterpret_cast&lt;void*&gt;(5));
                      • C++ 重新解释强制转换不能解决问题。如果原始类型是 void *,则在 sizeof(void *) != sizeof(int) 的平台上转换为 int 可能会丢失日期(LP64 编程模型也是如此)。
                      • 嗯?只要sizeof(void *) &gt; sizeof(int),就没有问题,看你先从int投到void *再投回来。
                      【解决方案18】:

                      正确的方法是将其转换为另一个pointer type。将void* 转换为int 是一种不可移植的方式,它可能有效,也可能无效!如果需要保留返回的地址,保留为void*即可。

                      【讨论】:

                      • 关键是(可能)传递给线程的值是一个整数值,而不是真正的'void *'。
                      • 谢谢乔纳森,我正在考虑我在另一个线程中的答案:stackoverflow.com/questions/1593580/…
                      • AraK 是正确的,传递整数和指针不一定可以互换。他应该传递整数的地址,线程应该得到那个地址,static_cast 它到一个int*,然后得到值。这必须在整数超出范围之前完成。
                      猜你喜欢
                      • 1970-01-01
                      • 2022-12-31
                      • 2014-10-18
                      • 2019-10-23
                      • 1970-01-01
                      • 2021-01-31
                      • 2014-10-19
                      • 1970-01-01
                      相关资源
                      最近更新 更多