Reputation: 1698
I have the following c++ source test in MinGW in winx , g++ version is 4.8.1 : compiled : 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)) ;
}
The answer is :
(1000)(1001)(1002)(1003)
(1000)(0)(1001)(0)
Obviously , the atomic uint64_t is wrong , while atomic int is right !! But I don't know what cause this problem , what should I modify so that I can use atomic correctly ...thanks !!
Upvotes: 0
Views: 1190
Reputation: 88225
You are using incorrect formats for the line printing uint64_t
data. When I compile your code my compiler produces the following warnings:
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
Note: You can enable a similar format checking warning in GCC 4.8.1 using the flag -Wformat
, or better yet, -Wall
.
On my platform the types int
and unsigned long long
are not layout compatible and so when printf tries to access a vararg specified by %d
when the actual passed argument is uint64_t
the result will be undefined behavior.
The normal formatters for printf such as %d
and %llu
are used for built in types such as int
or unsigned long long
. The types in stdint.h are not built in and may correspond to different built-in types on different platforms, requiring different formatters on each platform.
For example int64_t
might be the same as int
on one platform and the same as long long
on a different platform. Since to use int
in printf you use the format specifier %d
and to use long long
you use the format specifier %lld
, you cannot write portable code using stdint types and the normal format specifiers.
Instead, the header inttypes.h is provided with macros that contain the correct format specifiers. The macro for uint64_t
is PRIu64. This macro will be defined to be whatever the correct format specifier is on your platform. Use it like this:
printf("(%" PRIu64 ")(%" PRIu64 ")(%" PRIu64 ")(%u)\n",u1,u2,u3,unsigned(uint64local1));
Make sure you get the spaces put in between the macro and the quoted string fragments, otherwise in C++11 the macros will not work correctly.
Here's a useful reference on the normal formatters: http://en.cppreference.com/w/cpp/io/c/fprintf
Here's a reference for the stdint.h types and formatters: http://en.cppreference.com/w/cpp/types/integer
Note: Using incorrect format specifiers with printf an easy mistake to make and leads to undefined behavior. One of the advantages of the iostream library is that this sort of mistake is impossible.
Upvotes: 3