I have the following piece of simple C code where I simply try to
assign a 64-bit value to uint64_t. However, the assignment happens as
32-bit value only and the most significant 32-bit are always zero.
You are wrong. The initialization of the variable a64
is correct. The integer constant 0x100000000UL
has an appropriate unsigned integer type that allows to store such an unsigned value. Its type either unsigned long
(if objects of this type can accomodate such a value) or unsigned long long
.
For example in systems (as MS Visual Studio) where sizeof( unsigned long int )
is equal to 4
the type of the constant is unsigned long long int
While in systems where sizeof( unsigned long int )
is equal to 8
the type of the constant is unsigned long int
.
If the integer constant cannot be represented by any of the mentioned above types it may have an extended integer type, if the extended integer type can represent its value. And it is evident that in any case the constant may be represented in an object of the extended type uint64_t
. So there is neither lost of data. The declaration
uint64_t a64 = 0x100000000UL;
is correct and does not require any changes.
The problem with your program is that you are using incorrect conversion specifiers in calls of printf
.
For example to output a value of the type size_t
you need to use conversion specifier zu
instead of d
and to output a value of the type uint64_t
you need to use macros defined in header <inttypes.h>
.
Here is your updated program.
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main( void )
{
uint64_t a64 = 0x100000000UL;
printf( "Size of a64 = %zu.\n", sizeof( a64 ) );
printf( "a64 = %#" PRIx64 ".\n", a64 );
}
The program output is
Size of a64 = 8.
a64 = 0x100000000.
Edit: I think it will be reasonable to comment the incorrect answer of @Lundin
that was upvoted even two times while my answer was down-voted.:) Otherwise these estimations of the answers will only confuse readers of the question.
He wrote
The integer constant 0x100000000UL has type unsigned long which is not
guaranteed to be the same size as uint64_t.
That is entirely wrong. The type of the constant 0x100000000UL
is implementation defined as I pointed out in my answer. It can be either unsigned long int
provided that the constant can be represented in an object of that type or unsigned long long int
or even some extented integer type if neither type, unsigned long int
or unsigned long long int
, can represent the constant.
All this is described in the C Standard in the section 6.4.4.1 Integer constants.
5 The type of an integer constant is the first of the corresponding
list in which its value can be represented.
and for the suffixes "both u or U and l or L" the corresponding types for a hexadecimal constant are
unsigned long int
unsigned long long int
And below in the same section of the C Standard there is written:
6 If an integer constant cannot be represented by any type in its
list, it may have an extended integer type, if the extended integer
type can represent its value.
For example try the C++ demonstration program shown below. It is a C++ program because using C++ it is easy to determine the type of an expression.
#include <iostream>
#include <type_traits>
int main()
{
std::cout << "sizeof( unsigned long int ) = "
<< sizeof( unsigned long int ) << '\n';
std::cout << "sizeof( unsigned long long int ) = "
<< sizeof( unsigned long long int ) << '\n';
std::cout << std::boolalpha;
std::cout << "std::is_same_v<unsigned long int, decltype( 0x100000000UL )> is "
<< std::is_same_v<unsigned long int, decltype( 0x100000000UL )>
<< '\n';
std::cout << "std::is_same_v<unsigned long long int, decltype( 0x100000000UL )> is "
<< std::is_same_v<unsigned long long int, decltype( 0x100000000UL )>
<< '\n';
}
If to run the program using MS Visual Studio the output might look like
sizeof( unsigned long int ) = 4
sizeof( unsigned long long int ) = 8
std::is_same_v<unsigned long int, decltype( 0x100000000UL )> is false
std::is_same_v<unsigned long long int, decltype( 0x100000000UL )> is true
As it is seen in the environment of MS Visual Studio the constant has the type unsigned long long int
because the type unsigned long int
is unable to represent the constant.
He also wrote in his answer that it is not guaranteed that the size of the type of the constant will be the same as the size of the type uint64_t
. But it entirely does not matter. The constant in any case can be represented in the type uint64_t
.
So this declaration
uint64_t a64 = 0x100000000UL;
is valid and is not required to be changed though one could write for example like using only suffux U
(the compiler itself will correctly determine the type of the unsigned constant)
uint64_t a64 = 0x100000000U;
or
uint64_t a64 = 0x100000000ULL;
taking into account that if the constant in the used system has the type unsigned long int
then moreover it can be represented as having the type unsigned long long int
. What is important in this declaration is the type of the variable a64
that in any case can represent the constant.
Using the macro UINT64_C
in the declaration of the variable a64
as it is proposed by @Lundin
uint64_t a64 = UINT64_C(0x100000000);
only makes the code more complicated and confuses readers of the code. Using this macro would make sense if for example the constant needs to be outputted for example with function printf
as a value of the type uint64_t
. But in the shown program that is not required. There is outputted the variable a64
that already has the concrete type uint64_t
.