Understanding Variables In C: A Comprehensive Introduction

Everything you need to know to get started using variables in C programming.

All C programs, whatever their size, consist of functions and variables. A function contains statements that specify the computing operations to be done, and variables store values used during the computation.

In this article, you will learn the meaning of a variable in C, and how to declare a variable and assign values to it. You will learn about the different types of variables and the range of values they can store. You will learn about the ASCII character set. And you will learn how to print the values stored in the different types of variables with the C standard library functions printf and putchar .

Prerequisites

To enjoy the full experience of this article, you should have the following:

  • A basic understanding of how to compile a C source file using a gcc compiler and how to run the output file of a compiled C source file.

  • Ubuntu 20.04 LTS installed on your computer

  • A text editor (preferably Vim)

  • A cup of water or coffee

Every code in this article is compiled on Ubuntu 20.04 LTS using gcc, using the options -Wall -Werror -Wextra -pedantic -std=gnu89

What is a variable in C?

In C, a variable is a symbolic name that represents a particular storage location on the hard drive or any memory device connected to the machine executing your program.

"In C, all variables must be declared before they are used, usually at the beginning of the Function before any executable statements." - The C programming language book (Brian W. Kernighan & Dennis M. Ritchie)

To declare a Variable in C programming, we first specify the Type of the variable, and then its Identifier.

/* syntax of a variable declaration in C */
Type identifier; /*  declaration of a single variable */
Type identifier_1, identifier_2, identifier_N; /* declaration of multiple variables with the same Type */

What is an Identifier?

An Identifier is any name you provide for Variables, Functions, Macros, and other data Objects in your C program. You're required to provide a valid Identifier.

The following list indicates the rules you must follow to construct a valid Identifier in C:

  • It must start with an alphabet(the underscore _ counts as an alphabet).

  • It must not have any spaces in between.

  • It must be unique and must not be a keyword in C.

  • It can be made up of uppercase or lowercase alphabetic characters, the ten digits(0 - 9) and the underscore _

/* example of valid identifiers */
sum
flag
i
Sum
mem_alloc
x5j6
SUM
/* examples of invalid identifiers */
sum$value /* $ is not a valid character */
piece flag /* Embedded spaces are not permitted */
3spencer /* identifier cannot start with a number */
int /* int is a keyword in C */

Uppercase and lowercase letters are distinct in C. Therefore sum, Sum, and SUM are different identifiers.

Programming best practices recommend that you construct identifiers that reflect their intended use.

What is a Type?

A Type is a specification that describes the kind of values an identifier can store or manipulate.

C is a statically typed language. This means all identifiers must be associated with a Type.

The Type specified for an Identifier indicates the following to the compiler:

  • The kind of values that are acceptable to be stored through the Identifier.

  • The acceptable range of values that can be stored through the Identifier.

  • The amount of storage space that should be allocated for the Identifier.

  • The arithmetic computation that should be allowed to be performed with the Identifier.

If any of the above conditions cannot be satisfied for the Type you have specified for an Identifier, the compiler generates an error message.

Fundamental Types in C

C supports three (3) fundamental Types:

  • Integer types

  • Floating types

  • Character types

To specify any of the fundamental types in a variable declaration, we use the dedicated keywords provided for each Type in the C standard:

  • int is the keyword used to specify the Integer type.

  • float is the keyword used to specify the Floating type.

  • char is the keyword used to specify the Character type.

You might have heard or seen people use the term Datatype and wonder what the difference is between Datatype and Type. Although both terms can be used interchangeably, they have slightly different meanings; Type refers to a specification while Datatype refers to data that fits a Type specification.

So when we specify a Type in a variable declaration, we're instructing the compiler to make a variable that can store Datatypes that fits the specifications of the Type specified for the Variable.

How to declare a Variable in C

To declare a variable in C programming, we first specify the Type of the variable, and then its Identifier. For example, we might declare variables "height" and "profit" as:

/* examples of variable declaration */
int height;
float profit;

The first declaration states that height is a variable of Type int, which means that height can store whole numbers. The second declaration states that profit is a variable of Type float, which means that profit can store numbers with digits after the decimal point.

After a variable has been declared, it can be initialized (given a value) by means of assignment:

height = 8;
profit = 21.50;

What happens in your computer when you declare a variable in C?

When you declare a variable in C programming, you essentially introduce the variable to the program. This declaration informs the compiler to allocate storage space for the variable on the hard drive of the computer executing the program.

While compiling your program, the compiler generates machine code that will instruct your computer to allocate a designated portion of memory on its hard drive for every declared variable in your program. The compiler then subsequently assigns corresponding Identifiers to the allocated memory locations. This allocated memory serves as storage for the values assigned to the variables.

The CPU architecture of your computer determines the precise amount of memory that will be allocated for each variable's Type. Different CPU architectures have distinct memory allocation specifications for the fundamental C Types.

Memory allocation specifications are generally defined in Bytes but machines translate memory allocation in Bits.

1 Byte = 8 Bits

The following table shows the memory that will be allocated for variables declared with the fundamental C types on most 64-bit Linux machines:

TypeMemory allocation
char1 Byte
int4 Byte
float4 Bytes

To obtain the memory allocation specifications for each of the fundamental C Types on your computer, pass the Type keyword as an argument to the standard library function sizeof and print its return value with the printf function.

Let's create a program that prints out the memory allocation specification for each of the fundamental C types on our computer:

 /* file name: type_sizes.c */
#include <stdio.h>
/**
 * main - Entry point of the program 
 * Description: Program will print the memory allocation specification 
 * for each of the C fundamental types using   
 * 
 * Return: 0 if program successfully executes
 */

int main(void)
{
    /* print the size of the C fundamental types */
    printf("Size of type 'char' on my computer is: %lu bytes\n," sizeof(char));
    printf("Size of type 'int' on my computer is: %lu bytes\n", sizeof(int));
    printf("Size of type 'float' on my computer is %lu bytes\n", sizeof(float));

    return (0);
}

Image show the memory allocation of the fundamental on my 64-bit Linux computer

We use a format specifier with the printf function to print the value stored in a variable. In the above code snippet, the format specifier %lu is used in the printf function to print the value returned by the sizeof function because its return value is an unsigned Integer type (more details about this Integer type will be discussed soon).

Importance of specifying a Type for a variable declaration

The Type specified for a variable determines the range of values that can be stored through the variable.

C takes portability(the ability of your program to compile on different kinds of machines) seriously and bothers to tell you what ranges of values are guaranteed to be safe for each Type. In the following sections, you will learn more about each Type and its guaranteed range of values.

Integer type

An Integer type variable will store integer constants. int is the keyword that is used to specify the Integer type.

An integer constant consists of a sequence of one or more digits. A minus sign before the sequence indicates that the value is negative. No embedded spaces are permitted between the digits, and values larger than 999 cannot be expressed using commas. C allows integer constants to be written in decimal notation (base 10), octal notation (base 8), or hexadecimal notation (base 16).

Decimal notation of integer constants contains numbers between 0 and 9 but they must not start with a zero:

/* examples of valid integer constants */
10
255
11000

Octal notation of integer constants contains only numbers between 0 and 7, and must start with a zero:

/* example of valid octal integer constants */
017
0377
077777

Hexadecimal notation of integer constants contains numbers between 0 and 9 and letters between a and f, and must start with 0x:

/* examples of valid hexidecimal integer constants */
0xff
0xfF
0x1A
0xAB

Integers are always stored in binary regardless of what notation we have used to express them in our codes. These notations are nothing more than an alternative way to write numbers.

An Integer type variable can be declared as one of two forms; signed or unsigned. By default, a variable declared with the keyword int is a signed Integer type variable. This means such a variable can store either negative or positive integer constants.

Let's create a program that assigns a random number to a variable. The randomly assigned number could be negative or positive. The program will be able to store a different value every time we will run it:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/**
 * main - Entry point of the program 
 * Description: Program will assign a random number to a variable and print it 
 * whether the number stored in the variable is positive or negative followed by
 * a new line.
 * 
 * Return: 0 if program successfully executes
 */
int main(void)
{
    /* variable declaration */
    int n;

    /* assign random number to variable n */
    srand(time(0));
    n = rand() - RAND_MAX / 2;

    /* check if number assigned to the variable n is positive or negative */
    if (n < 0)
    {
        printf("%d is negative\n", n);
    } else if (n == 0)
    {
        printf("%d is zero\n", n);
    } else
    {
        printf("%d is positive\n", n);
    }

     /* exit program after successful execution */
    return (0);
}

The focus of the above code snippet is to show you that the variable n declared with the int keyword is a signed integer type variable. You don't have to understand what rand, srand, and RAND_MAX do.

From the output, you can see every time we run the program(output file of the compiled source code) it prints a different value; either positive or negative.

To declare an unsigned Integer type variable, use the unsigned int keyword. An unsigned integer type variable can only store positive integer constants. (You can test this by changing the type keyword in the declaration of the variable n and then recompile the program).

For both signed and unsigned integer types, C provides 3 subtypes to allow us to construct an Integer type variable that meets our needs. The following keywords are used to specify each of the subtypes:

  • short int

  • unsigned short int

  • int

  • unsigned int

  • long int

  • unsigned long int

C allows us to abbreviate these Integer subtypes keywords in our codes by dropping the word int. For example, unsigned short int can be abbreviated to unsigned short and long int can be shortened to just long. Omitting int this way is a widespread practice among C programmers.

The integer Type you specify in a variable's declaration determines the memory allocation and the range of values that can be stored through the variable. The range of values represented by each integer Type and their memory allocation varies from machine to machine. The following table shows the memory allocation for each integer Type and the range of values that they represent on most 64-bit Linux machines:

TypeMemory AllocationValue Range
short2 Bytes-32,768 to 32,767
unsigned short2 Bytes0 to 65,535
int4 Bytes-2,147,483,648 to 2,147,483,647
unsigned int4 Bytes0 to 4,294,967,295
long8 Bytes-9,233,372,036,854,775,808 to 9,223,372,036,854,775,807
unsiged long8 Bytes0 to 18,446,744,073,709,551,615

How to print the value in an Integer type Variable

The following table shows each Integer subtype and its printf format specifier:

Typeprintf format specifier (Decimal, Octal, Hexadecimal)
short%hd, %ho, %hx
unsigned short%hu, %hu, %hx
int%d, %o, %x
unsigned int%u, %o, %x
long%ld, %lo, %lx
unsigned long%lu, %lo, %lx

One way to determine the value ranges of the integer types on our computer's implementation is to check the limits.h header, which is part of the standard library. The limits.h header defines macros (identifiers that represent statements or expressions) that represent the smallest and largest values of each integer type.

Let's create a program that assigns to variables, macros defined for each integer type in the limits.h header. And then prints out the values stored in each of the variables using the format specifier for their type:

#include <stdio.h>
#include <limits.h>
/**
 * main - Entry point of the program 
 * Description: Program will print the range values for each integer type
 * 
 * Return: 0 if program successfully executes
 */
int main(void)
{
    /* variable declaration */
    int integerVarMin;
    int integerVarMax;
    unsigned int uIntegerVarMax;
    short sIntegerVarMin;
    short sIntegerVarMax;
    unsigned short u_sIntegerVarMax;
    long LIntegerVarMin;
    long LIntegerVarMax;
    unsigned long u_LIntegerVarMax;


    /* Assign to each variable the macros defined 
     * for their integer type in the limits.h header 
     */ 
    integerVarMin = INT_MIN;
    integerVarMax = INT_MAX;
    uIntegerVarMax = UINT_MAX;
    sIntegerVarMin = SHRT_MIN;
    sIntegerVarMax = SHRT_MAX;
    u_sIntegerVarMax = USHRT_MAX;
    LIntegerVarMin = LONG_MIN;
    LIntegerVarMax = LONG_MAX;
    u_LIntegerVarMax = ULONG_MAX;

    /* Use printf and format specifiers to print values stored in each variable */
    printf("The min value that can be stored in an INT variable is: %d\n", integerVarMin);
    printf("The max value that can be stored in an INT variable is: %d\n", integerVarMax);
    printf("The max value that can be stored in an UNSIGNED INT variable is: %u\n", uIntegerVarMax);
    printf("The min value that can be stored in a SHORT INT variable is: %hd\n", sIntegerVarMin);
    printf("The max value that can be stored in a SHORT INT variable is: %hd\n", sIntegerVarMax);
    printf("The max value that can be stored in an UNSIGNED SHORT INT variable is: %hu\n", u_sIntegerVarMax);
    printf("The min value that can be stored in an LONG INT variable is: %ld\n", LIntegerVarMin);
    printf("The max value that can be stored in an LONG INT variable is: %ld\n", LIntegerVarMax);
    printf("The max value that can be stored in an UNSIGNED LONG INT variable is: %lu\n", u_LIntegerVarMax);

    /* exit program after successful execution */
    return (0);
}

Signed vs Unsigned (Optional read)

In C programming, unsigned Integer type variables are preferred over signed Integer type variables because unsigned integers are more efficient. Also, unsigned Integers produce defined results for modulo-arithmetic overflow

Character types

A Character type variable will store character constants. char is the keyword that is used to specify the Character type.

In C programming, a single character in a single quote '' forms a character constant.

'v'

A character constant is a character that is represented in a character set. A character set is an encoding scheme used to represent text characters with numeric codes that computers understand.

The American Standard Code for Information Interchange(ASCII) is a popular character set used as a standard for character encoding in computers and communication systems. It uses integer constants to encode text characters. ASCII is the underlying character set on most Linux/Unix-like machines.

The following characters are represented in ASCII:

  • Character constants of uppercase and lowercase letters of the alphabet.

  • Character constants of digits 0 to 9

  • Character constants of C special characters.

ASCII is capable of representing 128 different characters:

/* examples of characters represented in ASCII */
'b' /* lowercase letter b */
'\n' /* C special character */
'0' /* digit 0 */
' '; /* space */
'B' /* uppercase letter B */

It is important to note that in C programming, character constants are single characters enclosed in single quotes, not double quotes: 'b' is not the same as "b"

C also considers its special characters as single characters, which is why '\n' (a C special character used to inform printf to print a new line) can be a character constant.

How to print the value in a Character type variable

%c is the printf format specifier used to print any character constant stored in a char type variable.

Let's create a program that assigns a character constant to a character type variable and prints the value stored in a Character type variable:

#include <stdio.h>
/**
* main - Entry point of the program 
* Description: Program will print the value stored in a charater type variable
*
* Return: 0 if program successfully executes
*/
int main(void)
{
    /* variable declaration */
    char ch;

    /* variable initialization */
    ch = 'W';

    /* print the value stored in variable ch */
    printf("The character constant stored in variable ch is: %c\n", ch);

    /* exit program after successful execution */
    return (0);
}

Another way to print character constants is to use the putchar function instead of printf. putchar is a standard library function that can take a single character constant as an argument and print it.

Let's create a program that uses putchar to print all letters of the alphabet in lowercase and then a new line:

#include <stdio.h>
/**
* main - Entry point of the program 
* Description: Program will print the letters of the alphabet in lowercase, 
* followed by a new line.
*
* Return: 0 if program successfully executes
*/
int main(void)
{
    /* declare variable */
    char i;

    /* initialize variable */
    i = 'a';

    /* loop and print the value stored in the variable then increment it */
    while (i <= 'z')
    {
        putchar(i);
        i++;
    }
    /* print a new line */
    putchar('\n');

    /* exit program after successful execution */
    return (0);
}

putchar can also take as an argument, the integer value that represents a character constant in ASCII and then print the character constant.

Let's create a program that uses putchar to print all single-digit numbers of base 10 starting from 0 and then a new line.:

#include <stdio.h>
/**
* main - Entry point of the program
* Description: Program will prints all single digit numbers of base
* 10 starting from 0 and then a new line.
* 
* Return: 0 if program successfully executes
*/
int main(void)
{
    /* declare variable */
    int i;

    /* initialize variable */
    i = 48;

    /* loop and print the value stored in the variable then increment it */
    while (i <= 57)
    {
        putchar (i);
        i++;
    }

    /* print a new line */
    putchar('\n');

     /* exit program after successful execution */
    return (0);
}

Similarities between character constants and int types

C treats character constants as small Integers, which means character constants can be used in arithmetic computation. When a character constant appears in an arithmetic computation, C simply uses the Integer value that represents the character constant in the underlying character set (which is most likely ASCII) on the machine executing the program. This opens up the possibility to convert digits represented as strings to integers and vice versa.

Let's create a program that does a simple arithmetic computation with character constants and prints the result of the computations:

#include <stdio.h>
/**
 * main - Entry point of the program
 *
 */
int main(void)
{
    char ch;
    int num;

    /* Convert an integer to its character constant equivalent */
    ch = 9 + '0' 
    /* Convert a character constant to its integer equivalent */
    num = '3' - '0';

    /* print the values
 stored in the variables */
    printf("The value stored in variable 'ch': %c\n", ch);
    printf("The value stored in variable 'num': %d\n", num);

    /* exit program after successful execution */
    return (0);
}

In the above code snippet, the subtraction in the line num = '3' - '0';, is performed to convert the character constant representation of the digit 3 to its decimal integer constant representation. In ASCII encoding, the character constants '0' to '9' are represented by consecutive integer values. The character constant '0' has an ASCII value of 48, '1' has a value of 49, '2' has a value of 50, and so on. Subtracting the ASCII value of '0' (48) from the ASCII value of '3' (51) gives us 51 - 48 = 3, which is the decimal integer constant representation of the digit 3.

In the line ch = 9 + '0' the same principle applies but in reverse. here the addition is performed to convert the decimal integer constant representation of the digit 9 to its corresponding character constant representation. Adding the ASCII integer value of '0' (48) to the integer 9 gives us 48 + 9 = 57, which is the ASCII encoding of the digit 9.

The difference between a char type variable and any int type variable is their respective storage allocation specifications and range of values. char type variables use a lesser storage allocation and accept a smaller range of values.

Also since C allows character constants to be used as integers in arithmetic computation, a char type variable can exist in both signed and unsigned forms.

The following table shows the storage allocation specification and range of values for the different forms of char type variables:

TypeMemory Allocation SpecValue Range
signed char1 Byte-128 to 127
unsigned char1 Byte0 to 255

The C standard doesn't define whether just specifying char in a variable declaration indicates a signed type or an unsigned type. Some compilers treat ordinary char as a signed type while others treat it as an unsigned type... Most of the time it doesn't matter whether char is signed or unsigned.

Do not assume that char is either signed or unsigned by default. If it matters, use signed char or unsigned char instead of just char.

Floating types

A floating type variable will store floating-point constants. float is the keyword that is used to specify the floating type.

Floating point constants are values that have digits after the decimal point. You can omit digits before or after the decimal point, but you can't omit both. Floating point constants can also be expressed in scientific notation.

/* Example of valid floating point constants */
3.
120.8
-.0001
1.7e4 /* scientific notation 1.7 x 10^-4 */

C provides three (3) Floating types keywords, corresponding to different floating point formats:

  • float for single-precision floating-point formats

  • double for double-precision floating-point formats

  • long double for extended-precision floating-point formats

The C standard doesn't state how much precision the float, double, and long double types provide since different computers may store floating-point numbers in different ways. Most modern computers follow the specification in IEEE Standard 754 (also known as IEC 60559).

By default, all floating-point constants are stored as double-precision values by the C compiler. This means for example when a C compiler finds a floating-point constant of 12.34 in your program, it arranges for the number to be stored in memory in the same format as a value stored through a double variable.

If you want a floating-point constant to be stored in memory as a float add f to the end of the constant as you assign it to a float type variable:

12.34f

How to print the value in a Floating type Variable

%f , %e, or %g is the format specifier used to print floating-point constants with printf. The format specifier %g produces the most aesthetically pleasing output because it lets printf decide whether to display the floating-point constant as normal floating-point notation or scientific notation. To print the value in a long double type variable add L to any of the above-mentioned format specifiers.

/**
 * file name: float_type.c
 * main - Entry point of the program. 
 */
#include <stdio.h>

int main(void)
{
    /* variable declarations */
    float fVar;
    double dVar;
    long double LdVar;

    /* variable assignment */
    fVar = 12.34f;
    dVar = 12.34;
    LdVar = 12.341;

    /* print the values stored in the repective variables */
    printf("This is the value stored in a Float type variable: %g\n", fVar);
    printf("This is the value stored in a double type variable: %g\n", dVar);
    printf("This is the value stored in a long double type variable: %Lg\n", LdVar); 

    return (0);
}

Conclusion

Variables and constants are the basic data objects manipulated in a program. Declarations list the variables to be used and state what type they have. Without variables, it is difficult to get much done with your program.

In this article, you have been introduced to the fundamentals of how to declare variables that suit specific needs in your program, how to assign values to variables, and how to print the values stored in different types of variables using C standard library functions and format specifiers.

Thank you for taking the time to read my article. Please click like icon and share the article to your social network, the information in this article might help someone in your network.❤

References

C Library - | Tutorialspoint

The C Book — Keywords and identifiers (gbdirect.co.uk)

C Identifiers | Microsoft Learn

The C Book — Integral types (gbdirect.co.uk)