Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c

sizeof

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)?

like image 556
Jacob Pollack Avatar asked May 30 '13 04:05

Jacob Pollack


People also ask

What does sizeof operator return in C?

It returns the size of a variable. It can be applied to any data type, float type, pointer type variables. When sizeof() is used with the data types, it simply returns the amount of memory allocated to that data type.

Does sizeof in C return bytes?

sizeof always returns size as the number of bytes. But according to wikipedia: In the programming languages C and C++, the unary operator sizeof is used to calculate the size of any datatype, measured in the number of bytes required to represent the type.

Why sizeof (' a ') is 4 in C?

'a' by default is an integer and because of that you get size of int in your machine 4 bytes.

Why does sizeof return 4?

Value 100 in the square brackets indicates to the programmer that he is working with an array of 100 items. But it is not an array of a hundred items which is passed into the function - it is only the pointer. So, the sizeof(B) expression will return value 4 or 8 (the size of the pointer in a 32-bit/64-bit system).


2 Answers

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.

like image 128
Luchian Grigore Avatar answered Sep 18 '22 16:09

Luchian Grigore


[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).

like image 44
torek Avatar answered Sep 18 '22 16:09

torek