If you are an embedded software developer, then believe me this article will help you. Before the C99, the C standard only introduced built-in types with no defined size. This ambiguity is intentional in the original C standard to give the compiler vendors more flexibility. But sometimes it creates a problem and reduces portability.
According to the C standard, the size of the long
must be at least 32 bits and the size of the int
and short
must be at least 16 bits but the size of the short must be no longer than the int
.
//C built-in types arranged in size (bytes) sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long) (only C99)
You can resolve the problem by creating a header file, where you can create a fixed-width integer type using the typedef and original built-in data type. In the project, you need to include this created header file in all source files (.c)
But the problem with this handmade header file is that it can not be universal. There are two important reasons, first, it uses some made-up name, which is not standardized in the same sense as the built-in type is. And second definition is only correct for the specific processor and specific compiler.
So to resolve this problem the C standard introduces a new <stdint.h> header file in the C99 standard. For the embedded software developer <stdint.h> header file is the most valuable feature introduced in the C99 standard.
As you can see, the file uses the typedefs to define the fixed width integer types. According to the updated standard, this required set of typedefs (along with some others) is to be defined by compiler vendors and included in the new header file stdint.h . Let’s see some newly defined fixed-width integer types,
Size | Signed | Unsigned |
8-bit: | int8_t | uint8_t |
16-bit: | int16_t | uint16_t |
32-bit: | int32_t | uint32_t |
64-bit: | int64_t | uint64_t |
#include <stdio.h> #include <stdint.h> int main() { //signed printf("sizeof(int8_t) = %zu\n", sizeof(int8_t)); printf("sizeof(int16_t) = %zu\n", sizeof(int16_t)); printf("sizeof(int32_t)) = %zu\n", sizeof(int32_t)); printf("sizeof(int64_t) = %zu\n", sizeof(int64_t)); //unsigned printf("sizeof(uint8_t) = %zu\n", sizeof(uint8_t)); printf("sizeof(uint16_t) = %zu\n", sizeof(uint16_t)); printf("sizeof(uint32_t) = %zu\n", sizeof(uint32_t)); printf("sizeof(uint64_t) = %zu\n", sizeof(uint64_t)); return 0; }
Output:
Let’s discuss some important concepts related to the integer. I have already written an article on signed vs unsigned integer. If you want you can read this article.
Scenario 1:
When you will compile the below code on a 32-bit machine, the program will work fine and you will get the expected result.
#include <stdio.h> #include <stdint.h> int main() { uint16_t a = 40000; uint16_t b = 50000; uint32_t c = a + b; printf("%u\n",c); return 0; }
Output:
Now running the same code on a 16-bit machine where standard int is 16-bit wide.
Oh my God, you are not getting the actual result that you assumed. The basic reason behind this output is overflow. The C automatically promotes any smaller size integer to the built-in type int or unsigned int before performing any computation.
So when you ran the same code on a 32-bit machine, the integer promotion was 32 bits because the int size is 32-bits. But for 16 bits there is no real promotion because type int is only 16 bits wide.
Now we know the problem but how we can resolve this problem?
The solution is very simple we only need to enforce the promotion to 32-bit precision of at least one operand ‘a’ or ‘b’. If one of the operands 32 bit wide others will automatically be promoted to 32-bits wide and the whole operation performed at 32 bits.
#include <stdio.h> #include <stdint.h> int main() { uint16_t a = 40000; uint16_t b = 50000; uint32_t c = (uint32_t)a + b; printf("%u\n",c); return 0; }
Note: You can typecast explicitly both operand ‘a’ and ‘b’.
Scenario 2:
When we mix signed and unsigned numbers together it creates a problem if we will not carefully handle it. Let’s see a program and compile and run it on a 32-bits machine where the size of int is 32 bits.
#include <stdio.h> #include <stdint.h> int main() { uint16_t a = 50; int32_t b = 10 - a; printf("%d\n",b); return 0; }
You can see that we are getting the expected value on a 32-bits machine. But the problem arises when you will run the same code on a machine where int is 16-bits wide.
In the above case, you have mixed signed and unsigned operands together so implicit conversion will occur. Both operands are promoted to unsigned int and the result is unsigned int. The result will convert signed 32 because the left signed operand is 32 bits wide.
When this code runs on a 16-bit machine then the issue occurs because the unsigned int is 16-bits wide here. So if we assign the 2’s complement value of unsigned int to variable b (which is 32-bits wide), it only fills up the lower half of the bytes of b. Because the value is unsigned, and it is not signed extended to 32 bits and is interpreted as a big positive value.
We can avoid this non-portable issue to avoid mixing signed and unsigned operands by making the unsigned operands to signed explicitly.
#include <stdio.h> #include <stdint.h> int main() { uint16_t a = 50; int32_t b = 10 - (int16_t)a; printf("%d\n",b); return 0; }
Scenario 3:
Another issue occurs when you mixed signed and unsigned integers in comparison statements. Let’s see the below example where only else part will execute.
#include <stdio.h> #include <stdint.h> int main() { uint32_t a = 100; if (a > -1) { printf(" a > -1"); } else { printf(" a < -1"); } return 0; }
Output:
a < -1
We can also resolve this issue by explicit typecasting signed int.
#include <stdio.h> #include <stdint.h> int main() { uint32_t a = 100; if ((int32_t)a > -1) { printf(" a > -1"); } else { printf(" a < -1"); } return 0; }
Output:
a > -1
Recommended Post
- C Programming Courses And Tutorials.
- CPP Programming Courses And Tutorials.
- Python Courses and Tutorials.
- Set, clear or toggle a single bit in C.
- Interview questions on bitwise operators in C.
- Best Laptop for programming.
- 5 ways to reverse bits of an integer.
- Best mouse for programming and coding
- Structure padding and data alignment in C.
- Signed vs unsigned int in detail.
- Operator Precedence And Associativity In C.
- C interview questions.
- 5 Best C++ Books.
- Best gift for programmers and techies.