C Programming Language Tutorial - Part 2: Functions, Data Types, and Control Flow

c programming
data type
control flow
function
recursion

This page delves into the structure of C functions, compilation models, data types, operators, control statements, functions, storage classes, scope, and recursion.

C Function Structure

Each C function typically includes arguments, a return statement, a function name, and input/output statements (using functions like scanf for input and printf for output) to interact with the user and display results.

Compilation

Compiling Code

Compile code : gcc helloworld.c
Output created : a.out
Run Program : ./a.out

Makefile Example

helloworld.out : helloworld.o
	gcc helloworld.o -o helloworld.out

helloworld.o : helloworld.c
	gcc -c helloworld.c

Compilation Models

  • Preprocessor: Resolves all preprocessor directives, such as #include, #define macros, and #ifdef.
  • Compiler: Converts the preprocessed text into object files. These files may contain unresolved references to other object files.
  • Linker: Resolves all inter-object references. If any references are missing or incorrect, the linker will produce an error. Otherwise, it creates the final executable binary.
  • Loader: Loads the executable program into RAM and begins execution at the main() function.

c function Image alt: c function

Data Types

We will explore data types, operators, expressions, control flow, and functions in detail with illustrative examples. Instructions operate on data of a specific size. Data is composed of bits; eight bits form a byte. Programs must know the type of data they are processing, including how it is organized and its size. This is determined by the data types supported by the language. A “word” represents the processor’s default data size, currently 64 bits in many modern processors.

Here’s a review of basic C data types:

  • Characters (char): A single byte, typically used to store a character using ASCII encoding.
  • Integer (int): Typically 4 bytes, representing a whole number. It has a data range from 0 to 231-1 for signed integers or 0 to 232-1 for unsigned.
  • Floating point (float): Typically 4 bytes. Interpretation is governed by the IEEE 754 standard for single-precision floating-point numbers.
  • Long (long): At least 4 bytes, but may be larger. On 32-bit systems, it’s often the same size as int.
  • Enumeration (enum): A user-defined data type representing a set of named integer constants. For example: enum Color { red, blue, black, yellow };

Overflow of Data Types

Overflow occurs when the result of an operation exceeds the maximum value that a data type can store. This often results in the setting of status register bits.

Example:

unsigned int x = 2123456789;
unsigned int y = 3123456789;
unsigned int z;
z = x + y;

In this case, z will likely be 951,946,282 (due to overflow), not the mathematically expected 5,246,913,578. While compilers may issue warnings, especially with constants, it’s crucial to understand the limitations of data types.

Type Conversion and Casting

The C compiler follows specific rules for automatic type conversions:

  • char and short operands are typically converted to int.
  • Lower data types are promoted to higher data types, and the result is of the higher type.
  • Conversions between signed and unsigned types can sometimes produce unexpected results.

Example:

float f;
double d;
long l;
int i;
short s;

d + f  // f will be converted to double
i / s  // s will be converted to int
l / i  // i is converted to long; long result

Explicit Conversion (Type Casting)

The general form of type casting is: (type_name) expression.

It’s generally better to use explicit casts than to rely solely on automatic conversions.

Example:

float c = (float)9 / 5 * ( f-32 ); // float to int conversion causes truncation of fractional part

Note:

  • float to int conversion truncates the fractional part.
  • double to float conversion rounds digits.
  • long int to int may drop higher-order bits.

Operators

C provides a variety of operators, including:

  • Arithmetic operators
  • Unary operators
  • Binary operators
  • Comparison/relational operators
  • Logical operators
  • Compound assignment operators
  • Member and pointer operators
  • Other operators

Arithmetic Operators

C has unary and binary arithmetic operators:

  • Unary operators: Operate on a single operand (e.g., -x, ++i).
  • Binary operators: Operate on two operands (e.g., x + y, a * b).

Pre and Post Increment

++i (pre-increment) and i++ (post-increment) both increment the value of i. However, they differ in when the increment happens relative to the expression’s evaluation.

Consider the example below:

int a=9;
printf("%d",a++);
printf("%d",a);

Output:

9, 10
c
int a = 9;
printf("%d",++a);
printf("%d",a);

Output:

10, 10

a++ returns the current value of a and then increments it. ++a increments a before returning the value.

Control Statements

C offers selection and repetition control statements.

Selection Statements

  • if, if-else
  • switch
  • Conditional operator (ternary operator: condition ? value_if_true : value_if_false)

Repetitions

  • while, do-while
  • for
  • Nested loops
  • break and continue

break and continue Examples

int a = 10;
while(a>=0) {
    printf("\nValue of a = %d",a);
    a--;
    if(a==5)
        break;
}

Output:

Value of a = 10
Value of a = 9
Value of a = 8
Value of a = 7
Value of a = 6
c
int a = 6;
while(a>= 0 ) {
    a--;
    if(a==3)
        continue;
    printf("\nValue of a = %d",a);
}

Output:

Value of a = 5
Value of a = 4
Value of a = 2
Value of a = 1
Value of a = 0

break and continue always operate on the innermost loop or switch case. Other conditions are ignored.

C Function Example

#include <stdio.h>

int maximum(int a, int b);

int main() {
    int a,b;
    int result;

    printf("Input 2 integers \n");
    scanf("%d %d",&a, &b);

    result = maximum(a,b);
    printf("max is %d\n",result);

    return 0;
}

int maximum(int a, int b) {
    return (a>b?a:b);
}

Storage Classes

  • Storage class specifiers determine:

    • Storage duration: How long an object exists in memory.
    • Scope: Where the object can be referenced in the program.
  • Automatic storage:

    • Object is created and destroyed within its block.
    • auto: Default for local variables. auto double x, y;
    • register: Suggests storing the variable in a high-speed register (compiler may ignore). Can only be used for automatic variables: register int counter = 1;
  • Static storage:

    • Variables exist for the entire program execution.
    • Default value is zero.
    • static: Local variables defined in functions. Keep their value after the function ends, but are only known within their function.
    • extern: Default for global variables and functions. Known in any function.

Scope

  • File scope:

    • Identifier is defined outside any function and known in all functions within the file.
    • Used for global variables, function definitions, and function prototypes.
  • Function scope:

    • Can only be referenced inside a function body.
  • Block scope:

    • Identifier declared inside a block (code within curly braces {}).
    • Block scope begins at the declaration and ends at the right brace.
    • Used for variables and function parameters (which are local variables of the function).
    • Outer blocks are “hidden” from inner blocks if a variable with the same name exists in the inner block.
  • Function prototype scope:

    • Used for identifiers in the parameter list of a function prototype.
#include <stdio.h>

int x = 1; // global scope

void a(void);
void b(void);
void c(void);

int main() {
    int x = 5;
    printf("outer loop:x is %d\n",x);
    {
        int x = 7;
        printf("inner loop:x is %d\n",x);
    }
    a();
    b();
    c();
    a();
    b();
    c();
    return 0;
}

void a() {
    int x = 9;
    printf("auto x is %d\n",x);
    x++;
    printf("auto x is %d\n",x);
}

void b() {
    static int x = 11;
    printf("static x is %d\n",x);
    x++;
    printf("static x is %d\n",x);
}

void c() {
    printf("global x is %d\n",x);
    x++;
    printf("global x is %d\n",x);
}

Output of example above:

outer loop x is 5
Inner loop x is 7
auto x is 9
auto x is 10
static x is 11
static x is 12
global x is 1
global x is 2
auto x is 9
auto x is 10
static x is 12
static x is 13
global x is 2
global x is 3

Recursion

  • Recursive functions: Functions that call themselves.

  • Recursion works by breaking a problem down into:

    • A base case (a simple case the function can solve directly).
    • A recursive step (what the function cannot solve directly, but can break down into a smaller, similar subproblem).
  • The function launches a new copy of itself (recursion step) to solve the subproblem.

  • Eventually, the base case is reached and solved. The results are then “plugged in” back up the call stack, ultimately solving the original problem.

EXAMPLE:

int fact(int in)
{
    return in>0? in * fact(in-1): 1;
}



C Programming Language Tutorial - Introduction

A comprehensive introduction to C programming, covering data types, control flow, operators, functions, arrays, pointers, structures, unions, and more.

c programming
data type
control flow

C Programming: Understanding Pointers

Learn about pointers in C programming, including reference and dereferencing operators, pointer concepts, function pointers, and dynamic memory allocation.

c programming
pointer
memory management

C FAQs: Questions and Answers

Frequently asked C programming questions and answers, useful for interviews and viva examinations. Covers scope, operators, structures, unions, and more.

c programming
faq
interview question

Top 10 C/C++ Interview Questions and Answers

Prepare for your C/C++ interview with these frequently asked questions covering structures, unions, increment operators, volatile variables, and OOP concepts.

c++
c programming
interview question