In C, sizeof operator returns 8 bytes when passing 2.5m but 4 bytes when passing 1.25m * 2

CSizeof

C Problem Overview


I do not understand why the sizeof operator is producing the following results:

sizeof( 2500000000 ) // => 8 (8 bytes).

... it returns 8, and when I do the following:

sizeof( 1250000000 * 2 ) // => 4 (4 bytes).

... it returns 4, rather than 8 (which is what I expected). Can someone clarify how sizeof determines the size of an expression (or data type) and why in my specific case this is occurring?

My best guess is that the sizeof operator is a compile-time operator.

Bounty Question: Is there a run time operator that can evaluate these expressions and produce my expected output (without casting)?

C Solutions


Solution 1 - C

2500000000 doesn't fit in an int, so the compiler correctly interprets it as a long (or long long, or a type where it fits). 1250000000 does, and so does 2. The parameter to sizeof isn't evaluated, so the compiler can't possibly know that the multiplication doesn't fit in an int, and so returns the size of an int.

Also, even if the parameter was evaluated, you'd likely get an overflow (and undefined behavior), but probably still resulting in 4.

Here:

#include <iostream>
int main()
{
    long long x = 1250000000 * 2;
    std::cout << x;
}

can you guess the output? If you think it's 2500000000, you'd be wrong. The type of the expression 1250000000 * 2 is int, because the operands are int and int and multiplication isn't automagically promoted to a larger data type if it doesn't fit.

http://ideone.com/4Adf97

So here, gcc says it's -1794967296, but it's undefined behavior, so that could be any number. This number does fit into an int.

In addition, if you cast one of the operands to the expected type (much like you cast integers when dividing if you're looking for a non-integer result), you'll see this working:

#include <iostream>
int main()
{
    long long x = (long long)1250000000 * 2;
    std::cout << x;
}

yields the correct 2500000000.

Solution 2 - C

[Edit: I did not notice, initially, that this was posted as both C and C++. I'm answering only with respect to C.]

Answering your followup question, "Is there anyway to determine the amount of memory allocated to an expression or variable at run time?": well, not exactly. The problem is that this is not a very well formed question.

"Expressions", in C-the-language (as opposed to some specific implementation), don't actually use any memory. (Specific implementations need some code and/or data memory to hold calculations, depending on how many results will fit into CPU registers and so on.) If an expression result is not stashed away in a variable, it simply vanishes (and the compiler can often omit the run-time code to calculate the never-saved result). The language doesn't give you a way to ask about something it doesn't assume exists, i.e., storage space for expressions.

Variables, on the other hand, do occupy storage (memory). The declaration for a variable tells the compiler how much storage to set aside. Except for C99's Variable Length Arrays, though, the storage required is determined purely at compile time, not at run time. This is why sizeof x is generally a constant-expression: the compiler can (and in fact must) determine the value of sizeof x at compile time.

C99's VLAs are a special exception to the rule:

void f(int n) {
    char buf[n];
    ...
}

The storage required for buf is not (in general) something the compiler can find at compile time, so sizeof buf is not a compile-time constant. In this case, buf actually is allocated at run time and its size is only determined then. So sizeof buf is a runtime-computed expression.

For most cases, though, everything is sized up front, at compile time, and if an expression overflows at run-time, the behavior is undefined, implementation-defined, or well-defined depending on the type. Signed integer overflow, as in 2.5 billion multiplied by 2, when INT_MAX is just a little over 2.7 billion, results in "undefined behavior". Unsigned integers do modular arithmetic and thus allow you to calculate in GF(2k).

If you want to make sure some calculation cannot overflow, that's something you have to calculate yourself, at run time. This is a big part of what makes multiprecision libraries (like gmp) hard to write in C—it's usually a lot easier, as well as faster, to code big parts of that in assembly and take advantage of known properties of the CPU (like overflow flags, or double-wide result-register-pairs).

Solution 3 - C

Luchian answered it already. Just for complete it..

C11 Standard states (C++ standard has similar lines) that the type of an integer literal with no suffix to designating the type is dertermined as follows:

From 6.4.4 Constants (C11 draft):

> Semantics > > 4 The value of a decimal constant is computed base 10; that of an > octal constant, base 8; that of a hexadecimal constant, base 16. The > lexically first digit is the most significant. > 5 The type of an integer constant is the first of the corresponding list in which its value can be represented.

And the table is as follows:

Decimal Constant

int
int long int 
long long int

Octal or Hexadecimal Constant

int
unsigned int
long int
unsigned long int
long long int
unsigned long long int

For Octal and Hexadecimal constants, even unsigned types are possible. So depending on your platform whichever in the above list (int or long int or long long int) fits first (in the order) will be the type of integer literal.

Solution 4 - C

Another way to put the answer is to say that what is relevant to sizeof is not the value of the expression but it's type. sizeof returns the memory size for a type that can be provided either explicitely as a type or as an expression. In this case the compiler will compute this type at compile time without actually computing the expression (following known rules, for instance if you call a function, the resulting type is the type of the returned value).

As other poster stated there is an exception for variable length array (whose type size is only known at run time).

In other word you usually write things like sizeof(type) or sizeof expression where expression is an L-Value. Expression is almost never a complex computing (like the stupid example of calling a function above) : it would be useless anyway as it is not evaluated.

#include <stdio.h>

int main(){
    struct Stype {
            int a;
    } svar;
    printf("size=%d\n", sizeof(struct Stype));
    printf("size=%d\n", sizeof svar);
    printf("size=%d\n", sizeof svar.a);
    printf("size=%d\n", sizeof(int));

}

Also notice that as sizeof is a language keyword, not a function parenthesis are not necessary before the trailing expression (we have the same kind of rule for return keyword).

Solution 5 - C

For your follow-up question, there's no "operator", and there's no difference between the "compile time" size of an expression, and the "run time" size.

If you want to know if a given type can hold the result you're looking for, you can always try something like this:

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

int main(void) {
    int a = 1250000000;
    int b = 2;

    if ( (INT_MAX / (double) b) > a ) {
        printf("int is big enough for %d * %d\n", a, b);
    } else {
        printf("int is not big enough for %d * %d\n", a, b);
    }

    if ( (LONG_MAX / (double) b) > a ) {
        printf("long is big enough for %d * %d\n", a, b);
    } else {
        printf("long is not big enough for %d * %d\n", a, b);
    }

    return 0;
}

and a (slightly) more general solution, just for larks:

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

/* 'gssim' is 'get size of signed integral multiplication */

size_t gssim(long long a, long long b);
int same_sign(long long a, long long b);

int main(void) {
    printf("size required for 127 * 1 is %zu\n", gssim(127, 1));
    printf("size required for 128 * 1 is %zu\n", gssim(128, 1));
    printf("size required for 129 * 1 is %zu\n", gssim(129, 1));
    printf("size required for 127 * -1 is %zu\n", gssim(127, -1));
    printf("size required for 128 * -1 is %zu\n", gssim(128, -1));
    printf("size required for 129 * -1 is %zu\n", gssim(129, -1));
    printf("size required for 32766 * 1 is %zu\n", gssim(32766, 1));
    printf("size required for 32767 * 1 is %zu\n", gssim(32767, 1));
    printf("size required for 32768 * 1 is %zu\n", gssim(32768, 1));
    printf("size required for -32767 * 1 is %zu\n", gssim(-32767, 1));
    printf("size required for -32768 * 1 is %zu\n", gssim(-32768, 1));
    printf("size required for -32769 * 1 is %zu\n", gssim(-32769, 1));
    printf("size required for 1000000000 * 2 is %zu\n", gssim(1000000000, 2));
    printf("size required for 1250000000 * 2 is %zu\n", gssim(1250000000, 2));

    return 0;
}

size_t gssim(long long a, long long b) {
    size_t ret_size;
    if ( same_sign(a, b) ) {
        if ( (CHAR_MAX / (long double) b) >= a ) {
            ret_size = 1;
        } else if ( (SHRT_MAX / (long double) b) >= a ) {
            ret_size = sizeof(short);
        } else if ( (INT_MAX / (long double) b) >= a ) {
            ret_size = sizeof(int);
        } else if ( (LONG_MAX / (long double) b) >= a ) {
            ret_size = sizeof(long);
        } else if ( (LLONG_MAX / (long double) b) >= a ) {
            ret_size = sizeof(long long);
        } else {
            ret_size = 0;
        }
    } else {
        if ( (SCHAR_MIN / (long double) llabs(b)) <= -llabs(a) ) {
            ret_size = 1;
        } else if ( (SHRT_MIN / (long double) llabs(b)) <= -llabs(a) ) {
            ret_size = sizeof(short);
        } else if ( (INT_MIN / (long double) llabs(b)) <= -llabs(a) ) {
            ret_size = sizeof(int);
        } else if ( (LONG_MIN / (long double) llabs(b)) <= -llabs(a) ) {
            ret_size = sizeof(long);
        } else if ( (LLONG_MIN / (long double) llabs(b)) <= -llabs(a) ) {
            ret_size = sizeof(long long);
        } else {
            ret_size = 0;
        }
    }
    return ret_size;
}

int same_sign(long long a, long long b) {
    if ( (a >= 0 && b >= 0) || (a <= 0 && b <= 0) ) {
        return 1;
    } else {
        return 0;
    }
}

which, on my system, outputs:

size required for 127 * 1 is 1
size required for 128 * 1 is 2
size required for 129 * 1 is 2
size required for 127 * -1 is 1
size required for 128 * -1 is 1
size required for 129 * -1 is 2
size required for 32766 * 1 is 2
size required for 32767 * 1 is 2
size required for 32768 * 1 is 4
size required for -32767 * 1 is 2
size required for -32768 * 1 is 2
size required for -32769 * 1 is 4
size required for 1000000000 * 2 is 4
size required for 1250000000 * 2 is 8

Solution 6 - C

Yes, sizeof() doesn't calculate the memory required for the result of that multiplication.

In the second case both literals : 1250000000 and 2 each requires 4 bytes of memory, hence sizeof() returns 4. If one of the values was above 4294967295 (2^32 - 1), you would have got 8.

But i don't know how sizeof() returned 8 for 2500000000. It returns 4 on my VS2012 compiler

Solution 7 - C

The C11 Draft is here: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf You can find the Cx0 draft here: http://c0x.coding-guidelines.com/6.5.3.4.html

In both cases, section 6.5.3.4 is what you are looking for. Basically, your problem boils down to this:

// Example 1:
long long x = 2500000000;
int size = sizeof(x); // returns 8

// Example 2:
int x = 1250000000;
int y = 2;
int size = sizeof(x * y); // returns 4

In example 1, you have a long long (8 bytes), so it returns 8. In example 2, you have an int * int which returns an int, which is 4 bytes (so it returns 4).

To answer your bounty question: Yes and no. sizeof will not calculate the size needed for the operation you are trying to perform, but it will tell you the size of the results if you perform the operation with the proper labels:

long long x = 1250000000;
int y = 2;
int size = sizeof(x * y); // returns 8

// Alternatively
int size = sizeof(1250000000LL * 2); // returns 8

You have to tell it you are dealing with a large number or it will assume it is dealing with the smallest type it can (which in this case is int).

Solution 8 - C

The most simple answer in one line is:

sizeof() is a function evaluated at COMPILE TIME who's input is a c type, the value of which is completely ignored

FURTHER DETAIL: ..therefore as 2500000000 is compiled it would have to be stored as a LONG as it is too long to fit in an int, therefore this argument is simply compiled as '(type) long'. However, 1250000000 and 2 both fit in type 'int' therefore that is the type passed to sizeof, since the resulting value is never stored as because the compiler simply is interested in the type, the multiplication is never evaluated.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJacob PollackView Question on Stackoverflow
Solution 1 - CLuchian GrigoreView Answer on Stackoverflow
Solution 2 - CtorekView Answer on Stackoverflow
Solution 3 - CP.PView Answer on Stackoverflow
Solution 4 - CkrissView Answer on Stackoverflow
Solution 5 - CCrowmanView Answer on Stackoverflow
Solution 6 - CAravindView Answer on Stackoverflow
Solution 7 - CZac HowlandView Answer on Stackoverflow
Solution 8 - Csbail95View Answer on Stackoverflow