Completion status: this resource has reached a high level of completion.

Objective

edit

In this lesson, you will learn about:

  • Introduction to variables in C programming
  • Primitive types and qualifier
  • Storage class
  • Typecasting
  • Creating your own storage type with enum and typedef

Introduction

edit

The most fundamental concept in C (and most other programming languages) is the variable. A variable is like a container. You can store things in it for later use, particularly numbers. The concept of a variable is borrowed from mathematics. A statement such as

x = 1 // This is a variable statement in Math, not in C programming

stores the value 1 in the variable x. In C, it is much the same. To assign a value to a variable, you must declare it. You declare a variable by specifying it's data type and name, like so:

data_type variable_name;

The programmer may combine declaration and assignment, which is known as initialization. Here is the variable x being declared and initialized:

int x = 1;

Every variable has a name. Whenever you want to use a variable, you specify what variable you want to use by using its name. The set of rules, ways and syntax we used to name a variable, as well as other programming terminology like function, source files,... is known as naming convention.

In C, a variable name can be any combination of letters, digits, and underscores (the _ character) you want, so long as the first character is not a digit.

Picking a good name makes your program much easier to read and understand. A good variable name describes exactly what the data it holds is. For example, if a variable stores the current temperature, you can name it current_temperature. If it stores the score of a video game, name it player_score.

However, putting the full words for a variable, like current_temperature, can make the variable's name very long when the situation involved with the variable is complicated. That's why we are expected to use abbreviations in such cases. For example, current_temperature could be abbreviated to cur_tem. However, you should also be careful with abbreviations, as they might save you a bit of typing, but it can remove clarity from your code in some ways. You should really only use abbreviations if there is only 1 possible interpretation for it, and if it’s obvious what that interpretation is by the context of the program. For example when using the abbreviated word temp, does it mean temperature or temporary ? Things can even get more confusing when your variable needs to store the temporary temperature, should it be temp_tem ? In those situations, it's recommended to use full names, it’s not that many more keys.

Variable naming convention provides one more guideline with the syntax to name the variables. It involves the use of capitalized and uncapitalized letters, as well as the underscore symbol. This results in three naming conventions: snake_case, camelCase, and PascalCase.

A few examples of snake_case: current_temperature, temp_variable, this_is_a_long_name,...

A few examples of the camelCase: currentTemperature, tempVariable, thisIsALongName,...

A few examples of the PascalCase: CurrentTemperature, TempVariable, ThisIsALongName,...

All those ways are fine, it's just a matter of preference/readability and also depends on the coding standard or the naming convention expected in your project.

Data Types and Qualifiers

edit

Data types and the name of a variable combine to completely describe the variable, where the variable's data type describes what kind of data it holds.

C is strongly typed, which means that variables must be explicitly assigned a data type. Data types indicate the type of data a variable can hold. When a variable is defined, a memory location will be assigned to the newly defined variable and it will also define the type of data that memory location will hold.

C has 4 basic data types:

  • int - an integer; reflects size of integers on host machine.
  • float - single-precision floating point.
  • double - double-precision floating point.
  • char - character, a single byte.

In addition to basic data types C also defines certain qualifiers to these data types. Qualifiers are used to make variable declaration more specific to variable uses.

Qualifiers available in the C language are:

  • short (applied to integers).
  • long (applied to integers).
  • signed (applied to char, or any integer).
  • unsigned (applied to char, or any integer).

Using these qualifiers, basic data types can be flavoured in many ways as shown in the table below. Note that the values given are acceptable minimum magnitudes defined by the C Standard - each implementation will define values greater or equal in magnitude.

Data Type Bits Range Begin Range End
char 8 -128 127
unsigned char 8 0 255
short int 16 -32768 +32767
unsigned short int 16 0 65,535
int 16 -32,768 32,767
unsigned int 16 0 65,535
long int 32 -2,147,483,648 2,147,483,647
unsigned long int 32 0 4,294,967,295
float 32 1e-37 1e+37
double 32 1e-37 1e+37
long double 32 1e-37 1e+37

Note: If a variable goes beyond its range, like defining an int in a 16-bit system to be larger than 32,767, you'll end up with a negative number while you expect a positive one.

C type qualifiers add special properties to the variables being declared. C provides the following two keywords:

  • const - the const qualifier is used to indicate the variable value can not be changed after its initialization. As an alternative, the #define preprocessor directive can be used to define constant values and these will consume no memory space.
  • volatile - the volatile qualifier indicates that variable values can be changed without the current program's knowledge and so the compiler should not optimize away uses of that variable.

Note:

  • A standard conforming compiler can ignore these qualifiers; nevertheless, it must issue a message when an attempt is made to alter an object declared as const.
  • The variable's data type can be treated like units in physics. The programmer has to be as careful as possible when choosing the data type. Take temperature- how are we measuring temperature ? Celsius ? Farenheit ? Kelvin ? NASA lost the Mars Rover because one group of programmers used length in feet, and the other used meters.

Storage Classes

edit

Storage classes define the longevity and scope of variables and functions. There are two types of storage classes: automatic and static. There are several storage class specifiers:

  • auto: this is the default storage class specifier for variables defined inside a function. auto can be used only inside functions. Variables declared auto will automatically have their storage allocated on entry to a function and deallocated when the function exits. auto variables contain garbage until explicitly initialised. Use of auto is archaic and there is no need to use it.
  • register: this storage class specifier can be used to indicate to the compiler that the variable will be used frequently and it should be placed in a CPU register, if possible. However, defining a variable with this specifier does not guarantee that it will be stored in a CPU register (a compiler can ignore it) and modern compilers should allocate registers much better than a programmer can.
  • static - this storage class specifier is used for two main purposes as given below
    • For local variables: when a local variable is defined with the static storage class specifier, it is initialized to 0 by default in the absence of an explicit initialisation value. A static variable in a function definition will retain its value across multiple calls to the function.
    • For global variables and functions: When a global variable or a function is defined as static, its scope is reduced to the C program file in which it is defined. Thus even though it is a global variable, it can not be accessed from another module (it is said to have internal linkage).
  • extern - this storage class specifier is used to declare a variable or function that is defined in another module. For function declarations, the storage class extern can be omitted.

Storage classes can be remembered easily by remembering mnemonic RASE, i.e., register, auto, static, and extern.

Example: the arithmetic mean of two numbers

edit

The following source code is compiled with GCC:

#include <stdio.h>

void main(void)
{
     int a, b;
     float avg;  // data type

     printf("Enter the a:");
     scanf("%d", &a);

     printf("Enter the b:");
     scanf("%d", &b);

     avg = (a + b) / 2;  // expression

     printf("%f", avg);
     getch();  // getchar() may work instead

     return 0;
}

Expressions

edit

To manipulate the variable, ‘a’, declared and defined in the previous section, an expression is needed. By definition, an expression, in C, is an interpreted combination of values, variables, operators or functions. There are a number of operators available including addition, ‘+’, subtraction, ‘-‘, division ‘/’, and multiplication ‘*’. In an expression, the variable name on the left side of the assignment operator represents the area of memory that stores interpreted results. Variable and constants on the right side of the assignment operator are interpreted to determine a result prior to assignment. Note these definitions and declarations:

int a;
int b;

a = 0;
b = 8;

What follows is a statement which manipulates storage in memory (an expression becomes a statement when it is followed by a semicolon):

a = b + 24;

In this statement, the constant ‘24’, is added to the value stored in the variable ‘b’. The result of that calculation, then, is assigned to a memory location, symbolically represented by the variable ‘a’. After the interpretation of the statement, the variable 'a' is assigned the value 32.

Type conversion

edit

Type conversion is simply conversion from one type to another one. There are two types of type conversion:

  • Implicit Type Conversion, also known as automatic type conversion.
  • Explicit Type Conversion: This process is also called type casting and it is user-defined. The user can typecast the result to make it of a particular data type, i.e changing an expression from one data type to another.

Implicit type conversion

edit
  • Implicit Type Conversion is done by the compiler on its own, without any external trigger from the user. It generally takes place when in an expression more than one data type is present. In such condition, type conversion (also called type promotion) takes place to avoid loss of data.
  • It is possible for implicit conversions to lose information, signs can be lost (when signed is implicitly converted to unsigned), and overflow can occur (when long is implicitly converted to float).

C has the concept of data types:

int x = 10; // integer x
char y = 'a'; // character y

// y implicitly converted to int. ASCII
// value of 'a' is 97
x = x + y;

// x is implicitly converted to float
float z = x + 1.0;

printf("x = %d\n", x);//107
printf("y = %c\n", y);//a
printf("z = %f\n", z);//108.0000

Implicit casting for float to int:

int b = 3.1;
printf("%d \n", b); //3
printf("%f \n", b); //0.0

Implicit casting in printf() for float to int:

float c = 3.1;
printf("%d \n", c); //-1073741824

Implicit casting during calculation:

int a = (998/1024)*100; // a = 0

Explicit type conversion

edit

Explicit Type Conversion, which is user-defined, allows user to typecast the result to make it of a particular data type.

Syntax:

(type) expression

double to int

edit
double x = 1.2;

// Explicit conversion from double to int
int sum = (int)x + 1;

printf("sum = %d\n", sum);//2

float to int

edit
int a = (int) 12.9;
printf("%d \n", a);	//12
printf("%f \n", a); //0

int to float

edit
float a = (float) 3;
printf("%d \n", a);	//0
printf("%f \n", a); //3.000

Creating your own storage types

edit

Beside using the primitive data type, C allows to create your own storage data type. There are 2 ways to do this: typedefs and enums

enum

edit

enum is short for enumeration. An enum allows you to use a different name for one of the built-in storage types, and simultaneously defines what the legal values for the enum are.

Take this example for enum:

enum {value0 = 10, value1, value2, value3, value4 = 10, value5 = 16, value6};
enum {value7, value8, value9, value10, value11=1, value12=12, value13};

printf("%d, %d, %d, %d, %d, %d, %d\n", value0 , value1, value2, value3, value4, value5, value6);//10, 11, 12, 13, 10, 16, 17
printf("%d, %d, %d, %d, %d, %d, %d\n", value7 , value8, value9, value10, value11, value12, value13);//0, 1, 2, 3, 1, 12, 13

Note:

  • After declaring the enum, all of its variable (value0, value1, ...) are all constant and unique.
  • It can be seen from the example that if you don't arbitrarily define the value for each enum member, their value is 1 greater than the last one, and the first name takes a value of 0. So if you don't define any, they count up 0,1,2,3,4,...

object of enum

enum enum_object {value0 = 10, value1, value2, value3, value4 = 10, value5 = 16, value6};
enum enum_object enum_object_1;

printf("%d\n", enum_object_1);//0; enum_object_1 value now is not set
enum_object_1 = value0;
printf("%d\n", enum_object_1);//10
enum_object_1 = value1;
printf("%d\n", enum_object_1);//11

typedef

edit

typedef is short for type definition. A typedef basicly allows you to use a different name for one of the built-in storage types. You can declare a typedef by using:

typedef storage_type real_type;

Take this example for typedef:

#include <stdio.h>

typedef int integer;

integer displayFunction();

int main() {
	integer a = 19;
	printf("%d\n", a); //19
    printf("%d\n", displayFunction()); //4
}

integer displayFunction(){
    return 4;
}

Naming the variable

edit

As the final part of this lecture, we're going to review the naming for the variable again.

Since naming is by far the most important thing to take away from this lesson, Choosing good names is one of the most important thing to do when writing code. It will do more for making your code easy to read and understand than almost anything else. It's important to get in good habits now, too many students who first learn to code and use x and y for all their variables. Lets use an example. Which of these give you the most information:

int x;
int myHeight;
inches x;
inches myHeight;

The first tells you nothing. The second tells you what's being stored, but not how its represented (inches? centimeters? microns?). The third tells you how something is being stored, but not what it is (you can use typedef to define that inches type, i.e typedef int inches). The final one tells us not only what's being stored, but also how it's represented. Imagine a large program with 10,000 variables, you might prefer the the fourth way, while in a small program, the second way can be used.

External resources

edit

YouTube: Learn C Programming Tutorial 1.11 Math Operators

Assignments

edit
  • What data type(s) could you use if you were creating a program that stored monetary sums (Dollars, Euros, etc.)?
  • What data type could you use to store if you wanted someone to type their name into your program?
  • What data type(s) could you need if you wanted to count from 0 to 100?
  • Compiler dependent: Modify the "arithmetic mean" example program in order to get the right result even if a and b differ by an odd number (e.g. a=1 and b=2).
  • Variables quiz