1

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. Not sure, what I am missing.

/*
C code : Temp.c
*/
#include "stdio.h"
#include <stdint.h>
int main() {
    uint64_t a64 = 0x100000000UL;
    printf("Size of a64 = %0d.\n", sizeof(a64));
    printf("a64 = %16.16x. \n", a64);
    return 0;
}
/*
//Output
Size of a64 = 8.
a64 = 0000000000000000.
*/

I tried to compile code using the blow options, but output remained the same.

  1. gcc Temp.c
  2. gcc -std=c99 Temp.c
  3. gcc -m64 Temp.c

I also checked the gcc version, it is 64-bit. Not sure what is wrong.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
G Dave
  • 19
  • 1
  • 2
    https://stackoverflow.com/questions/2844/how-do-you-format-an-unsigned-long-long-int-using-printf – Mat Jun 30 '23 at 09:13
  • 1
    Have a look at [this answer](https://stackoverflow.com/a/9225648/3568358). – panic Jun 30 '23 at 09:21

2 Answers2

3
  • The integer constant 0x100000000UL has type unsigned long which is not guaranteed to be the same size as uint64_t.
  • %x in printf assumes that you pass an unsigned int so that's almost certainly a bug.
  • Use %zu when printing parameters of type size_t, such as sizeof(x).

For truly portable code, you need to use more portable features:

  • UINT64_C is a macro in stdint.h which can be used to get a correct integer constant of the type uint_least64_t.
  • The inttypes.h header provides portable conversion specifiers for printf/scanf, to use when printing the stdint.h types.

Example:

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

int main() {
    uint64_t a64 = UINT64_C(0x100000000);
    printf("Size of a64 = %0zu.\n", sizeof(a64));
    printf("a64 = %16.16" PRIx64 ". \n", a64);
    return 0;
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
1

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.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335