Data types..

According to ISO Standard 6.2.5 paragraph 7 as:

The standard signed integer types and standard unsigned integer types are collectively called the standard integer types, the extended signed integer types and extended unsigned integer types are collectively called the extended integer types.

This defines the terms standard integer types and extended integer types. Note that this definition does not include the type char or the enumerated types. These are included in the definition of the term integer types. Let us see what we have learned :
In our , we have discussed about Signed integer type and thus standard signed integer types are:
  1. short int
  2. int
  3. long int
  4. long long int
and thus standard Unsigned Integer type are:
  1. unsigned short int
  2. unsigned int
  3. unsigned long int
  4. unsigned long long int
and combining the standard singed and unsigned Integer types are collectively called the standard integer types and thus standard integer types are :
  1. short int
  2. int
  3. long int
  4. long long int
  5. unsigned short int
  6. unsigned int
  7. unsigned long int
  8. unsigned long long int

Extended Integer types

Extended integer types are implementation-specific integer types that are provided as an extension. Because almost everything about such extensions is implementation-defined, the standard can’t say much about them. However, a proposal provides a framework for implementing such extensions in a way that doesn’t interfere with the behavior of standard compliant programs.

N1988 proposes a new category of integer types called extended integer types. Here, “extended” means “nonstandard” rather than “types that are necessarily bigger than the standard types”. The extended integral types could thus be bigger than the largest standard type, or have a size between two standard types. Two common examples are 128-bit integers that some processors already support, and the 48-bit integers that can be found in some embedded systems and math packages.

Obviously, a proposal about extension raises some eyebrows. The extended integers types are implementation-defined so a C proposal can’t name them or specify their underlying types. Additionally, you can’t use such types portably, so what’s the purpose of the extended integer proposal? The main purpose thereof is to guarantee that if an implementation supports additional integer types, it shall conform to certain rules. Additionally, the proposal guarantees that the behavior of standard-conforming programs shall not be affected by the presence of extended integer types, if any.
An implement may support additional signed integral types. These are called extended signed integral types. The extended signed integral types and the standard signed integral types are collectively called signed integral types. For each of the extended signed integral types, the implementation shall also define a corresponding extended unsigned type with the same amount of storage and alignment requirements of the signed type. The standard and extended unsigned integer types are collectively called unsigned integer types. Likewise, the standard unsigned and signed integral types together with the extended signed and unsigned integral types are collectively called integral types. This classification is needed for the new integral promotion rules that apply to extended integral types, too.

Conversion rank

The concept of Conversion rank is new in C99. According to ISO Standard 6.2.5 paragraph 8 as:

For any two integer types with the same signedness and different integer conversion rank (see 6.3.1.1), the range of values of the type with smaller integer conversion rank is a subrange of the values of the other type.

Sizes of integer types provides a minimum range of values that each type must be able to represent. Nothing is said about maximum values. The specification of rank is based on the names of the types, not their representable ranges. This requirement prevents, for instance, an implementation from supporting a greater range of representable values in an object of type short than in an object of type int. An implementation could choose to support an integer type, with a given conversion rank, capable of representing the same range of values as an integer type with greater rank. But an integer type cannot be capable of representing a greater range of values than another integer type (of the same sign) without also having a greater rank.
The ranks order the integer data types by their width from lowest to highest. The signed and unsigned varieties of each type are assigned the same rank. The following abridged list sorts integer types by conversion rank from high to low. The C standard assigns ranks to other integer types, but this list should suffice for this discussion:
  • long long int, unsigned long long int
  • long int, unsigned long int
  • unsigned int, int
  • unsigned short, short
  • char, unsigned char, signed char
  • _Bool
Basically, any place in C where you can use an int or unsigned int, you can also use any integer type with a lower integer conversion rank. This means you can use smaller types, such as chars and short ints, in the place of ints in C expressions. You can also use a bit field of type _Bool, int, signed int, or unsigned int. The bit fields aren’t ascribed integer conversion ranks, but they are treated as narrower than their corresponding base type. This makes sense because a bit field of an int is usually smaller than an int, and at its widest, it’s the same width as an int.

Modulo Operators – Overflow

According to ISO Standard 6.2.5 paragraph 9 as:

The range of non-negative values of a signed integer type is a subrange of the corresponding unsigned integer type, and the representation of the same value in each type is the same. A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.

For an unsigned integer type, the C compiler saves the value of the number to be saved modulus one plus the largest value that type can hold.
#include<stdio.h>
int main(int argc, char* argv[]){
    unsigned short int num1 = 65534;
    unsigned int good_num1 = 65534;
    printf("\nSize of num1 \t\t=\t %d bytes", sizeof(num1));
    printf("\nSize of good_num1 \t=\t %d bytes", sizeof(good_num1));
    printf("\n\nTrue value(good_num1)\t\t value stored in num1");
    for(int i=0; i<10; i++){
        printf("\n%d\t\t\t\t\t%d", good_num1, num1);
        num1++;
        good_num1++;
    }
    return 0;
}
OUTPUT:
Size of num1               =     2 bytes
Size of good_num1    =     4 bytes
True value(good_num1)        value stored in num1
65534                                        65534
65535                                        65535
65536                                        0
65537                                        1
65538                                        2
65539                                        3
65540                                        4
65541                                        5
65542                                        6
65543                                        7
Here we see that when one tries to store a value in an unsigned integer type that is larger than the maximum value, only the modulus remains, which leads to results like 65535 + 1 = 0. The value of essentially “wraps around” to 0. This can cause some unusual behavior if the resultant value is used in computation. Before we consider some of the consequences, let’s see what happened when signed integer overflows.
For signed integer, the Standard says that Integer overflow results in what the manual refers to as “undefined behavior,” which means “for which this International Standard imposes no requirements” What this means is that any result is acceptable, including an application crash. On most compilers, including two of the most common ones (gcc and MS Visual Studio), treat signed integers in much same way as unsigned integer. Consider the following program:
#include<stdio.h>
int main(int argc, char* argv[]){
	short int num1 = 32766;
	int good_num1 = 32766;
	printf("\nSize of num1 \t\t = %d bytes", sizeof(num1));
	printf("\nSize of good_num1 \t = %d bytes", sizeof(good_num1));
	for(int i=0;i<10;i++){
		printf("\n%d\t\t\t%d", good_num1, num1);
		num1++;
		good_num1++;
	}
	return 0;
}
Output:
Size of num1              =  2 bytes
Size of good_num1   =  4 bytes
32766                              32766
32767                              32767
32768                             -32768
32769                             -32767
32770                             -32766
32771                             -32765
32772                             -32764
32773                             -32763
32774                             -32762
32775                             -32761
As with most compiler implementations, here we see a “wraparound effect” where 32767 +1 = -32767. This occurs not only when the number is incremented, but also when too large value is stored, as well.
Integer overflows can also happen when we perform arithmetic operations on pointers. A pointer is simply an unsigned integer that stores a memory address. Often in the course of writing an application we need to move forward and backward through an array, and sometimes this is done through loops. If pointer value aren’t checked then its easy to make a mistake here and overflow the pointer variable. The result can be unpredictable, but typically the application crashes from a memory access violation.
A closely related issue to integer overflows is integer underflow. An underflow occur when the number we are attempting to store in an integer is smaller than the lower bound of the variable type. For unsigned integer, for example. subtracting 1 from an integer with the value 0 stored can result in a very large number. For signed integers, the “wraparound” effect can mean that subtracting 1 from an integer that has its minimum value stored can lead to a positive number as a result.
Tagged , , ,