0

It seems that I found a bug when print value of (0xffffffff + 1) with %llu is 0, see below code:

unsigned long long resTestBad = 0xffffffff + 1;  // number of f is 8
printf("resTestBad is %llu, sizeof(unsigned long long) is %ld\n", resTestBad, sizeof(unsigned long long));

will output:

resTestBad is 0, sizeof(unsigned long long) is 8

I think that output is wrong, it should be output or say correct output:

resTestBad is 4294967296, sizeof(unsigned long long) is 8

And

unsigned long long resA = 4294967295 + 1; 
printf("resA is %llu, sizeof(unsigned long long) is %zu\n", resA, sizeof(unsigned long long));

will output:

resA is 4294967296, sizeof(unsigned long long) is 8

But below code is no problem:

unsigned long long resTestAgain = 0xfffffffff + 1;   // number of f is 9
printf("resTestAgain is %llu, sizeof(unsigned long long) is %zu\n", resTestAgain, sizeof(unsigned long long));

It will output(I think it right):

resTestAgain is 68719476736, sizeof(unsigned long long) is 8

My .c file is below:

#include <stdio.h>
#include <limits.h>

int main(void)
{
    int s = 0xfffe;
    printf("s is %d\n", s);

    unsigned int ww = 0xfffe;
    printf("ww is %d\n", ww);

    unsigned long long resTestOk = 0xfffffffe + 1; 
    printf("resTestOk is %llu, sizeof(unsigned long long) is %zu\n", resTestOk, sizeof(unsigned long long));

    unsigned long long resTestBad = 0xffffffff + 1;  
    printf("resTestBad is %llu, sizeof(unsigned long long) is %zu\n", resTestBad, sizeof(unsigned long long));

    unsigned long long resTestAgain = 0xfffffffff + 1; 
    printf("resTestAgain is %llu, sizeof(unsigned long long) is %zu\n", resTestAgain, sizeof(unsigned long long));

    unsigned long long resA = 4294967295 + 1; 
    printf("resA is %llu, sizeof(unsigned long long) is %zu\n", resA, sizeof(unsigned long long));

    unsigned long long resTestUUU = 0xffffffffffffff + 1; 
    printf("resTestUUU is %llu, sizeof(unsigned long long) is %zu\n", resTestUUU, sizeof(unsigned long long));

    unsigned long long resH = (0xffffffff+1) / 1024 / 1024 / 1024;
    printf("%llu\n", resH);

    unsigned long long resD = (4294967296) / 1024 / 1024 / 1024;
    printf("%llu\n", resD);

    return 0;
}

Run it will output:

s is 65534
ww is 65534
resTestOk is 4294967295, sizeof(unsigned long long) is 8
resTestBad is 0, sizeof(unsigned long long) is 8
resTestAgain is 68719476736, sizeof(unsigned long long) is 8
resA is 4294967296, sizeof(unsigned long long) is 8
resTestUUU is 72057594037927936, sizeof(unsigned long long) is 8
0
4

May be you don't believe that, I also don't believe, so I record a video that for prove I said.

See https://imgur.com/a/RAQVPxS

Is it a Bug of CPU or bug of VMWare Workstation? How can I solve it?

Update

Sorry, may be my question is not clear, or say confusion for people.

Problem origin from that I want to test how many memory a 32-bit computer can access(I know it is 4GB, address from 0x00000000 to 0xffffffff, total number is 0xffffffff + 1, unit is bytes, so (0xffffffff + 1) / 1024 /1024 / 1024 = 4GB), so I use below code:

unsigned long long resH = (0xffffffff + 1) / 1024 / 1024 / 1024;
printf("%llu\n", resH);

But it output number 0, that is not what I want to get. I except output is 4. So I also use below code:

unsigned long long resQ = (4294967295 + 1) / 1024 / 1024 / 1024;
printf("%llu\n", resQ);

It output 4 that what is I except. So I want to find out what reason result to different of result.

Then I use below code:

unsigned long long resTestBad = 0xffffffff + 1; 
printf("resTestBad is %llu, sizeof(unsigned long long) is %zu\n", resTestBad, sizeof(unsigned long long));

it output:

resTestBad is 0, sizeof(unsigned long long) is 8

That not is my except, so I test below code:

unsigned long long resTestAgain = 0xfffffffff + 1; 
printf("resTestAgain is %llu, sizeof(unsigned long long) is %zu\n", resTestAgain, sizeof(unsigned long long));

it output:

resTestAgain is 68719476736, sizeof(unsigned long long) is 8

as below code:

unsigned long long resA = 4294967295 + 1; 
printf("resA is %llu, sizeof(unsigned long long) is %zu\n", resA, sizeof(unsigned long long));

it output:

resA is 4294967296, sizeof(unsigned long long) is 8

By see answer, now I understand why 0xffffffff + 1 output 0.

But now I can't understand that value size of 0xffffffff and 4294967295 is the same, why 0xffffffff as unsigned int type(occupy 32-bit), however 4294967295 may be as long or long long or other type(occupy 64-bit).

Tom
  • 417
  • 3
  • 10
  • 4
    `0xffffffffULL + 1ULL` – Ry- Aug 31 '23 at 10:54
  • @Ry- Must have a UL? Is it ok if no UL? – Tom Aug 31 '23 at 10:55
  • 3
    Sidenote: The correct format specifier for `size_t` which is result type of `sizeof` is `%zu` – Gerhardh Aug 31 '23 at 10:55
  • As you demonstrate in your code, the result is different. Is that OK for you or not? – Gerhardh Aug 31 '23 at 10:56
  • @Gerhardh But I use %llu, is it ok? – Tom Aug 31 '23 at 10:57
  • The type of `0xffffffff + 1;` is the type of `0xffffffff` or of `int` if `int` is "larger". It does not help if you assign the result to a `unsigned long long` or an `unsigned int`. Any overflow that already happens during calculation will not vanish after assigning – Gerhardh Aug 31 '23 at 10:57
  • https://stackoverflow.com/questions/42892227/c-literal-integer-type – Ry- Aug 31 '23 at 10:57
  • 1
    `unsigned long long resTestAgain = 0xfffffffff + 1; // number of f is 8` No.... count again – Support Ukraine Aug 31 '23 at 10:57
  • 3
    `%llu` is OK for `unsigned long long`. But `%ld` is not OK for `sizeof` – Gerhardh Aug 31 '23 at 10:57
  • 1
    What type is `0xffffffff` ? What type is `0xfffffffff` ? Once you answer that, you know what is going on – Support Ukraine Aug 31 '23 at 10:58
  • @Gerhardh My question is mainly about %llu for 0xffffffff + 1. – Tom Aug 31 '23 at 10:59
  • 1
    @Tom *But I use %llu, is it ok?* [No](https://port70.net/~nsz/c/c11/n1570.html#7.21.6.1p9): "If a conversion specification is invalid, the behavior is undefined. If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined." – Andrew Henle Aug 31 '23 at 11:00
  • @SupportUkraine `0xffffffff` and `0xfffffffff` all are unsigned long long type. – Tom Aug 31 '23 at 11:01
  • @Tom *0xffffffff and 0xfffffffff all are unsigned long long type* Why do you think those integer literals are `unsigned long long` types? – Andrew Henle Aug 31 '23 at 11:01
  • @Tom Nope.... That's your mistake. They are different types. Adding `1` to `0xffffffff` results in zero – Support Ukraine Aug 31 '23 at 11:01
  • 1
    If you wrote `float f = 0xffffffff;` would that make `0xffffffff` a `float`? – Andrew Henle Aug 31 '23 at 11:02
  • @AndrewHenle The `float` example is good. Nice. – Support Ukraine Aug 31 '23 at 11:03
  • @SupportUkraine "Adding 1 to 0xffffffff results in zero" but 0xffffffff is unsigned long long that occupy 64-bits. – Tom Aug 31 '23 at 11:05
  • @Tom Nope... With 8 f like `0xffffffff` you don't get 64 bits. 32 bits will do. Then adding 1 leads to .... tada, zero – Support Ukraine Aug 31 '23 at 11:06
  • @AndrewHenle "would that make 0xffffffff a float", print with %f will display float number. – Tom Aug 31 '23 at 11:06
  • @SupportUkraine "With 8 f like 0xffffffff you don't get 64 bits", but I define With 8 f like 0xffffffff as unsigned long long. – Tom Aug 31 '23 at 11:07
  • 3
    @Tom It doesn't matter at all what type you write on the Left-Hand-Side. The types used for the Right-Hand-Side is completely independent of Left-Hand-Side. It's only when the assignment/initialization is done i.e. the `=` that the Left-Hand-Side type matters. And in your case that's too late... The Right-Hand-Side have already reached the result zero. – Support Ukraine Aug 31 '23 at 11:11
  • @tom https://stackoverflow.com/questions/1277799/type-selection-for-literal-numeric-values-in-c – Support Ukraine Aug 31 '23 at 11:14
  • @Tom *print with %f will display float number.* You might as well have said, "My pony has 4 hooves". That would be as applicable to your problem right now, because `printf()`'s `%f` format specifier literally has nothing to do with what type of value the integer literal `0xffffffff` is. You need to think about **what `0xffffffff` IS**. And **only** `0xffffffff`. – Andrew Henle Aug 31 '23 at 11:26
  • @AndrewHenle  Why With 9 f like 0xfffffffff + 1 is not overflow? – Tom Aug 31 '23 at 11:30
  • @Tom Print these: `sizeof(0xffffffff)`, `sizeof(0xfffffffff)`, `sizeof(0xffffffff + 1)`, `sizeof(0xfffffffff + 1)` – VLL Aug 31 '23 at 11:36
  • @VLL ok. I try it. – Tom Aug 31 '23 at 11:37
  • @VLL print 4 8 4 8, it look that I declare `unsigned long long` that is usefulless. – Tom Aug 31 '23 at 11:44
  • "My question is mainly about %llu for 0xffffffff + 1" You didn't get it. You do not pass `0xffffffff + 1` to `printf`. You pass a `unsigned long long`. For that `%llu` is OK. But `0xffffffff + 1` isn't an `unsigned long long` unless you add `ULL` suffix. – Gerhardh Sep 01 '23 at 11:22
  • @Gerhardh Thank you for you replay. `0xffffffff + 1 isn't an unsigned long long unless you add ULL suffix or force convert (unsigned long long) 0xffffffff + 1` , that I learned at long before, then long time don't use relative it, so I forget it. At at begin my problem is that I want to print (0xffffffff+1) / 1024 / 1024 / 1024 that output is 0, but (4294967295 + 1) / 1024 / 1024 / 1024 output 4. – Tom Sep 01 '23 at 11:28

4 Answers4

3

As explained in @rici’s answer to another question (about C++, but the same rule applies to C), unsigned types are candidates for hex integer literals. 0xffffffff fits in a 32-bit unsigned int or unsigned long, so one of those is its type. Then the int 1 is converted to unsigned long/int for the addition, which overflows. Finally, the unsigned long/int 0 is converted to unsigned long long to become the initial value for resTestBad.

Use 0xffffffffULL to force the literal to be unsigned long long from the get go.

(When you add another f, the value no longer fits in an unsigned long, so the literal already has type long long and it works.)

Ry-
  • 218,210
  • 55
  • 464
  • 476
  • I declaration unsigned long long resTestBad = 0xffffffff + 1; that means occupy 64-bits, why 0xffffffff save into 32-bit? – Tom Aug 31 '23 at 11:23
  • @Tom *why 0xffffffff save into 32-bit?* Why do you think `0xffffffff` is a 64-bit value? Going back to `float`: if you **assign** `0xffffffff` to a `float`, does that make `0xffffffff` itself a `float`? – Andrew Henle Aug 31 '23 at 11:29
  • @AndrewHenle Because I declare 0xffffffff as unsigned long long. See unsigned long long resTestBad = 0xffffffff + 1; // number of f is 8 – Tom Aug 31 '23 at 11:39
3

This answer assumes int to be 32 bits (which is the most common size)

Is it a Bug of CPU or bug of VMWare Workstation?

No, it not a bug. It's how the C standard says it has to be done.

When doing

unsigned long long resTestBad = 0xffffffff + 1;

the first step is to calculate

0xffffffff + 1;

To do that the compiler needs to ensure the the operands at both side of + has the same type. So first it needs to figure out the types.

0xffffffff 

is in decimal the value 4294967295 which can't be stored in a 32 bit signed value. However, it can be stored in a 32 bit unsigned value, and was written in hex in the source. (As @Lundin's answer points out, 4294967295 in the source will only pick signed types, unlike hex literals not considering unsigned int as a possibility. So it will be a 64-bit signed type, either long or long long depending on whether long is 64 of 32-bit. You can check this yourself by printing sizeof(4294967295) vs. sizeof(0xffffffff).

1 has type int, which can be implicitly converted to unsigned int, so the compiler selects 32-bit unsigned for the calculation.

So the calculation is done as:

(unsigned int)0xffffffff + (unsigned int)1;

This gives (a well defined) overflow to the final result zero.

So

unsigned long long resTestBad = 0xffffffff + 1;

is really the same as

unsigned long long resTestBad = (unsigned int)0;

And notice, the type on the Left Hand Side of the = has absolutely no impact on the calculation of the Right Hand Side. No matter if you write unsigned long long resTestBad or float resTestBad or double resTestBad, the calculation on the Right Hand Side is the same. It will always be:

SomeType resTestBad = (unsigned int)0;

BTW: Try this:

float f = 5/2;

Will you get 2.5 because you have float on the Left Hand Side? No, the division is still an integer division so the result is (int)2.

When you use 9 f instead of 8 f, the value can no longer be saved in a 32 bit type so the compiler selects a 64 bit type for the calculation. Consequently, there won't be an overflow.

How can I solve it?

By forcing the Right Hand Side calculation to be done in 64 bit. For instance:

0xffffffffULL + 1
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • I see book name "The c programming language", you said "C standard", does it in that book? – Tom Aug 31 '23 at 11:47
  • The C standard is the document that describes how C works. It has all the rules that C compilers must follow. – Support Ukraine Aug 31 '23 at 11:50
  • @Tom Drafts of the standard can be found for free: https://www.iso-9899.info/n1570.html Read 6.4.4.1 and 6.3.1.8 – Support Ukraine Aug 31 '23 at 11:53
  • According to you answer, I find other solution: unsigned long long resTestBad = (unsigned long long)0xffffffff + 1; Thank you very much for you answer and link that you provided. – Tom Aug 31 '23 at 11:58
  • @Tom yep, you can use a cast like ` (unsigned long long)0xffffffff`. It will work.... but it's more common to use suffixs. – Support Ukraine Aug 31 '23 at 12:00
  • Why `unsigned long long resA = 4294967295 + 1;` output `resA is 4294967296`?4294967295 is 0xffffffff, as you said "(unsigned int)0xffffffff + (unsigned int)1;, This gives (a well defined) overflow to the final result zero.". I think it should be output 0. – Tom Aug 31 '23 at 13:03
  • Does result of (unsigned int)0xffffffff + (unsigned int)1 and 4294967295 + 1 different? – Tom Aug 31 '23 at 13:16
  • @Tom: As Lundin's answer explains, the way you write the number in the source determines whether `unsigned int` is a candidate before moving on to a larger signed type like `long`. Yes for hex like `0xffffffff`, but no for `4294967295`. https://godbolt.org/z/7sab5csrK) that shows how `4294967295` has a signed 64-bit type but `0xffffffff` has an unsigned 32-bit type, when compiled for x86-64. (I used C++ for its `auto` keyword to infer a variable's type from the right-hand side. C uses the same rules.) You should accept Lundin's answer which explains that it's not just the numeric value. – Peter Cordes Aug 31 '23 at 18:36
  • 1
    Or @SupportUkraine should fix this answer to avoid implying that `0xffffffff` and `4294967295` are the same thing as integer literal constants in C; they have different types. `sizeof(4294967295)` is 8, `sizeof(0xffffffff)` is 4, on mainstream implementations where `int` is 32-bit and the next larger integer size is 64-bit. Or I could edit... – Peter Cordes Aug 31 '23 at 18:38
  • @PeterCordes Before my question is not say clear, please see my updated answer. – Tom Aug 31 '23 at 22:52
  • @SupportUkraine Can you explain that value size of 0xffffffff and 4294967295 is the same, why 0xffffffff as unsigned int type(occupy 32-bit), however 4294967295 may be as long or long long or other type(occupy 64-bit)? I updated my answer. Please see my updated answer. – Tom Aug 31 '23 at 22:54
  • @Tom: That's just how C was designed, that base 10 constants in the source can only be signed types, but hex constants can also be unsigned if they fit. See Lundin's answer. As for the language-design reasons why, it's typical to use unsigned types when doing bit-manipulation stuff, and it's also typical to write constants in hex when you care about their bit-patterns more than their numeric value, so this rule is sometimes helpful. Early C might have gotten that rule before introduction of suffixes like `1U` vs. `1ULL` that let you specify the type and signedness of a literal constant. – Peter Cordes Aug 31 '23 at 23:26
  • @Tom: If C were being redesigned from scratch these days, I think most people would be in favour of a different design, because the current behaviour leads to non-intuitive results like you ran into where different ways of writing the same number have different behaviour. I'm not sure what Rust does; it generally requires you to be explicit about types, like if you had a u8 and an i16, you'd have to explicitly convert them if you wanted to add them. But IDK about literal constants. – Peter Cordes Aug 31 '23 at 23:31
  • @Tom: See [Why are decimal and hexadecimal integer literals treated differently?](https://stackoverflow.com/q/36560143) about the design decision. It's a C++ question, but since C++11 changed to match C99, the answer quotes the C99 committee's stated rationale, which is what I guessed, that hex constants are more often bit-patterns or masks, so shouldn't be widened just to avoid having the high bit set, thus they can choose from unsigned types as well. Also related: [What are integer literal types? And how are they stored?](//stackoverflow.com/q/41405578) re: `-` being a separate unary op. – Peter Cordes Aug 31 '23 at 23:35
  • 1
    @PeterCordes Thank you for your explain. That is to say, when using a number, it is best to specify its type, such as `(unsigned long long) 0xffffffff + 1` or `0xffffffffULL + 1` or `4294967295ULL + 1` or `(unsigned long long) 0xffffffff` or `0xffffffffULL` or `4294967295ULL` – Tom Aug 31 '23 at 23:55
3
  • Everything in C has a type, including these things: 1 which are known as integer constants.
  • Normally an integer constant has type int. Or unless it is too large to fit in an int, it has type long, or otherwise if still not large enough, long long. In all these cases it is a signed type.
  • Hex (and octal) integer constants are special. In case a hex integer constant can't fit in an int, it will become unsigned int, or if it can't fit in one either, then long, then unsigned long and so on.

So on a 32 bit int system, then 0xffffffff will get type unsigned int and 1 will get type int. Neither which is a type large enough to store the result of 0xffffffff + 1 so we already found the bug here.

Now as it happens, if you have one operand of type unsigned int and one of signed int, an implicit conversion (the usual arithmetic conversions) converts the signed type to unsigned. Details here: Implicit type promotion rules

Therefore both operands end up unsigned int and the result of the addition will also be unsigned int. The result of (unsigned int)0xffffffff + (unsigned int)1 is well-defined in C, on a 32 bit int system we will get a wrap-around and the result is 0.

What types you use in the rest of the program from there doesn't really matter because the result is already calculated.

Conclusion: which type that any operation in C is carried out as, has everything to do with the operands of the given operator and nothing to do with some left operand of assignment where the result will be stored later.

For the same reason as if you write down 1+1=2 on a paper and then store that paper in a folder, the equation on the paper does not magically change depending on what type of folder you store it in - it has already been carried out.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • In your answer "Hex (and octal) integer constants are special. In case a hex integer constant can't fit in an int, it will become unsigned int, or if it can't fit in one either, then long and so on." that I never heard. Thank you for you explain. As "So on a 32 bit int system", I am using 64-bits computer. – Tom Aug 31 '23 at 12:12
  • @Tom Yeah it should say "32 bit int system" which in the real world means any 32 or 64 bit CPU. 8 or 16 bit CPUs use 16 bit int. – Lundin Aug 31 '23 at 12:28
  • @Lundin I ever don't heard "8 or 16 bit CPUs use 16 bit int.". Thank you for your explain. – Tom Aug 31 '23 at 12:42
  • Does result of `(unsigned int)0xffffffff + (unsigned int)1` and `4294967295 + 1` different? – Tom Aug 31 '23 at 13:15
  • @Tom Yes, `4294967295` will result in a `long` or `long long` and the result will become `4294967296`. However, in case you write `2147483647 + 1` then both operands are `int` and you get an integer overflow bug, which is undefined behavior unlike unsigned wrap-around. – Lundin Aug 31 '23 at 14:06
  • 1
    Since I was curious (and since the accepted answer forgot to make the distinction between hex vs. decimal literals), I made a demo (https://godbolt.org/z/7sab5csrK) that shows how `4294967295` has a signed 64-bit type but `0xffffffff` has an unsigned 32-bit type, when compiled for x86-64. (I used C++ for its `auto` keyword to infer a variable's type from the right-hand side. C uses the same rules.) – Peter Cordes Aug 31 '23 at 18:32
  • @PeterCordes It's kind of a FAQ, I remember this curious question where someone had managed to write a rather surprising bug because of this: https://stackoverflow.com/q/34182672/584518. – Lundin Sep 01 '23 at 06:34
2

For starters it is much better to use the conversion specifier %zu to output values of the type size_t instead of %ld as you are doing:

 printf("resTestOk is %llu, sizeof(unsigned long long) is %ld\n", resTestOk, sizeof(unsigned long long));

Firstly though the type size_t in general represents an alias for the type unsigned long however according to the C Standard (7.19 Common definitions <stddef.h>)

4 The types used for size_t and ptrdiff_t should not have an integer conversion rank greater than that of signed long int unless the implementation supports objects large enough to make this necessary.

It seems in your system size_t is indeed an alias for the type unsigned long and outputted values can be represented in objects of the signed type long int.

Now let's consider for example this declaration:

unsigned long long resTestBad = 0xffffffff + 1; 

The unsigned hexadecimal constant 0xffffffff can be represented in an object of the type unsigned int. So it has the type unsigned int. Thus in this expression:

0xffffffff + 1

there is used the arithmetic for objects of the type unsigned int (the integer constant 1 is also converted to the type unsigned int due to usual arithmetic conversions).

As a result there is an overflow because the result can not be represented in an object of the type unsigned int. And the value of the expression is 0.

The same situation takes place in this code snippet:

unsigned long long resH = (0xffffffff+1) / 1024 / 1024 / 1024;
printf("%llu\n", resH);

where the expression 0xffffffff+1 of the type unsigned int produces an overflow for objects of that type and yields 0.

As for this declaration:

unsigned long long resTestAgain = 0xfffffffff + 1; 

then the hexadecimal constant 0xfffffffff can not be represented in an object of the type unsigned int (provided that sizeof( unsigned int ) is not greater than 4). If sizeof( long int ) is equal to 8 or if it is equal to 4 but sizeof( long long int ) equal to 8 (and in your program it indeed is equal to 8) then the constant can be represented in an object of the type either long int. or long long int.

So in this expression:

0xfffffffff + 1

there is used the arithmetic for objects of the type long int or long long int. And the result of the expression can be also represented in an object of that type.

As for this code snippet:

unsigned long long resA = 4294967295 + 1; 
printf("resA is %llu, sizeof(unsigned long long) is %ld\n", resA, sizeof(unsigned long long));

then the constant 4294967295 is greater than the value of INT_MAX equal to 2147483647. So it has type long int if sizeof( long int ) is greater than sizeof( int ) or long long int otherwise. And the result of the expression 4294967295 + 1 can be represented n object of that type as a positive value.

Pay attention to the following quote from the C Standard (6.4.4.1 Integer constants) relative to types of integer constants

5 The type of an integer constant is the first of the corresponding list in which its value can be represented.

                            Octal or Hexadecimal
Suffix     Decimal Constant            Constant
===============================================================
none             int                     int
                 long int                unsigned int
                 long long int           long int
                                         unsigned long int
                                         long long int
                                         unsigned long long int
================================================================
u or U           unsigned int            unsigned int
                 unsigned long int       unsigned long int
                 unsigned long long int  unsigned long long int
================================================================  
l or L           long int                long int
                 long long int           unsigned long int
                                         long long int
                                         unsigned long long int
================================================================
ll or LL         long long int           long long int
                                         unsigned long long int
================================================================  
Both u or U      unsigned long long int  unsigned long long int
and ll or LL
=================================================================
halfer
  • 19,824
  • 17
  • 99
  • 186
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • I declaration unsigned long long resTestBad = 0xffffffff + 1; that means occupy 64-bits, why 0xffffffff save into 32-bit? – Tom Aug 31 '23 at 11:23
  • @Tom Because the C standard says so. Read "6.4.4.1 Integer constants" of the standard. As I already told you: Whatever type you have on LHS means nothing to types on RHS. This is also why `float f = 5/2;` will **not** give you `2.5` – Support Ukraine Aug 31 '23 at 11:25
  • @SupportUkraine I see book named "The c programming language". – Tom Aug 31 '23 at 11:27
  • @SupportUkraine Why With 9 f like 0xfffffffff + 1 is not overflow? – Tom Aug 31 '23 at 11:31
  • @Tom Becaus 9 `f` can't be stored in 32 bit. So the compiler selects 64 bits instead. Consequently no overflow. – Support Ukraine Aug 31 '23 at 11:32
  • @SupportUkraine But I declare unsigned long long resTestBad = 0xffffffff + 1; // number of f is 8. Please unsigned long long. – Tom Aug 31 '23 at 11:40
  • 1
    @Tom I told you several time. It does **not** matter which type you write on the left side of the `=` See my answer. – Support Ukraine Aug 31 '23 at 11:45
  • @SupportUkraine I know, but it is different for my think. – Tom Aug 31 '23 at 11:46
  • @Tom In this expression 0xffffffff + 1 there is used the arithmetic for objects of the type unsigned int. And only the result is assigned to an object of the type unsigned long long int. – Vlad from Moscow Aug 31 '23 at 11:58
  • @VladfromMoscow I find other solution: unsigned long long resTestBad = (unsigned long long)0xffffffff + 1; – Tom Aug 31 '23 at 11:59
  • *type size_t in general represents an alias for the type unsigned long* - Maybe you mean "typically" or "often"? It's definitely not true *in general*, since there are mainstream counterexamples. Windows x64 is an LLP64 ABI so `unsigned long` is a 32-bit type, but it allows array sizes larger than 4GiB so `size_t` has to be `unsigned long long`, matching `uintptr_t`. Most non-Windows 64-bit systems use LP64 ABI, often derived from System V, so outside of Windows yeah `size_t` often `unsigned long`. Presumably some 16-bit systems have 16-bit `size_t` = `unsigned int`. – Peter Cordes Aug 31 '23 at 18:19
  • @PeterCordes In my answer there is written clear. And moreover there is provided a quote from the C Standard. What is unclear? – Vlad from Moscow Aug 31 '23 at 19:32
  • 1
    "Generally" can mean that something is true everywhere, or almost everywhere. That quote from the standard isn't just some obscure detail that doesn't come up on "normal" systems, it applies on a widely used platform, Windows x64. Like I said, "typically" or "often" would be better choices for that sentence, to avoid the implication that `size_t = unsigned long` is a good assumption. The OP is only printing small `size_t` values with `%ld`, so we can't tell whether it was looking at the low 32 bits of a register or the whole thing. I'd expect the same output in Windows x64 for example. – Peter Cordes Aug 31 '23 at 19:44
  • From the OP's screen-capture animation, we can see they're doing this inside an Ubuntu VM, so `size_t` will match `%ld`, whether that's 32 or 64-bit. But IDK if that was your basis for saying "it seems". – Peter Cordes Aug 31 '23 at 19:49
  • @PeterCordes Please reread my answer one more if one time is not enough for you. And pay attention to that size_t is an unsigned integer type. – Vlad from Moscow Aug 31 '23 at 20:03
  • Oh right, so `%ld` definitely doesn't actually match. It just happens to work for non-huge numbers where the same bits can be interpreted as signed positive. Anyway, regardless of what the rest of your answer says, I'm suggesting changing the word "generally", because that's something that's *not* generally true. Correcting a potentially-misleading statement later is not as good as avoiding giving some readers the wrong impression in the first place. Not everyone reads every word of every answer. I'm not saying your answer is *technically* wrong, I'm suggesting what I think is an improvement – Peter Cordes Aug 31 '23 at 20:10