Category: 03. Variables and Constants

https://cdn3d.iconscout.com/3d/premium/thumb/add-variable-3d-icon-png-download-4652969.png

  • Internal and External Linkage

    In C, linkage is a concept that explains whether or not names or identifiers can refer to the same entity throughout the entire program or a single translation unit. It may sound similar to program scope but it is not so. To understand this concept better, let us dig deeper into the compilation process.

    Before learning the concept of linkage in C, we first need to understand what a translation unit is.

    What is a Translation Unit?

    A translation unit is a file that has source code, header files, and other dependencies. All of these sources are grouped together to form a single translation unit which can then be used by the compiler to produce one single executable objects.

    What is Linkage?

    Assume there is a C program with many source code files. Each source file is compiled sequentially. In the compilation process, the last stage is linking, where multiple machine code files are used to produce an executable object code. It is handled by a program called linker.

    Linkage is a property that describes how variables should be linked by the linker.

    Variable Linkage in C

    In C, the linkage can control whether a variable can be used only in the file where it is declared or it can also be used in other files of the program.

    Linkage tells us if a variable’s name is limited to one file or can be shared across multiple files, helping us manage how variables are connected in a program.

    Types of Linkage in C

    There are two types of Linkage in C −

    • Internal Linkage
    • External Linkage

    Let’s understand these two concepts in detail.

    Internal Linkage

    Internal linkage refers to everything only in the scope of a translation unit. An identifier that implements internal linkage is not accessible outside the translation unit in which it is declared. So, an identifier with internal linkage can be accessed only within the same file where it is declared.

    Internal Linkage is implemented using the static keyword, and its value is stored in RAM (either in the initialized or uninitialized).

    Let’s understand it through the following example. Let’s consider a source file items.c (identifier) −

    #include <stdio.h>// Variable with internal linkage
    Static int items =10;

    The above code implements static linkage on identifier items.

    Let’s consider another source file display.c which is located in the same translation unit.

    #include <stdio.h>#include "items.c"intmain(){printf("total item is: %d", items);return0;}

    Following is the output of the above code after executing the display.c −

    total item is: 10
    

    Suppose display.c is located in a different translation unit (meaning we are not including the items.c using #include). Then it gives the error “Each undeclared identifier is reported only once for each function it appears in.”

    External Linkage

    An identifier implementing external linkage exists beyond a particular translation unit and is accessible throughout the entire program, which is the combination of all translation units (or object files). It is the default linkage for globally scoped variables (without static) and functions.

    The extern keyword is used to implement external linkage. When we use the ‘extern’ keyword, we tell the compiler that the variable is defined in another file. Thus, the declaration of an external identifier doesn’t use any memory space.

    Extern identifiers are stored in RAM’s data (initialized/uninitialized) or code (text) segments.

    Let’s take an example to understand the concept better. Consider a source file: items.c (identifier). In the internal linkage we used the static keyword; here we create a source file without the extern keyword.

    #include <stdio.h>// Variable with internal linkageint items =10;

    As the variable items is declared globally, it is accessible to all the translational units. Now, consider the file display.c is in the different translational unit −

    #include <stdio.h>// tells the compiler that the variable// have external linkageexternint items;intmain(){printf("total item is: %d", items);return0;}

    Here is the output of the above code after executing the display.c −

    total item is: 10
    

    Conclusion

    Linkage in C defines whether variables and functions can be accessed only within a single file or across multiple files of a program. By using internal linkage (static) and external linkage (extern), we can control the scope and accessibility of identifiers. It ensures better program structure and modularity.

  • Const Qualifier

    The const qualifier in C is used to declare variables whose values should remain fixed throughout the program. It is used to declare a variable as a constant, whose value cannot be changed after initialization. Once a variable is declared as const, it becomes readonly, and the compiler prevents any accidental modifications.

    What is the Use of const Qualifier in C?

    Using the const qualifier, programmers can improve the safety, readability, and maintainability of their program codes. It helps programmers make their intentions clear by signaling which values should stay fixed throughout the program.

    For example, constants like mathematical values (PI, e, etc.), array sizes, or configuration values should not be altered at runtime. By declaring them as const, we protect these values from accidental changes.

    Example of Using const Qualifier in C

    Take a look at the following example. Here, we have used const to ensure the value of PI remains fixed throughout the program.

    #include <stdio.h>intmain(){// Defining a constant variableconstint PI =3.14;printf("Value of PI: %d\n", PI);// Trying to modify PI will cause an error// PI = 10; // Not allowed, PI is read-onlyreturn0;}

    Observe how the const variable (PI) prevents unwanted changes to constants. When we run this code, it will produce the following output –

    Value of PI: 3
    

    Using const Qualifer in Different Contexts

    Apart from defining constant variables, the const qualifier can also be used in different contexts to provide various behaviors. Here are some different scenarios where we can use the const qualifier:

    • Pointer to Constant
    • Constant pointer to variable
    • Constant pointer to constant

    In the subsequent sections of this chapter, we will cover each of these topics in detail.

    Pointer to Constant

    As its name suggests, a “pointer to a constant” points to a constant value which cannot be modified. However, the pointer itself can change because it’s a variable and it can point somewhere else.

    Let’s see its syntax −

    const type* name;

    Or,

    typeconst* name;

    A “pointer to a constant” means the pointer can point to different variables, but the value of the object being pointed to cannot be changed through this pointer.

    • The pointer itself is stored in the read-write area (such as a stack in this case).
    • The object being pointed to may reside either in the read-only area or in the read-write area.
    • Even if the object is stored in the writable location, you cannot alter its value using this pointer, since the pointer treats the object as a constant.

    Example: Pointer to Constant with an Integer

    In this example, we are using a pointer to constant with an integer −

    #include <stdio.h>intmain(){int x =10, y =20;// Pointer to constant integerconstint*ptr =&x;printf("Value pointed by ptr: %d\n",*ptr);// *ptr = 15;  // Not allowed (cannot change value through ptr)
       ptr =&y;// allowed (pointer can point to another variable)printf("Value pointed by ptr: %d\n",*ptr);return0;}

    Notice that we cannot change the value of the variable “x” though the pointer. On executing the code, you will get the following output −

    Value pointed by ptr: 10
    Value pointed by ptr: 20
    

    Example: Pointer to Constant with an Array

    In this example, we are using a pointer to constant with an array −

    #include <stdio.h>intmain(){int arr[]={1,2,3};// Pointer to constant integer (array base address)constint*ptr = arr;printf("First element: %d\n",*ptr);// *ptr = 5;  	// not allowed (cannot change array element via ptr)
       ptr++;// Allowed (can move pointer to next element)printf("Second element: %d\n",*ptr);return0;}

    When we run the above program, it will produce the following output −

    First element: 1
    Second element: 2
    

    Here, we cannot modify an element of the array using a pointer in a regular way. Notice the following commented line in the program −

    // *ptr = 5;

    Constant Pointer to Variable

    A constant pointer means the pointer itself is a constant variable; it means we cannot alter the pointer to point to another variable. Its syntax is as follows −

    int*const ptr;

    Let’s understand this concept using the following example. It demonstrates that the value of the object pointed by the constant pointer can be changed but the pointer cannot point to another variable.

    #include <stdio.h>intmain(void){int i =10;int j =20;/* constant pointer to integer */int*const ptr =&i;printf("ptr: %d\n",*ptr);*ptr =100;// change the pointer valueprintf("ptr: %d\n",*ptr);// ptr = &j; // pointer cannot point to another variablereturn0;}

    Here is the output of the above code −

    ptr: 10
    ptr: 100
    

    Notice the commented line −

    // ptr = &j; 	// pointer cannot point to another variable

    If we uncomment this line and run the code, then it will produce an error. Why? It’s because the constant pointer (ptr) is pointing to “i” and it cannot point to another variable “j”.

    Constant Pointer to Constant

    A “constant pointer to constant” is a type of constant pointer that points to a constant variable, which means we cannot alter the value pointed to by the pointer, also the pointer cannot point to other variables. Let’s see its syntax −

    constint*const ptr;

    The following example shows how you can use a “constant pointer to constant” in a C program −

    #include <stdio.h>intmain(void){int i =10;int j =20;/* constant pointer to constant integer */constint*const ptr =&i;printf("ptr: %d\n",*ptr);
    
       ptr =&j;// error*ptr =50;// errorreturn0;}

    Now, let’s run this code and check its output −

    ERROR!
    main.c: In function 'main':
    main.c:10:9: error: assignment of read-only variable 'ptr'
       10 |     ptr = &j; // error
    
      |         ^
    ERROR! main.c:11:10: error: assignment of read-only location '*(const int *)ptr' 11 | *ptr = 50; // error

    In the code, observe that we are trying to modify the pointer as well as the value it is point to. And, a “constant pointer to constant” denies both the actions, which is why we are getting an error in both the cases.

    Advantages of Const Qualifier

    Following are the advantages of using a const qualifier in a C program −

    • Helps prevent unintentional changes to variables
    • Reduces the risk of bugs and errors in your code; makes the code more reliable
    • Prevents functions from modifying arguments that should remain unchanged

    By using const qualifiers, we can avoid having to make a copy of their values, which can reduce the memory usage and improve performance.

    Conclusion

    The const qualifier in C is a powerful feature that improves code readability, safety, and reliability. It helps prevent accidental modification by clearly defining which values should not change. It supports compiler optimizations and makes programs error-free and easy to maintain.

  • Constants

    constant in C is a user-assigned name to a location in the memory, whose value cannot be modified once declared. This is in contrast to a variable in C, which is also a named memory location, however whose value may be changed during the course of the code.

    Instead of repeatedly using hard-coded values in a program, it is advised to define a constant and use it. Constants in a C program are usually employed to refer to a value which may be error-prone if it is to be used repetitively in the program, at the same time its value is not likely to change.

    For example, the value of mathematical constant PI is a high-precision floating point number 3.14159265359, and if it is likely to appear frequently, it is declared as a constant and used by its name.

    We may consider a constant in C as a read-only variable, as its value can only be used subsequently but cannot be modified.

    You can declare a constant in C program with either of the following two ways −

    • Using the const Keyword
    • Using the #define Directive

    Let’s understand these two ways of declaring a constant in C.

    Defining a Constant Using the const Keyword

    The syntax of declaring a constant is as follows −

    const type NAME = val;

    For example,

    constfloat PI =3.14159265359;

    We can now use PI in any expression, as we would use any variable.

    Example

    Take a look at the following example −

    #include <stdio.h>intmain(){constfloat PI =3.14159265359;float radius =5;float area = PI*radius*radius;printf("area: %f", area);return0;}

    Output

    On running this code, you will get following output −

    area: 78.539818
    

    However, changing the value of a constant is prohibited. The following statement gives a compiler error −

    constfloat PI =3.14159265359;
    PI=22/7;

    Here, you will get the following error message −

    error: assignment of read-only variable 'PI'

    In case of variables, you can declare a variable and assign it a value later on in the program, however you cannot follow the same process in case of a constant.

    You can declare a constant in C without assigning it a value. But when you try to assign it a value afterwords, then the compiler will throw an error.

    constfloat PI;
    PI =3.14159265359;

    Here, you will get this error message −

    error: assignment of read-only variable 'PI'

    Note: “sizeof” returns “size_t”. The type of unsigned integer of “size_t” can vary depending on platform. And, it may not be long unsigned int everywhere. In such cases, we use “%zu” for the format string instead of “%d”.

    This is because the compiler assigns a random garbage value at the time of declaration, which you cannot change afterwards. Hence, you must declare and initialize the constant at once.

    constant in C can be of any of the data types including primary data types such as int, float, char, and derived data types such as struct.

    Defining a Constant Using the #define Directive

    Using the #define preprocessor directive is also an effective method to define a constant. Here is its syntax −

    #define name = value

    Take a look at the following example:

    #define PI = 3.14159265359

    Although the constant so defined can also be used in any expression (just as the one with the const keyword), there is a difference between the two.

    The constants created by the #define directive are not handled by the compiler. Instead, they behave as macros, whose values are substituted at the runtime.

    The other notable difference is that you need not mention the data type of the value to be assigned to the constant when using the #define directive.

    Example: Define a Constant Using the #define

    Given below is another example of a constant defined using the #define directive −

    #include <stdio.h>#define LENGTH 10   #define WIDTH  5#define NEWLINE '\n'intmain(){int area;  
       area = LENGTH * WIDTH;printf("length: %d width: %d", LENGTH, WIDTH);printf("%c", NEWLINE);printf("value of area : %d", area);return0;}

    Output

    Upon running this code, you will get the following output −

    length: 10 width: 5
    value of area : 50
    

    Since a constant is also an identifier in C, it follows all the rules of forming an identifier. Identifiers in C are case-sensitive. Hence the convention followed while defining a constant in C is that it uses uppercase characters, however it is not mandatory.

    Changing the Value of a Constant

    By definition, constants are immutable. Why would you change the value of a constant in the first place? We use constants whose value is supposed to remain unchanged. To be able to change the value, we would define a variable rather than a constant.

    We have seen that it is not possible to assign a new value to an already defined constant. However, there exists a workaround with which a new value can be assigned to a constant.

    The technique uses the concept of pointers in C. A Pointer is a variable that stores the address of another variable. Since it is a variable, its value can be changed. Moreover, this change reflects in the original variable.

    Example: Change the Value of a Constant

    The following code demonstrates how to change the value of a constant with the pointer mechanism −

    #include <stdio.h>intmain(){constint x =10;printf("Initial Value of Constant: %d\n", x);// y is a pointer to constant xint* y =&x;// assign new value*y =100;printf("Value of Constant after change: %d", x);return0;}

    Output

    On executing this code, you will get the following output −

    Initial Value of Constant: 10
    Value of Constant after change: 100
    

    Note that this technique is effective only for those constants which are defined using the const qualifier.

    If you have defined your constant using the #define directive, then you cannot apply this process. This is because the pointer has a data type, and it must be of the same type whose address is to be stored. On the other hand, the constant defined using the #define directive doesn’t really have a data type. It is in fact a macro whose value is substituted during the runtime.

  • Variables

    variable is nothing but a name given to a storage area that our programs can manipulate. Each variable in C has a specific type, which determines the size and layout of the variable’s memory; the range of values that can be stored within that memory; and the set of operations that can be applied to the variable.

    Why Do We Use Variables in C?

    A variable in C is a user-assigned name to a certain location in the computer’s memory, which is a collection of a large number of randomly accessible locations capable of holding a single bit. Each location in the memory is identified by a unique address, expressed in binary (or Hexa-decimal for convenience) format.

    Since it is extremely cumbersome to store and process the data in the memory by referring to their locations in binary form, high-level languages such as C let the locations be identified by user-defined names or variables.

    Instead of identifying a free memory location and assigning it a value, you can find a suitable mnemonic identifier and assign it a value. The C compiler will choose an appropriate location and bind it to the identifier specified by you.

    Naming Conventions of C Variables

    The name of the variable must start with an alphabet (upper or lowercase) or an underscore (_). It may consist of alphabets (upper or lowercase), digits, and underscore characters. No other characters can be a part of the name of a variable in C.

    Variable names in C are case-sensitive. For example, “age” is not the same as “AGE”.

    The ANSI standard recognizes a length of 31 characters for a variable name. Although you can choose a name with more characters, only the first 31 will be recognized. Using a descriptive name for a variable, that reflects the value it intends to store is considered to be a good practice. Avoid using very short variable names that might confuse you.

    C is a statically typed language. Hence, the data type of the variable must be mentioned in the declaration before its name. A variable may be declared inside a function (local variable) or globally. More than one variable of the same type may be declared in a single statement.

    Example

    Based on the above set of rules and conventions, here are some valid and invalid variable names:

    int _num =5;// valid integer variablefloat marks =55.50;// valid float variablechar choice ='0';// valid char variable// invalid variable name// cannot use "-"int sub-1=35;//invalid; must have data type
    avg =50;// invalid; name can be used for // declaration only once in a functionint choice =0;// Valid integer nameint sal_of_employee =20000;// Valid because all are of same typeint phy, che, maths;// error because variables of // different types in same statementint sal,float tax;

    In C, variables can store data belonging to any of the types it recognizes. Hence there are as many number of types of variables as the number of data types in C.

    Sr.NoType & Description
    1charTypically a single octet(one byte). It is an integer type.
    2intThe most natural size of integer for the machine.
    3floatA single-precision floating point value.
    4doubleA double-precision floating point value.
    5voidRepresents the absence of type.

    C programming language also allows to define various other types of variables such as Enumeration type, Pointer type, Array type, Structure type, Union type, etc. For this chapter, let us study only basic variable types.

    Variable Definition in C

    A variable definition tells the compiler where and how much storage to create for the variable. A variable definition specifies a data type and contains a list of one or more variables of that type as follows −

    type variable_list;

    Here, type must be a valid C data type including char, w_char, int, float, double, bool, or any user-defined object; and variable_list may consist of one or more identifier names separated by commas.

    Some valid variable declarations are shown here −

    int    i, j, k;char   c, ch;float  f, salary;double d;

    The line int i, j, k; declares and defines the variables i, j, and k; which instruct the compiler to create variables named i, j and k of type int.

    Variables can be initialized (assigned an initial value) in their declaration. The initializer consists of an equal sign followed by a constant expression as follows −

    type variable_name = value;

    Example: Variable Definition and Initialization

    Take a look at the following examples:

    // declaration of d and f
    extern int d = 3, f = 5;     
    
    // definition and initializing d and f
    int d = 3, f = 5;            
    
    // definition and initializes z 
    byte z = 22;
    
    // the variable x has the value 'x'
    char x = 'x';               
    

    For definition without an initializer: variables with static storage duration are implicitly initialized with NULL (all bytes have the value 0); the initial value of all other variables are undefined.

    Variable Declaration in C

    As per the ANSI C standard, all the variables must be declared in the beginning. Variable declaration after the first processing statement is not allowed. Although the C99 and C11 standard revisions have removed this stipulation, it is still considered a good programming practice. You can declare a variable to be assigned a value later in the code, or you can initialize it at the time of declaration.

    Example: Variable Declaration

    // declaration with initialization
    int x = 10; 
    
    // declare first and assign later       
    int y;             
    y = 20;
    
    // define and initialize two variables
    int d = 3, f = 5;
    
    // the variable x has the value 'x'
    char x = 'x';      
    

    Once a variable of a certain type is declared, it cannot be assigned a value of any other type. In such a case the C compiler reports a type mismatch error.

    A variable declaration provides assurance to the compiler that there exists a variable with the given type and name so that the compiler can proceed with further compilation without requiring complete detail about the variable. A variable definition has its meaning at the time of compilation only, the compiler needs actual variable definition at the time of linking the program.

    A variable declaration is useful when you are using multiple files and you define your variable in one of the files which will be available at the time of linking the program. You will use the keyword “extern” to declare a variable at any place. Though you can declare a variable multiple times in your C program, it can be defined only once in a file, a function, or a block of code.

    Example

    Try the following example, where variables have been declared at the top, but they have been defined and initialized inside the main function −

    #include <stdio.h>// Variable declaration:externint a, b;externint c;externfloat f;intmain(){/* variable definition: */int a, b;int c;float f;/* actual initialization */
       a =10;
       b =20;
      
       c = a + b;printf("value of c : %d \n", c);
    
       f =70.0/3.0;printf("value of f : %f \n", f);return0;}

    Output

    When the above code is compiled and executed, it produces the following result:

    value of c : 30
    value of f : 23.333334
    

    The same concept applies on function declaration where you provide a function name at the time of its declaration and its actual definition can be given anywhere else. For example −

    // function declarationintfunc();intmain(){// function callint i =func();}// function definitionintfunc(){return0;}

    Lvalues and Rvalues in C

    There are two kinds of expressions in C:

    • lvalue expressions
    • rvalue expressions

    Lvalue Expressions in C

    Expressions that refer to a memory location are called “lvalue” expressions. An lvalue may appear as either the left-hand or right-hand side of an assignment.

    Variables in C are lvalues and so they may appear on the left-hand side of an assignment.

    Rvalue Expressions in C

    The term “rvalue” refers to a data value that is stored at some address in memory. An “rvalue” is an expression that cannot have a value assigned to it which means an rvalue may appear on the right-hand side but not on the left-hand side of an assignment.

    Numeric literals are rvalues and so they may not be assigned and cannot appear on the left-hand side.

    Take a look at the following valid and invalid statements:

    // valid statement
    int g = 20; 
    
    // invalid statement 
    // it would generate compile-time error
    10 = 20;    
    

    Variables in C can be classified based on the following parameters:

    • Data types − int, float, char or struct types.
    • Scope − global or local variables.
    • Storage type − automatic, static, register or extern.

    We shall learn about local and global types and storage types later in this tutorial.