struct default values in c - c

I came across this problem in C using structs.I'm not sure of what is really happening here
Thanks
#include<stdio.h>
int main()
{
struct num1
{
int n1:2;
int n2:3;
int n3:4;
};
struct num1 num={3,4,5};
printf("%d %d %d\n",num.n1,num.n2,num.n3);
return 0;
}
The obtained output is
-1 -4 5

These are bit fields, the number after the : specifies how many bits are in that member.
int n1:2
means a signed integer with 2 bits. In two's complement notation, this allows for values from -2 to 1; in sign+magnitude notation it allows for -1 to 1. When you try to assign 3 to this member, you get overflow, which results in undefined behavior.
Similarly
int n2:3
means a signied integer with 3 bits, whose range is -4 to 3 in two's complement, -3 to 3 in sign+magnitude, so assigning 4 causes overflow.
int n3:4
has a range from -8 to 7 or -7 to 7, so assigning 5 fits into it, so there's no overflow.

Related

value of members of unions with bit-fields

I'm trying to learn how memory is allocated for unions containing bit-fields.
I've looked at posts and questions similar to this and have understood that padding is involved most of the times depending on the order in which members are declared in structure.
1.
union Example
{ int i:2;
int j:9;
};
int main(void)
{
union Example ex;
ex.j=15;
printf("%d",ex.i);
}
Output: -1
2.
union Example
{ unsigned int i:2;
int j:9;
};
int main(void)
{
union Example ex;
ex.j=15;
printf("%d",ex.i);
}
Output: 3
Can someone please explain the output? Is it just the standard output for such cases?
I use the built-in gcc compiler in ubuntu
The way your compiler allocates bits for bit-fields in a union happens to overlap the low bits of j with the bits of i. Thus, when j is set to 15, the two bits of i are each set to 1.
When i is a two-bit signed integer, declared with int i:2, your compiler interprets it as a two-bit two’s complement number. With this interpretation, the bits 11 represent −1.
When i is a two-bit unsigned integer, declared with unsigned int i:2, it is pure binary, with no sign bit. With this interpretation, the bits 11 represent 3.
The program below shows that setting a signed two-bit integer to −1 or an unsigned two-bit integer to 3 produce the same bit pattern in the union, at least in your C implementation.
#include <stdio.h>
int main(void)
{
union Example
{
unsigned u;
int i : 2;
int j : 9;
unsigned int k : 2;
} ex;
ex.u = 0;
ex.i = -1;
printf("ex.i = -1: %#x.\n", ex.u);
ex.u = 0;
ex.j = 15;
printf("ex.j = 15: %#x.\n", ex.u);
ex.u = 0;
ex.k = 3;
printf("ex.k = 3: %#x.\n", ex.u);
}
Output:
ex.i = -1: 0x3.
ex.j = 15: 0xf.
ex.k = 3: 0x3.
Note that a compiler could also allocate the bits high-to-low instead of low-to-high, in which case the high bits of j would overlap with i, instead of the low bits. Also, a compiler could use different sizes of storage units for the nine-bit j than it does for the two-bit i, in which case their bits might not overlap at all. (For example, it might use a single eight-bit byte for i but use a 32-bit word for j. The eight-bit byte would overlap some part of the 32-bit word, but not necessarily the part used for j.)
Example1
#include <stdio.h>
union Example
{ int i:2;
int j:9;
};
int main(void)
{
union Example ex;
ex.j=15;
printf("%d",ex.i);
printf("\nSize of Union : %lu\n" , sizeof(ex));
return 0;
}
Output:
-1
Size of Union : 4
Observations:
From this ouput, We can deduce that even if we use 9 bits( max ), Compiler allocates 4 byte(because of padding).
Statement union Example ex; allocates 4 bytes of memory for Union Example object ex.
Statement ex.j=15; asigns 0x0000000F to that 4 Byte of memory. So for j it allocates 0-0-0-0-0-1-1-1-1 for 9 bits.
Statement printf("%d",ex.i); tries to print ex.i( which is 2 bits). We have 1-1 from the previous statement.
Here comes the interesting part, ex.i is of type signed int. Hence first bit is used to assign signed representation and most cpu does signed representation in 2's complement form.
So if we does reverse 2's complement of 1-1 we will get value 1. So the ouput we get is -1.
Example2
#include <stdio.h>
union Example
{ unsigned int i:2;
int j:9;
};
int main(void)
{
union Example ex;
ex.j=15;
printf("%d",ex.i);
return 0;
}
Output:
3
Observations:
Statement union Example ex; allocates 4 bytes of memory for Union Example object ex.
Statement ex.j=15; asigns 0x0000000F to that 4 Byte of memory. So for j it allocates 0-0-0-0-0-1-1-1-1 for 9 bits.
statement printf("%d",ex.i); tries to print ex.i( which is 2 bits). We have 1-1 from the previous statement.
Same as Above example but here ex.i is of type unsigned int. Hence no signed bit is used here for representation.
So whatever is stored in the 2 bit location is the exact value. Hence the ouput is 3.
Hope i cleared your doubt. Please check for 2's and 1's complement in the internet.

How to create a negative binary number using signed/unsigned in C?

My thoughts: if one declares an int it basically gets an unsigned int. So if I need a negative value I have to explicitly create a signed int.
I tried
int a = 0b10000101;
printf("%d", a); // i get 138 ,what i've expected
signed int b = 0b10000101; // here i expect -10, but i also get 138
printf("%d", b); // also tried %u
So am I wrong that an signed integer in binary is a negative value?
How can I create a negative value in binary format?
Edit Even if I use 16/32/64 bits I get the same result. unsigned/signed doest seems to make a difference without manually shifting the bits.
If numbers are represented as two's complement you just need to have the sign bit set to ensure that the number is negative. That's the MSB. If an int is 32 bits, then 0b11111111111111111111111111111111 is -1, and 0b10000000000000000000000000000000 is INT_MIN.
To adjust for the size int(8|16|64)_t, just change the number of bits. The sign bit is still the MSB.
Keep in mind that, depending on your target, int could be 2 or 4 bytes. This means that int a=0b10000101 is not nearly enough bits to set the sign bit.
If your int is 4 bytes, you need 0b10000000 0000000 0000000 00000000 (spaces added for clarity).
For example on a 32-bit target:
int b = 0b11111111111111111111111111111110;
printf("%d\n", b); // prints -2
because int a = 0b10000101 has only 8 bits, where you need 16 or 32. Try thi:
int a = 0b10000000000000000000000000000101
that should create negative number if your machine is 32bits. If this does not work try:
int a = 0b1000000000000101
there are other ways to produce negative numbers:
int a = 0b1 << 31 + 0b101
or if you have 16 bit system
int a = 0b1 << 15 + 0b101
or this one would work for both 32 or 16 bits
int a = ~0b0 * 0b101
or this is another one that would work on both if you want to get -5
int a = ~0b101 + 1
so 0b101 is 5 in binary, ~0b101 gives -6 so to get -5 you add 1
EDIT:
Since I now see that you have confusion of what signed and unsigned numbers are, I will try to explain it as simple as possible int
So when you have:
int a = 5;
is the same as:
signed int a = 5;
and both of them would be positive. Now it would be the same as:
unsigned int a = 5;
because 5 is positive number.
On the other hand if you have:
int a = -5;
this would be the same as
signed int a = -5;
but it would not be the same as following:
unsigned int a = -5;
the first 2 would be -5, the third one is not the same. In fact it would be the same if you entered 4294967291 because they are the same in binary form but the fact that you have unsigned in front means that compiler would store it the same way but treat it as positive value.
How to create a negative binary number using signed/unsigned in C?
Simply negate the constant of a positive value. To attempt to do so with many 1's
... 1110110 assumes a bit width for int. Better to be portable.
#include <stdio.h>
int main(void) {
#define NEGATIVE_BINARY_NUMBER (-0b1010)
printf("%d\n", NEGATIVE_BINARY_NUMBER);
}
Output
-10

Can you explain how int values are stored in bitfields in C, please?

#include <stdio.h>
struct marks{
int p:4;
int c:3;
unsigned int m:2;
};
void main()
{
struct marks s = {-15, 5, 3};
printf("%d %d %d\n", s.p, s.c, s.m);
}
Output:
1 -3 3
Why is the first value printed as 1 and the second value is printed as -3?
For p, you are allocating 4 bits. Therefore your valid range of values for p is 1000B - 0111B or -8 to 7. The fewest number of bits needed for -15 is 5 which in binary would be 10001B. Since you only allocated 4 bits, the sign bit is lost and you are left with 1.
For c, your are allocating 3 bits which has a valid range of 100B - 011B or -4 to 3. Since 5 is 101B and outside the valid range, it is displayed as -3.
The value 5 in binary is 101. Because the field in question is a 3 bit signed, and the most significant bit is set, the value is negative. In two's complement representation, it is -3.

Using bit with int in structure

#include<stdio.h>
struct a
{
int a:4;
};
main(){
struct a aa;
aa.a=9;
printf("a=%d\n",aa.a);
return 0;
}
Here the output is -7. Why is it so?
what does exactly int a:4 does ? please explain
Since it's two's complement, the highest order bit is used for the sign. By writing a:4 you're saying to only allocate 4 bits of memory, which leaves 3 bits left over for the actual number. So our effective range is [-8,7]. Since all 1's is -1, there's an extra number on the negative side. For more of an explanation on this, see the above link.
9, in (unsigned) binary is: 1001. When you put this into a (signed), you get that a is negative, due to the initial 1, and since the following numbers are 001, we add 1 to the max negative number, thereby giving us -7.
If you want to store the number 9 in only 4 bits, you need to use an unsigned int, which would give you a range of [0, 15].
EDIT:
In case anyone is struggling with figuring out how 1001 signed gives us -7, consider the following:
Since 1111 is -1, let some variable value = -1.
To figure out the values of a negative (signed) int num, let us denote xi in num:
xi : {0,1 at position i, where i=0 is the least significant bit)},
Then, for every xi = 0, subtract 2i from value.
Example:
1001:
value = -1 - 21 - 22 = -7
Your field is a 4 bit signed integer. For signed integers the upper bit is a sign bit, which means that you only have 3 bits for the actual number. The range of numbers you can store in the field are -8 to 7 (assuming 2's compliment storage).
The bit pattern for 9 is 1001, which has the 4th bit set, meaning it is interpreted as a negative number, which is why it is printing out as a -7. If you would have expected a -1, you need to read up on 2's compliment.
If you want to be able to store 9 in the field, make a an unsigned int
You only reserved 4 bits for the field, one bit is used for the sign, so only 3 bits remain for positive values. Thus you can only store values up to 7.
you have to use unsigned indeed int :
#include<stdio.h>
struct a
{
unsigned a:4; // <-- unsigned indeed int, then work good
};
main(){
struct a aa;
aa.a=9;
printf("a=%d\n",aa.a);
return 0;
}
output :
a=9

When does the signedness of an integer really matter?

Due to the way conversions and operations are defined in C, it seems to rarely matter whether you use a signed or an unsigned variable:
uint8_t u; int8_t i;
u = -3; i = -3;
u *= 2; i *= 2;
u += 15; i += 15;
u >>= 2; i >>= 2;
printf("%u",u); // -> 2
printf("%u",i); // -> 2
So, is there a set of rules to tell under which conditions the signedness of a variable really makes a difference?
It matters in these contexts:
division and modulo: -2/2 = 1, -2u/2 = UINT_MAX/2-1, -3%4 = -3, -3u%4 = 1
shifts. For negative signed values, the result of >> and << are implementation defined or undefined, resp. For unsigned values, they are always defined.
relationals -2 < 0, -2u > 0
overflows. x+1 > x may be assumed by the compiler to be always true iff x has signed type.
Yes. Signedness will affect the result of Greater Than and Less Than operators in C. Consider the following code:
unsigned int a = -5;
unsigned int b = 7;
if (a < b)
printf("Less");
else
printf("More");
In this example, "More" is incorrectly output, because the -5 is converted to a very high positive number by the compiler.
This will also affect your arithmetic with different sized variables. Again, consider this example:
unsigned char a = -5;
signed short b = 12;
printf("%d", a+b);
The returned result is 263, not the expected 7. This is because -5 is actually treated as 251 by the compiler. Overflow makes your operations work correctly for same-sized variables, but when expanding, the compiler does not expand the sign bit for unsigned variables, so it treats them as their original positive representation in the larger sized space. Study how two's compliment works and you'll see where this result comes from.
It affects the range of values that you can store in the variable.
It is relevant mainly in comparison.
printf("%d", (u-3) < 0); // -> 0
printf("%d", (i-3) < 0); // -> 1
Overflow on unsigned integers just wraps around. On signed values this is undefined behavior, everything can happen.
The signedness of 2's complement numbers is simply just a matter of how you are interpreting the number. Imagine the 3 bit numbers:
000
001
010
011
100
101
110
111
If you think of 000 as zero and the numbers as they are natural to humans, you would interpret them like this:
000: 0
001: 1
010: 2
011: 3
100: 4
101: 5
110: 6
111: 7
This is called "unsigned integer". You see everything as a number bigger than/equal to zero.
Now, what if you want to have some numbers as negative? Well, 2's complement comes to rescue. 2's complement is known to most people as just a formula, but in truth it's just congruency modulo 2^n where n is the number of bits in your number.
Let me give you a few examples of congruency:
2 = 5 = 8 = -1 = -4 module 3
-2 = 6 = 14 module 8
Now, just for convenience, let's say you decide to have the left most bit of a number as its sign. So you want to have:
000: 0
001: positive
010: positive
011: positive
100: negative
101: negative
110: negative
111: negative
Viewing your numbers congruent modulo 2^3 (= 8), you know that:
4 = -4
5 = -3
6 = -2
7 = -1
Therefore, you view your numbers as:
000: 0
001: 1
010: 2
011: 3
100: -4
101: -3
110: -2
111: -1
As you can see, the actual bits for -3 and 5 (for example) are the same (if the number has 3 bits). Therefore, writing x = -3 or x = 5 gives you the same result.
Interpreting numbers congruent modulo 2^n has other benefits. If you sum 2 numbers, one negative and one positive, it could happen on paper that you have a carry that would be thrown away, yet the result is still correct. Why? That carry was a 2^n which is congruent to 0 modulo 2^n! Isn't that convenient?
Overflow is also another case of congruency. In our example, if you sum two unsigned numbers 5 and 6, you get 3, which is actually 11.
So, why do you use signed and unsigned? For the CPU there is actually very little difference. For you however:
If the number has n bits, the unsigned represents numbers from 0 to 2^n-1
If the number has n bits, the signed represents numbers from -2^(n-1) to 2^(n-1)-1
So, for example if you assign -1 to a an unsigned number, it's the same as assigning 2^n-1 to it.
As per your example, that's exactly what you are doing. you are assigning -3 to a uint8_t, which is illegal, but as far as the CPU is concerned you are assigning 253 to it. Then all the rest of the operations are the same for both types and you end up getting the same result.
There is however a point that your example misses. operator >> on signed number extends the sign when shifting. Since the result of both of your operations is 9 before shifting you don't notice this. If you didn't have the +15, you would have -6 in i and 250 in u which then >> 2 would result in -2 in i (if printed with %u, 254) and 62 in u. (See Peter Cordes' comment below for a few technicalities)
To understand this better, take this example:
(signed)101011 (-21) >> 3 ----> 111101 (-3)
(unsigned)101011 ( 43) >> 3 ----> 000101 ( 5)
If you notice, floor(-21/8) is actually -3 and floor(43/8) is 5. However, -3 and 5 are not equal (and are not congruent modulo 64 (64 because there are 6 bits))

Resources