What are the data types in C

One of the most important concept in programming is the variable. The variable can be seen as the “place” to store “things” as: numerical values, characters, text strings, memory addresses, etc. There are two main concepts regarding variables. The fist concept is the declaration of the variable, which basically means setting its data type. The second concept is the definition of the variable, which means setting its content.

In the C programming language every variable used in the source code needs to be declared by setting its data type. By assigning a certain data type to a variable we define several aspects linked to the variable:

  • the memory size to be allocated to store the content of the variable
  • the types of operations that can be performed on the variable
  • the restrictions which are applied in terms of operations

By the end of this tutorial the reader will know:

  • what is the significance of a data type
  • how to declare and define a variable
  • which are the properties of the standard data types
  • what is integer overflow

In the C programming language a variable is declared as:

unsigned int uiVar;

where:

uiVar – is the name of the variable
unsigned – keyword which defines that our variable is always positive (without the sign “-“)
int – keyword which defines that our variable is an integer and has 4 bytes of memory allocated (for a 32-bit compiler)

The definition of the variable can be done in another line, but only after the declaration:

uiVar = 25;

Variables can be declared and defined in the same instruction:

unsigned int uiVar = 25;

The main data types in C programming language are:

  • integer (fixed-point)
  • floating-point
  • void
C programming language - standard data types

Image: C programming language – standard data types

Note: Depending on the type of the compiler (16-bit or 32-bit), the size and range of int, short and long are different.

The void type has no value and unknown size. It’s mainly used for function definition as return type or argument of the function.

Fixed-point (integers)

The integer data type is also knows as fixed-point type. Variables declared as integers store only integer numbers (e.g. -10, 0, 55), they can not store real values (e.g 1.5, 3.1416).

In the example below we’ll declare a variable of type integer and we’ll assign a non integer value to it. With an if statement we check if the value memorised in the variable is only the integer part of the value, if true we print a message.

#include <stdio.h>

int main(void)
{
	unsigned int uiVar;

	uiVar = 3.1416;

	if (uiVar == 3)
	{
		printf("Integer variables can not store decimals\n");
	}

	return (0);
}

As expected, the comparison uiVar==3 is true (since the message is displayed). This means that only the integer part of the number 3.1416 was memorised, since uiVar was declared as an integer.

An integer can be signed or unsigned. This mean that it can contain both positive and negative numbers (signed) or only positive numbers (unsigned). Function of the number of bytes used to store the content, integer variables can be:

  • char
  • short
  • int
  • long
  • long long

For a 32-bit compiler, the differences between the integer data types are summarised in the table below:

Data type # bytes # bits Minimum value Maximum value Format
printf()
char 1 8 -27 -128 27-1 127  %d
signed char 1 8 -27 -128 27-1 127  %d
unsigned char 1 8 0 0 28-1 255 %u
short 2 16 -215 -32768 215-1 32767 %d
signed short 2 16 -215 -32768 215-1 32767 %d
unsigned short 2 16 0 0 216-1 65535 %u
int 4 32 -231 -2147483648 231-1 2147483647 %d
signed int 4 32 -231 -2147483648 231-1 2147483647 %d
unsigned int 4 32 0 0 232-1 4294967295 %u
long 4 32 -231 -2147483648 231-1 2147483647 %ld
signed long 4 32 -231 -2147483648 231-1 2147483647 %ld
unsigned long 4 32 0 0 232-1 4294967295 %lu
long long 8 64 -263 -9223372036854775808 263-1 9223372036854775807 %lld
signed long long 8 64 -263 -9223372036854775808 263-1 9223372036854775807 %lld
unsigned long long 8 64 0 0 264-1 18446744073709551615 %llu

Note: The number of bytes applies to both X86 (32-bit) and X64 (64-bit) compilers.

The usage of the signed keyword is optional. If we don’t specify the sign, the default setting is signed. For example int is the same as signed int and long the same as signed long.

C programming language - signed integer data types

Image: C programming language – signed integer data types

Variables of type signed use half of the range for negative values and the other half for positive values. When coding in C we need to pay attention if we really need to use signed data types for our variables. If, for example, we define a variable to measure/calculate a fluid’s pressure, there is no point of using signed data type because the pressure is always positive. By using unsigned data types we double the range of the variable’s values.

C programming language - unsigned integer data types

Image: C programming language – unsigned integer data types

Floating-point

Floating-point values are stored in the single-precision or double-precision format specified by the IEEE Standard 754. Real numbers contain also decimal point numbers so they need to be stored in floating-point variables.

There are three types of floating-point data types:

  • float
  • double
  • long double

float data type is also knows as single-precision data type. double is a enhanced version of float, with bigger memory size, wider range and double-precision.

Data types # bytes Minimum value Maximum value Precision Format
printf()
float 4 1.2E-38 3.4E+38 6 decimal places %f, %e
double 8 2.3E-308 1.7E+308 15 decimal places %lf, %le
long double 10 3.4E-4932 1.1E+4932 19 decimal places %Lf, %Le

Data type limits

There are two header files (*.h) which allows the user to gain precise information regarding the standard data types. To include the content of these header files, we need to write the following instructions at the beginning of our C source code file (*.c):

#include <limits.h>
#include <float.h>

In the macros defined in limits.h header file we can find the limits (range) of the standard data types used in C programming language. The macros are constants with predefined names (e.g INT_MIN), which contain the numerical values of the data types limits. We can use these macros to check if our defined variables exceed the maximum allowable value for a particular data type. For example an unsigned char variable can not store a value bigger than UCHAR_MAX (255).

Example of different data types variables

In the source code below we can find different types of variables (fixed-point and floating-point), declared and defined in the same instruction. These are defined with the maximum and minimum value of each data type.

#include <stdio.h>
#include <limits.h>
#include <float.h>

int main(void)
{
			char 		cVar	= SCHAR_MIN;
	unsigned	char		ucVar	= UCHAR_MAX;
	signed 		char 		scVar 	= SCHAR_MAX;
			short		sVar	= SHRT_MIN;
	unsigned	short		usVar	= USHRT_MAX;
	signed		short		ssVar	= SHRT_MAX;
			int		iVar	= INT_MIN;
	unsigned	int		uiVar	= UINT_MAX;
	signed		int		siVar	= INT_MAX;
			long		lVar	= LONG_MIN;
	unsigned	long		ulVar	= ULONG_MAX;
	signed		long		slVar	= LONG_MAX;
			long long	llVar	= LLONG_MIN;
	unsigned	long long	ullVar	= ULLONG_MAX;
	signed		long long	sllVar	= LLONG_MAX;
			float		fVar	= FLT_MAX;
			double		dVar	= DBL_MAX;
			long double	ldVar	= LDBL_MAX;
	
	printf("SCHAR_MIN 	\t %d\n",	cVar);
	printf("UCHAR_MAX 	\t %u\n",	ucVar);
	printf("SCHAR_MAX 	\t %d\n",	scVar);
	printf("SHRT_MIN 	\t %d\n",	sVar);
	printf("USHRT_MAX 	\t %u\n",	usVar);
	printf("SHRT_MAX 	\t %d\n",	ssVar);
	printf("INT_MIN 	\t %d\n",	iVar);
	printf("UINT_MAX 	\t %u\n",	uiVar);
	printf("INT_MAX 	\t %d\n",	siVar);
	printf("LONG_MIN 	\t %ld\n",	lVar);
	printf("ULONG_MAX 	\t %lu\n",	ulVar);
	printf("LONG_MAX 	\t %ld\n",	slVar);
	printf("LLONG_MIN 	\t %lld\n",	llVar);
	printf("ULLONG_MAX	\t %llu\n",	ullVar);
	printf("LLONG_MAX 	\t %lld\n",	sllVar);
	printf("FLT_MAX 	\t %e\n",	fVar);
	printf("DBL_MAX 	\t %le\n",	dVar);
	printf("LDBL_MAX 	\t %Le\n",	ldVar);

	return (0);
}

Compiling and running the executable displays the following text:

SCHAR_MIN -128
UCHAR_MAX 255
SCHAR_MAX 127
SHRT_MIN -32768
USHRT_MAX 65535
SHRT_MAX 32767
INT_MIN -2147483648
UINT_MAX 4294967295
INT_MAX 2147483647
LONG_MIN -2147483648
ULONG_MAX 4294967295
LONG_MAX 2147483647
LLONG_MIN -9223372036854775808
ULLONG_MAX 18446744073709551615
LLONG_MAX 9223372036854775807
FLT_MAX 3.402823e+38
DBL_MAX 1.797693e+308
LDBL_MAX 1.797693e+308

One important observation is that, for the compiler used in this example, the double and long double data type have the same maximum value even if they are defined by different macros, DBL_MAX and LDBL_MAX respectively.

Integer overflow

What is happening if, by mistake, we exceed the limits contained in a certain data type. For example, we declare a variable as unsigned char and assign the value of 255. If we add 1 to this variable the result will be 256 which will be outside the range of unsigned char.

Integer overflow occurs when the value of an integer type variable exceeds its maximum limits. To understand what’s happening let’s look at the following example:

#include <stdio.h>
#include <limits.h> 

int main(void)
{

	unsigned char ucVar1, ucVar2;
	signed char cVar3, cVar4;

	ucVar1 = UCHAR_MAX;
	ucVar2 = ucVar1 + 1;

	cVar3 = SCHAR_MIN;
	cVar4 = cVar3 - 1;

	printf("ucVar1 = %u\n", ucVar1);
	printf("ucVar2 = %u\n", ucVar2);
	printf("cVar3 = %d\n", cVar3);
	printf("cVar4 = %d\n", cVar4);

	return (0);

}

In this example we declared four variables, two unsigned char and two signed char. The usage of the signed keyword is optional, the same behaviour is obtained without it. The idea behind the example is to assign the maximum value of the data type to one variable and then add/subtract one unit for the second variable of the same data type.

Running the executable outputs the following results:

ucVar1 = 255
ucVar2 = 0
cVar3 = -128
cVar4 = 127

If we perform an arithmetic operation which produces a result larger than the maximum above for a N-bit integer, an integer overflow occurs, which reduces the result to modulo N-th power of 2, keeping only the least significant bits of the result and effectively causing a wrap around (reset). This behaviour may cause security problems leading to unexpected results and arbitrary code execution.

For any questions or observations regarding this tutorial please use the comment form below.

Don’t forget to Like, Share and Subscribe!

One Response

  1. Shiva

Leave a Reply

Ad Blocker Detected

Dear user, Our website provides free and high quality content by displaying ads to our visitors. Please support us by disabling your Ad blocker for our site. Thank you!

Refresh