c ++ 11中原子fetch_add中int64_t和int的奇怪行为

时间:2013-06-20 03:03:49

标签: c++11 atomic

我在winx中的MinGW中有以下c ++源代码测试,g ++版本是4.8.1: 编译:g ++ -std = c ++ 11 int64test.cpp -o int64test.exe

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sched.h>
#include <errno.h>
#include <string.h>
#include <errno.h>
#include <atomic>
#include <iostream> 
#include <cstdint>

using namespace std ;

int main(int argc, const char *argv[])
{
    atomic<unsigned int> uintlocal1(1000) ;
    unsigned int uint1,uint2,uint3 ;
    uint1 = uintlocal1.fetch_add(1) ;
    uint2 = uintlocal1.fetch_add(1) ;
    uint3 = uintlocal1.fetch_add(1) ;   
    printf("(%d)(%d)(%d)(%d)\n",uint1,uint2,uint3,unsigned(uintlocal1)) ;

    atomic<uint64_t> uint64local1(1000) ;
    uint64_t u1,u2,u3 ;
    u1 = uint64local1.fetch_add(1) ;
    u2 = uint64local1.fetch_add(1) ;
    u3 = uint64local1.fetch_add(1) ;    
    printf("(%d)(%d)(%d)(%d)\n",u1,u2,u3,unsigned(uint64local1)) ;
}

答案是:

(1000)(1001)(1002)(1003)
(1000)(0)(1001)(0)

显然,原子uint64_t是错误的,而原子int是对的!! 但我不知道是什么导致这个问题,我应该修改什么才能使我 可以正确使用原子...谢谢!!

1 个答案:

答案 0 :(得分:2)

您使用的行格式uint64_t数据格式不正确。当我编译你的代码时,我的编译器会产生以下警告:

main.cpp:18:33: warning: format specifies type 'int' but the argument has type 'uint64_t' (aka 'unsigned long long') [-Wformat]
    printf("(%d)(%d)(%d)(%d)\n",u1,u2,u3,unsigned(uint64local1)) ;
             ~~                 ^~
             %llu
main.cpp:18:36: warning: format specifies type 'int' but the argument has type 'uint64_t' (aka 'unsigned long long') [-Wformat]
    printf("(%d)(%d)(%d)(%d)\n",u1,u2,u3,unsigned(uint64local1)) ;
                 ~~                ^~
                 %llu
main.cpp:18:39: warning: format specifies type 'int' but the argument has type 'uint64_t' (aka 'unsigned long long') [-Wformat]
    printf("(%d)(%d)(%d)(%d)\n",u1,u2,u3,unsigned(uint64local1)) ;
                     ~~               ^~
                     %llu

注意:您可以使用标记-Wformat或更好的-Wall在GCC 4.8.1中启用类似的格式检查警告。


在我的平台上,intunsigned long long类型不兼容布局,因此当实际传递的参数为%d时,printf尝试访问由uint64_t指定的vararg结果将是未定义的行为。

printf的常规格式化程序(例如%d%llu)用于内置类型,例如intunsigned long long。 stdint.h中的类型不是内置的,可能对应不同平台上的不同内置类型,每个平台需要不同的格式化程序。

例如int64_t可能与一个平台上的int相同,而另一个平台上的long long则相同。由于在printf中使用int使用格式说明符%d并使用long long使用格式说明符%lld,因此无法使用stdint类型和正常编写可移植代码格式说明符。


相反,标头inttypes.h提供了包含正确格式说明符的宏。 uint64_t的宏是PRIu64。此宏将定义为平台上正确的格式说明符。像这样使用它:

printf("(%" PRIu64 ")(%" PRIu64 ")(%" PRIu64 ")(%u)\n",u1,u2,u3,unsigned(uint64local1));

确保在宏和引用的字符串片段之间放置空格,否则在C ++ 11中宏将无法正常工作。

以下是普通格式化程序的有用参考:http://en.cppreference.com/w/cpp/io/c/fprintf

以下是stdint.h类型和格式化程序的参考:http://en.cppreference.com/w/cpp/types/integer


注意:对printf使用不正确的格式说明符很容易造成错误并导致未定义的行为。 iostream库的一个优点是这种错误是不可能的。