Confusion about sizeof operator in C - c

I'm confused about sizeof operator in C.
#include <stdio.h>
int main(void)
{
char num1=1, num2=2, result;
result = num1 + num2;
printf("Size of result: %d \n",sizeof result);
printf("Size of result: %d \n",sizeof(num1+num2));
}
The results are 1 and 4 respectively. Why does this happen?

TL;DR answer:
sizeof result is same as sizeof(char).
sizeof(num1+ num2) is same as sizeof (int) why?
In your case, they produce 1 (guaranteed by standard) and 4 (may vary), respectively.
That said, sizeof produces a result of type size_t, so you should %zu format specifier to print the value.
Why:
First, for the addition operator +, quoting C11, chapter §6.5.6
If both operands have arithmetic type, the usual arithmetic conversions are performed on
them.
Regarding usual arithmetic conversions, §6.3.1.8/p1
[....] Otherwise, the integer promotions are performed on both operands.[...]
and then from §6.3.1.1,/p2,
If an int can represent all values of the original type (as restricted by the width, for a
bit-field), the value is converted to an int; otherwise, it is converted to an unsigned
int. These are called the integer promotions.
So, sizeof(num1+num2) is the same as sizeof(int).

result is of char type, therefore sizeof is giving 1 while num1+num2 promoted to int type and therefore it gives 4 (size of int).
Note that when an arithmetic operation is performed on a type smaller than that of int and all of it's value can be represented by int then result will be promoted to int type.

num1 + num2 is becoming integer and hence the output is 4 whereas result is char which outputs 1.
You can refer this article Integer Promotion:
If an int can represent all values of the original type, the value is
converted to an int; otherwise, it is converted to an unsigned int.
These are called the integer promotions. All other types are unchanged
by the integer promotions.

the size of one char is 1byte, a char can hold values up to 127(unsigned up to 255).
when you say something like (a + b) a temporary variable is created and used to add a to b, because a and b can hold only 127, they are promoted by the compiler to be an int, just to be sure.
which is logical because if a = 100 and b = 100, the user would like to see 200 when he adds them and not 73 (which is the result of an overflow).

Related

Applying "not" operator on long double in C

I have below C code in which I have applied a not operator on a long double variable:
#include <stdio.h>
int main()
{
long double a;
signed char b;
int arr[sizeof(!a+b)];
printf("\n%d",sizeof(arr));
return 0;
}
This code outputs 16. I have problem in understanding what happenes when we apply not operator on long double, as we have done with a.
Please help me to understand whats happeneing with this code.
Thank You!
From the C Standard (6.5.3.3 Unary arithmetic operators)
5 The result of the logical negation operator ! is 0 if the value of
its operand compares unequal to 0, 1 if the value of its operand
compares equal to 0. The result has type int. The expression !E is
equivalent to (0==E).
So in this expression
sizeof(!a+b)
the sub-expression !a has the type int.
There are used the integer promotions of the operand b to the type int in the expression!a + b because the rank of the type signed char is less than the rank of the type int and the type int can represent all values of the type signed char.
From the C Standard (6.3.1.1 Boolean, characters, and integers)
...If an int can represent all values of the original type (as restricted
by the width, for a bit-field), the value is converted to an int;
otherwise, it is converted to an unsigned int. These are called the
integer promotions. 58) All other types are unchanged by the integer
promotions.
So the full expression is equivalent to
sizeof( int )
If the sizeof( int ) is equal to 4 then you have an array declared like
int arr[4];
Its size is equal to 16 that is to 4 * sizeof( int ).

Why doesn't integer promotion happen in my case?

#include <stdio.h>
void main()
{
unsigned char a = 0xfb;
char b = 0xfb;
printf("case1 : %d, %d", a, b); // case1
char x=90, y=60, z=100, h;
h = x*y/z;
printf("\ncase2 : %d", h); // case2
int m = 32768, n = 65536, o = 2, i;
i = m*n/o;
printf("\ncase3 : %d", i); // case3
}
result
case1 : 251, -5
case2 : 54
case3 : -107341824
In case1, identifier b is compiled as -5, which is not syntax error thought char only accept -128~127 value. So, first question is identifier b is firstly saved as int data type by end of its translation?(When translation is end, b will be saved in char.)
In case2, x, y is promoted as int. So h has right result value. But in case3, m, n aren't promoted as unsigned int(maybe). Identifier I doesn't have ordinary value(2^30).
C99 says that
If an int can represent all values of the original type, the value is converted to an int;
otherwise, it is converted to an unsigned int.
Being based on C99, value of h is natural, but dealing with m*n/o is overflowed. It's not natural because it's opposed to C99. This is my second query.
In case1, identifier b is compiled as -5, which is not syntax error
thought char only accept -128~127 value. So, first question is
identifier b is firstly saved as int data type by end of its
translation?(When translation is end, b will be saved in char.)
In case 1, the initializer for variable b, the constant 0xfb, represents a value of type int with value 251 (decimal). In the C abstract machine model, this value is converted to b's type (char) when the initializer runs, which for a block-scope variable is when execution reaches that declaration (not during translation). If indeed the range of char in your implementation is -128 - 127 then you have signed chars that cannot represent the initializer value, resulting in implementation-defined behavior.
So, again referencing the abstract machine model, nothing is stored in b at the end of translation. It is a block-scope variable, so it does not exist until execution reaches its declaration. The translated program does need somehow to store b's initial value, but C does not specify in what form it should do so. At no time during translation or execution does b contain a value of type int, however.
When the arguments to the printf call are evaluated, b's value is read and then converted to int (because it is a variadic argument). Arguably, then, it is ok to use a %d field to print it, but if you want to be certain to print the pre-conversion value then you should really use %hhd instead (though in your case, that will almost certainly print the same result).
In case2, x, y is promoted as int. So h has right result value.
More specifically, in case 2, the values of x, y, and z are promoted to int during evaluation of the expression x*y/z, and each operation produces an int result. The multiplication does not overflow the chosen type, int, and the overall result is in the range of type char, so the conversion to char applied upon assignment to h is unremarkable.
But in
case3, m, n aren't promoted as unsigned int(maybe). Identifier I
doesn't have ordinary value(2^30).
In case 3, m, n, and o already have type int, so they are not promoted, and the arithmetic expressions compute results of the same type (int). The result of sub-expression m*n is not in the range of type int, so undefined behavior ensues, pursuant to paragraph 6.5/5 of the Standard:
If an exceptional condition occurs during the evaluation of an
expression (that is, if the result is not mathematically defined or
not in the range of representable values for its type), the behavior
is undefined.
It is true that
C99 [and C11] says that
If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int.
, but that is irrelevant here. It is part of the description of the of the "integer promotions", which apply to the operands of an expression, based on their types.
Being based on C99, value of h is natural, but dealing with m*n/o is
overflowed. It's not natural because it's opposed to C99. This is my
second query.
You seem to expect that the intermediate expression m*n will be evaluated to produce a result of type unsigned int, so that it does not overflow, but this is not supported by the standard. The usual arithmetic conversions, including the integer promotions, are based only on the characteristics of operand types, including their signedness and value ranges. Operators that are subject to the usual arithmetic conversions, including *, use them to determine a common type for all operands and the result.
Your m and n already being the same type, and that type being int, no conversions / promotions apply. The result of the multiplication would also be an int if m and n's values were not such that their product (as an int) is undefined. In fact, however, the operation overflows type int, yielding undefined behavior.
In case 1, initializing a char, which is signed on your platform, with the value 0xfb, greater than CHAR_MAX, is not a syntax error. The effect is implementation defined and unsurprisingly 0xfb is converted to the value -5 which has the same 8-bit pattern. a and b are promoted to int when passed as variable arguments to printf because this is the specified behavior in such a case.
In case 2, x, y and z are promoted to int, because on your platform type int can represent all values of type char. The computation is performed with int arithmetic, producing a result of 54, which is in the range of type char, so can be stored into h without a problem. h is promoted to int when passed to printf as above.
In case 3, m and n are not promoted because they have type int already. The promotion rules apply to each operand, not to the result of the operation as if it was performed with arbitrary precision: m*n is performed with int arithmetic and overflows on your platform where int is presumably 32 bits wide. The behavior is undefined. It so happens that the result is -2147483648 so dividing it by 2 yields -107341824 but this is not guaranteed by the C Standard.
For the computation to be performed accurately, at least m or n must have a larger type or be cast as such: i = (unsigned)m * n / o; would produce 107341824 on your platform, but beware that type int might have fewer than 32 bits. For a portable expression, you would need to use type unsigned long, which is specified as having at least 32 value bits, for both the cast and the type of i.
Finally, main should be defined as int main() or int main(void) or int main(int argc, char *argv[]) or a compatible prototype. void main() is incorrect.
#include <stdio.h>
int main()
{
unsigned char a = 0xfb;
char b = 0xfb;
printf("case1 : %d, %d", a, b); // case1
char x=90, y=60, z=100, h;
h = x*y/z;
printf("\ncase2 : %d", h); // case2
unsigned long long int m = 32768, n = 65536, o = 2,i;
i = m*n/o;
printf("\ncase3 : %llu", i); // case3
}
in this type you can get answer [0, +18,446,744,073,709,551,615] range

why this sizeof(c+a) is giving 4 byte instead of 3

#include <stdio.h>
int main()
{
short int a;
char c;
printf("%d %d %d",sizeof(a),sizeof(c),sizeof(c+a));
}
In this sizeof a is 2 byte size of char is 1 byte but i add them up it is giving 4 byte. what it is doing inside the expression to make it 4
Adding a short int to a char results in an int, which apparently is 4 bytes on your system.
This is a case if "integer promotion". See In a C expression where unsigned int and signed int are present, which type will be promoted to what type? for an explanation. The rules are rather confusing, but the answers there explain it rather well.
Per 6.3.1.8 Usual arithmetic conversions of the C standard, the actual conversion rule is:
If both operands have the same type, then no further conversion is
needed.
Otherwise, if both operands have signed integer types or both have
unsigned integer types, the operand with the type of lesser
integer conversion rank is converted to the type of the operand
with greater rank.
Otherwise, if the operand that has unsigned integer type has
rank greater or equal to the rank of the type of the other
operand, then the operand with signed integer type is
converted to the type of the operand with unsigned integer
type.
Otherwise, if the type of the operand with signed integer type can
represent all of the values of the type of the operand with unsigned
integer type, then the operand with unsigned integer type is
converted to the type of the operand with signed integer type.
Otherwise, both operands are converted to the unsigned
integer type corresponding to the type of the operand with signed
integer type.
The result is 4, because, as #WeatherVane noted in the comments:
5.1.2.3 para 11 EXAMPLE 2 In executing the fragment char c1, c2; /* ... */ c1 = c1 + c2; the "integer promotions" require that the abstract machine promote the value of each variable to int size and then add the two ints and truncate the sum. But there is no truncation here because the destination is unknown.
sizeof returns the size of the object representation after it has been evaluated. The expression c+a apparently returns an int, which is four bytes. I think what you are looking for is:
sizeof(c) + sizeof(a)
When integral types like char, short int, bool take less number of bytes than int, then these data types are automatically promoted to int or unsigned int when an operation is performed on them.
C11 §6.3.1.1 Boolean, characters, and integers
If an int can represent all values of the original type (as restricted
by the width, for a bit-field), the value is converted to an int;
otherwise, it is converted to an unsigned int. These are called the
integer promotions. 58)
So, c+a are converted to type int and the result has this common type of operands that is int.
Also, the behaviour of your code is undefined, because you have used the wrong format specifier.
So, use %zu instead of %d because sizeof() returns size_t and size_t is unsigned.
C11 Standard: §7.21.6.1: Paragraph 9:
If a conversion specification is invalid, the behavior is
undefined. 225) If any argument is not the correct type for the
corresponding conversion specification, the behavior is undefined.
For the mathematically inclined (and because it occurred to me to wonder when such a thing might ever be true):
The misapprehension that is OP is labouring under is that
f(x) + f(y) = f(x+y)
which is certainly not true for sizeof() for the reasons Tom points out in the comments.
The class of functions for which it is true are called Additive Maps
Typical examples include maps between rings, vector spaces, or modules that preserve the additive group.

sizeof an integer expression in C

To my semi-surprise with Xcode compiling C (gnu11)
#include <stdio.h>
int main(int argc,char**argv)
{
short s = 1;
printf( "%zd %zd %zd\n", sizeof(s), sizeof(s*s), sizeof(s?s:s));
return 0;
}
Produces the output
2 4 4
I was expecting
2 2 2
or possibly
2 4 2
Why is this?
I recalled from K&R that any integer expression smaller than an int is promoted to int. I found this in Standard (C89):
3.2.1.1 Characters and integers
A char, a short int, or an int bit-field, or their signed or
unsigned varieties, or an object that has enumeration type, may be
used in an expression wherever an int or unsigned int may be used. If
an int can represent all values of the original type, the value is
converted to an int;
The surprise is s?s:s because all reference I could find say the resulting type is an lvalue but that's just C++. C treats it as an rvalue. As expected then, C++'s output is:
2 4 2
Even if the expression is an rvalue. An unexpected difference between C and C++.
This is because the latter two sizeof operands contain operators. In C, types narrower than int are promoted to int before the operation is performed. So s * s is an int multiplied with an int, yielding an int.
Similarly for short ? short : short getting promoted to int ? int : int.
According to C11§6.5.15/5:
If both the second and third operands have arithmetic type, the result type that would be determined by the usual arithmetic conversions, were they applied to those two operands, is the type of the result. [...]
In your case, the result is equal to sizeof(int) because the 'usual arithmetic conversions' promote the shorts to ints. Same as with sizeof(s*s).

Does Unary + operator do type conversions?

Till now I was believing that there is no use of unary + operator.
But then I came across with following example:
char ch;
short sh;
int i;
printf("%d %d %d",sizeof(ch),sizeof(sh),sizeof(i)); // output: 1 2 4
printf("%d %d %d",sizeof(+ch),sizeof(+sh),sizeof(i)); // output: 4 4 4
Does it mean + is doing type conversion here?
Because it is behaving same as following
printf("%d %d %d",sizeof((int)ch),sizeof((int)sh),sizeof(i)); // output: 4 4 4
This forces me to think + is doing type conversion.
But then I try it on double
double f;
printf("%d %d %d", sizeof(+f), sizeof((int)f), sizeof(f)); // output: 8 4 8
This forces me to rethink about unary + operator.
So my second question is: does unary + operator has special effect in sizeof operator?
Unary + performs integer promotions on its operand, we can see this by going to the draft C99 standard section 6.5.3.3 Unary arithmetic operators which says (emphasis mine going forward):
The result of the unary + operator is the value of its (promoted)
operand. The integer promotions are performed on the operand, and the
result has the promoted type.
and section 6.3.1 Arithmetic operands says:
If an int can represent all values of the original type, the value is
converted to an int; otherwise, it is converted to an unsigned int.
These are called the integer promotions.48) All other types are
unchanged by the integer promotions.
Note that all other types are unchanged by the integer promotions and thereofore double stays a double. This would also hold for float as well, which would not be promoted to double.
Also note that using %d for the result of sizeof is undefined behavior since the result is size_t. the proper format specifier would be %zu.
When smaller types are involved in an expression with larger types (for example, char is smaller than short which mostly is smaller than int which may be smaller than long), the involved types are promoted to the larger tyoes.
So yes, when you use the unary + operator, you get an int, because int is the natural integer type in C.
Regarding the double type, the natural floating point type for C is double, which is why there's no promotion on values or variables that already are of type double.
The sizeof operator have nothing to do with this.
Operator unary + triggers "initial" usual arithmeric conversions, so all integer operands, which type has lower rank than rank of int and unsigned int are promoted to int (or unsigned int if int type does not cover all values of type being promoted on that implementation).
It's not sizeof, it's the unary + itself. The operands of unary + undergo "the usual arithmetic conversions". See, e.g., this other answer involving ordinary addition.

Resources