© Mikael Olsson 2019
Mikael OlssonModern C Quick Syntax Referencehttps://doi.org/10.1007/978-1-4842-4288-9_3

3. Variables

Mikael Olsson1 
(1)
Hammarland, Länsi-Suomi, Finland
 

Variables are used for storing data during program execution.

Data Types

Depending on what data you need to store, there are several different kinds of built-in data types in C. The so-called simple or primitive types are listed in the following table. They consist of four integer types, three floating-point types, as well as the char type .

Data Type

Size (Byte)

Description

char

1

Integer or character

short

int

long

long long

2

4

4 or 8

8

Integer

float

double

long double

4

8

8 or 16

Floating-point number

In C, the exact sizes of the data types are not fixed. The sizes shown in the previous table are those commonly found on 32-bit and 64-bit systems. The C standard only specifies the minimum range that is guaranteed to be supported. The minimum size for char is 8 bits, for short and int it is 16 bits, for long it is 32 bits, and long long must contain at least 64 bits. Most modern compilers make int 32 bits, which nearly universally means 4 bytes. Each integer type in the table must also be at least as large as the one preceding it. The same applies to the floating-point types where each type must provide at least as much precision as the preceding one.

Declaring Variables

Before a variable can be used, it first has to be declared (created). To declare a variable, you start with the data type you want the variable to hold followed by an identifier, which is the name of the variable.
int myInt;
The identifier can consist of letters, numbers, and underscores, but it cannot start with a number. It also cannot contain spaces or special characters and must not be a reserved keyword.
int _myInt32; /* allowed */
int 32Int;    /* incorrect (starts with number) */
int my Int;   /* incorrect (contains space) */
int Int@32;   /* incorrect (contains special character) */
int int;      /* incorrect (reserved keyword) */

Note that C is a case-sensitive programming language, so uppercase and lowercase letters have different meanings.

Assigning Variables

To assign a value to a declared variable, you use the equals sign, which is known as the assignment operator (=). This is called assigning or initializing the variable.
myInt = 50;
The declaration and assignment can be combined into a single statement. When a variable is assigned a value it then becomes defined.
int myInt = 50;
If you need to create more than one variable of the same type, there is a shorthand way of doing this using the comma operator (,).
int x = 1, y = 2, z;
Once a variable has been defined (declared and assigned), you can use it by simply referencing the variable’s name. For example, to copy the value to another variable, you can use the following.
int a = x;

Printing Variables

In addition to strings, the printf function can be used to print values and variables to the standard output stream. This is done by embedding format specifiers into the string where the value is to be printed. Each specifier must be matched by a corresponding argument to printf of the correct type, as seen in the following example.
#include <stdio.h>
int main() {
  int x = 5;
  printf("x is %d and 2+3 is %d", x, 2+3);
}
The %d specifier displays an integer of the char, short, or int type. Other commonly used format specifiers are seen in the following table.

Specifier

Output

%d or %i

char, short, or int

%c

Character

%s

String of characters

%f

float or double

%Lf

long double

%ld

long int

%lld

long long int

%u

Unsigned char, short or int

%lu

Unsigned long int

%llu

Unsigned long long int

%p

Pointer address

For more information on printf and other standard library functions, visit the C library reference on cplusplus.com .1

Integer Types

There are four native integer (whole number) types you can use depending on how large a number you need the variable to hold. Typical ranges on 32-bit and 64-bit systems are given here.
char  myChar  = 0; /* -128   to +127 */
short myShort = 0; /* -32768 to +32767 */
int   myInt   = 0; /* -2^31  to +2^31-1 */
long  myLong  = 0; /* -2^31  to +2^31-1 */
C99 added support for the long long data type, which is guaranteed to be at least 64 bits in size.
long long myLL = 0; /* -2^63 to +2^63-1 */
To determine the exact size of a data type, you can use the sizeof operator. This operator returns the number of bytes that a type occupies in the system you are compiling for. The type returned is size_t, which is an alias for an integer type. The specifier %zu was introduced in C99 as a portable way to format this type with printf. Visual Studio does not support this specifier and uses %Iu instead.
#include <stdio.h>
int main(void) {
  size_t s = sizeof(int);
  printf("%zu", s); /* "4" (C99) */
  printf("%Iu", s); /* "4" (Visual Studio) */
}
In addition to standard decimal notation, integers can also be assigned by using octal or hexadecimal notation. The following values all represent the same number, which in decimal notation is 50.
int myDec = 50    /* decimal notation */
int myOct = 062;  /* octal notation (0) */
int myHex = 0x32; /* hexadecimal notation (0x) */

Signed and Unsigned

By default, all integer types in C are signed and may therefore contain both positive and negative values. This can be explicitly specified using the signed keyword.
signed char  myChar;   /* -128   to +127 */
signed short myShort;  /* -32768 to +32767 */
signed int   myInt;    /* -2^31  to +2^31-1 */
signed long  myLong;   /* -2^31  to +2^31-1 */
signed long long myLL; /* -2^63  to +2^63-1 */
If only positive values need to be stored, the integer types can be declared as unsigned to double their upper range.
unsigned char  uChar;   /* 0 to 255 */
unsigned short uShort;  /* 0 to 65535 */
unsigned int   uInt;    /* 0 to 2^32-1 */
unsigned long  uLong;   /* 0 to 2^32-1 */
unsigned long long uLL; /* 0 to 2^64-1 */
When an unsigned value is printed, the specifier %u is used for the unsigned char, short, and int types. The unsigned long type is specified with %lu and unsigned long long is specified with %llu.
unsigned int uInt = 0;
printf("%u", uInt); /* "0" */
The signed and unsigned keywords may be used as types on their own, in which case the int type is assumed by the compiler.
unsigned uInt; /* unsigned int */
signed sInt;   /* signed int */
In the same way, the short and long data types are abbreviations of short int and long int.
short myShort; /* short int */
long myLong;   /* long int */
 

Sized Integers

As mentioned, the actual sizes of the integer types are implementation dependent. For more precise specification of size, the C99 standard introduced a number of exact-width integer types. They can be enabled by including the stdint.h standard header.
#include <stdint.h>
/* Signed exact-width integers */
int8_t  iSmall;  /* 8 bits */
int16_t iMedium; /* 16 bits */
int32_t iLarge;  /* 32 bits */
int64_t iHuge;   /* 64 bits */
Unsigned versions of these types are available as well. Like the signed versions, these exact-width integer types are guaranteed to have the same number of bits across all implementations.
/* Unsigned exact-width integers */
uint8_t  uSmall;  /* 8 bits */
uint16_t uMedium; /* 16 bits */
uint32_t uLarge;  /* 32 bits */
uint64_t uHuge;   /* 64 bits */
It is recommended to use sized integers when available, to more easily keep track of the range of your integer variables and to enhance the portability of your programs. Compilers that comply with standards prior to C99 may provide sized integers with different type names. Visual Studio, for example, has built-in support for the following signed exact-width integers.
/* Visual Studio signed exact-width integers */
__int8  iSmall;  /* 8 bits */
__int16 iMedium; /* 16 bits */
__int32 iLarge;  /* 32 bits */
__int64 iHuge;   /* 64 bits */
 

Floating-Point Types

The floating-point types can store real numbers with different levels of precision.
float myFloat;    /* ~7 digits */
double myDouble;  /* ~15 digits */
long double myLD; /* typically same as double */
The precision shown here refers to the total number of digits. A float can accurately represent about 7 digits, whereas a double can handle around 15 digits.
float myFloat = 12345.678;
printf("%f", myFloat); /* "12345.677734" */
When printing a floating-point number you can limit the decimal places to, for instance, two in the following way.
printf("%.2f", myFloat); /* "12345.68" */
Floating-point numbers can be expressed using decimal, exponential, or hexadecimal notation. Exponential (scientific) notation is used by adding E or e followed by the decimal exponent, while the hexadecimal floating-point notation uses P or p to specify the binary exponent. Support for the hexadecimal notation was not standardized until C99.
double fDec = 1.23;
double fExp = 3e2;   /* 3*10^2 = 300 */
double fHex = 0xAp2; /* 10*2^2 = 40 */

Literal Suffixes

An integer literal (constant) is normally treated as an int by the compiler, or a larger type if needed to fit the value. Suffixes can be added to the literal to change this evaluation. With integers the suffix can be a combination of U and L, for unsigned and long, respectively. C99 also added the LL suffix for the long long type. The order and casing of these letters do not matter.
int i = 10;
long l = 10L;
unsigned long ul = 10UL;
A floating-point literal is treated as a double. The F or f suffix can be used to specify that a literal is of the float type instead. Likewise, the L or l suffix specifies the long double type.
float f = 1.23F;
double d = 1.23;
long double ld = 1.23L;

The compiler implicitly converts literals to whichever type is necessary, so this type distinction for literals is usually not necessary. If the F suffix is left out when assigning to a float variable, the compiler may give a warning since the conversion from double to float involves a loss of precision.

Char Type

The char type is commonly used to represent ASCII characters. Such character constants are enclosed in single quotes and can be stored in a variable of char type.
char c = 'x'; /* assigns 120 (ASCII for x) */
When the char is printed with the %c format specifier the ASCII character is displayed.
printf("%c", c); /* "x" */
Use the %d specifier to instead display the numerical value.
printf("%d", c); /* "120" */

Bool Type

C99 introduced a _Bool type to increase compatibility with C++. Variables of this type can store a Boolean value, which is a value that can only be either 1 (true) or 0 (false).
_Bool b = 0; /* false value */
The type _Bool is usually accessed via its alias name bool defined by the standard header stdbool.h. This header also defines the macros true and false as aliases for 1 and 0.
#include <stdbool.h>
bool b = true; /* true value */

Variable Scope

The scope of a variable refers to the region of code within which it is possible to use that variable. Variables in C may be declared both globally and locally. A global variable is declared outside of any code blocks and is accessible from anywhere after it has been declared. A local variable, on the other hand, is declared inside of a function and will only be accessible within the function in which it was declared. The lifetime of a local variable is also limited. A global variable will remain allocated for the duration of the program, while a local variable will be destroyed when its function has finished executing.
int globalVar; /* global variable */
int main(void) {
  int localVar; /* local variable */
}
The default values for these variables are also different. Global variables are automatically initialized to zero by the compiler, whereas local variables are not initialized at all. Uninitialized local variables will therefore contain whatever garbage is already present in that memory location.
int globalVar; /* initialized to 0 */
int main(void) {
  int localVar; /* uninitialized */
}
Using uninitialized variables is a common programming mistake that can produce unexpected results. It is therefore a good idea to always give your local variables initial values when they are declared.
int main(void) {
  int localVar = 0; /* initialized to 0 */
}
In C89, local variables must be declared before any other statements within their scope. The later C99 standard changed this to allow variables to be declared anywhere within a function’s scope, which can be more intuitive.
int main(void) {
  int var1;
  /* Other statements */
  int var2; /* C99 only */
}