Comp348 03 C Programming Part-I
Comp348 03 C Programming Part-I
PRINCIPLES OF
PROGRAMMING
LANGUAGES
Dept. of Computer Science and Software Engineering, Concordia University Fall 2021
Acknowledgement and Copyright Notice
• The following materials are the original lecture note from
the Course Pack “COMP 348 Principles of Programming
Languages” written and developed by:
2
Procedural Programming with C
Functions
3
Functions
• Similarly to its mathematical counterpart, a computing function
is a (named) block that normally receives some input,
performs some task and normally returns a result.
• Unlike its mathematical counterpart, a computing function
may receive no input or may produce no output.
• A function call implies transfer of control of execution to the
function. When a function completes its task and terminates,
control of execution is transferred back to the client.
• Synonyms for function exist in various languages (such as
method, procedure, or subroutine). It is also important to note
that some languages make a distinction between functions
that return a result and those that do not, the latter ones being
referred to as procedures.
4
Functions /cont.
• The general form of a function definition in C is
5
Functions /cont.
• If no type is in front of a variable in the parameter list, then int
is assumed. Finally, the body of the function is a sequence of
statements.
#include<stdio.h>
long factorial(int);
int main() {
...
}
long factorial(int n) {
...
}
7
Recursion: An example /cont.
#include<stdio.h>
long factorial(int);
int main() {
int n;
long f;
printf("Enter an non-negative integer: ");
scanf("%d", &n);
if (n < 0)
printf("Negative integers are not allowed.\n");
else {
f = factorial(n);
printf("%d! = %ld\n", n, f);
}
return 0; }
long factorial(int n) {
if (n == 0)
return 1;
else
return(n * factorial(n-1)); }
8
Recursion: An example /cont.
• The statement
long factorial(int);
9
Recursion: An example /cont.
• In long factorial(int n) {..}, n is called the parameter
(or formal argument, also dummy argument) of the function.
• The result of a function is called its return value and the data
type of the return value is called the function's return type.
10
Recursion: An example /cont.
• In the statement
f = factorial(n);
the function on the right-hand-side executes and the value
that it returns is assigned to the variable f on the left-hand-
side. We refer to n as the actual argument (or just argument if
the distinction is clear from the context).
12
Example: Fahrenheit to Celsius conversion /cont.
• Let us execute the program:
13
Global and local variables
• We distinguish between global and local variables.
• A global variable is defined at the top of the program file and
can be accessed by all functions.
• A local variable is accessed only within the function which it is
declared, called the scope of the variable.
• Though not a good programming practice, in the case where
the same name is used for a global and local variable then the
local variable takes preference within its scope. This is
referred to as shadowing.
• Global variables have default initializations, whereas local
variables do not.
14
Global and local variables /cont.
• Consider the following program that contains a global and a
local variable with the same name.
• Within function func(..) the global variable a is not visible as
the local variable a takes precedence.
#include<stdio.h>
int a = 3;
int func() {
int a = 5;
return a;
}
int main() {
printf("From main: %d\n", a);
printf("From func: %d\n", func());
printf("From main: %d\n", a);
}
• The output is as follows:
From main: 3
From func: 5
From main: 3 15
Variable and function modifiers
• Two modifiers are used to explicitly indicate the visibility of a
variable or function: The extern modifier indicates that a
variable or function is defined outside the current file, whereas
the static modifier indicates that the variable or function is
visible only from within the file it is defined in.
16
Variable and function modifiers: Summary
• extern: Variable/function is defined outside of current file.
17
Variable and function modifiers: More
• static local variables provide similar functionality as in global
variables but provide local visibility.
int a = 0;
int f () {
return a++;
}
vs.
int f2() {
static int a = 0;
return a++;
}
18
Example: Sorting
• Consider a program that reads in a collection of elements and
proceeds to sort them by calling a function bubbleSort() that is
defined outside the current file and thus must be declared
extern.
#include <stdio.h>
extern void bubbleSort(int[], int);
int main() {
int array[10], numberOfElements;
printf("Enter number of elements: ");
scanf("%d", &numberOfElements);
printf("Enter %d integers: ", numberOfElements);
for (int i = 0; i < numberOfElements; i++)
scanf("%d", &array[i]);
bubbleSort(array, numberOfElements);
printf("Sorted list (ascending order): ");
for (int i = 0 ; i < numberOfElements ; i++)
printf("%d ", array[i]);
return 0;
}
19
Example: Sorting /cont.
• Function bubbleSort(), defined in some other file, makes use
of function swap() that swaps two elements.
• As function swap() need not be visible outside the file in which
it is defined, it is declared static:
static void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp; }
20
Example: Sorting /cont.
21
The C standard library
• An application programming interface (API) is a protocol that
constitutes the interface of software components.
• In the C language this is a collection of functions grouped
together according to their domain.
• We can access this API (called the C standard library) by
adding the #include directive at the top of our program file.
• Perhaps the most common is the group of functions that
support input-output and are accessed by <stdio.h>.
22
The C standard library: Common header files
• math.h: Defines common mathematical functions.
• stdio.h: Defines core input and output functions.
• stdlib.h: Defines numeric conversion functions, pseudo-
random number generation functions, memory allocation,
process control functions.
• string.h: Defines string manipulation functions.
23
Formatted Output
• You may be surprised to know that the C language defines no
input/ouput functionality.
• The printf function is part of the standard library. The
following is a list of format specifiers:
24
Formatted Output /cont.
• The \n we used in some printf statements is called an escape
sequence and it represents a newline character. The following
are common escape sequences:
25
Formatted Output /cont.
• Example:
#include<stdio.h>
main () {
int a, b;
float c, d;
a = 7;
b = a / 2;
c = 10.5;
d = c / 2;
printf("%d\n", b);
printf("%3d\n", b);
printf("%3.2f\n", c);
printf("%3.3f\n",d);
return 0;
}
• The output is:
3
3
10.50
5.250 26
Procedural Programming with C
27
Data types
• A program is composed by constructs, such as functions and
variables.
• A data type (or simply a type) is a description of the possible
values that a construct can store or compute to, together
with a collection of operations that manipulate that type, e.g.
the set of integers together with operations such as addition,
subtraction, and multiplication.
• Common types among most programming languages include
booleans, numerals, characters and strings.
28
Classes of data types
• The Boolean type contains the values true and false.
• The numeral type includes integers that represent whole
numbers and floating points that represent real numbers.
• The character type is a member of a given set (ASCII) and,
finally, strings are sequences of alphanumeric characters.
• We can distinguish between simple types and composite (or
aggregate) types based on whether or not the values of a type
can contain subparts.
• As an example, we can say that integer is a simple type,
whereas record is composite. A data item is an instance (also:
a member) of a type.
29
Primitive data types
• With respect to a given programming language, a primitive
type is one that is built in (provided by the language).
• The C language supports two different classes of data types,
namely numerals and characters, which are divided into four
type identifiers (int, float, double, char), together with four
optional specifiers (signed, unsigned, short, long):
30
Optional specifiers: Short, long, signed and unsigned
• The four specifiers define the amount of storage allocated to
the variable.
• We distinguish between short and long numeral data types
that differ in their range.
• The amount of storage is not specified, but ANSI places the
following rules:
31
Optional specifiers: Short, long, signed and unsigned /cont.
• We can also distinguish between signed and unsigned
numeral data types.
• Signed variables can be either positive or negative. On the
other hand unsigned variables can only be positive, thus
covering a larger range.
32
Type conversion
• Type conversion is the transformation of one type into
another.
• In C, implicit type conversion (or coercion) is the automatic
type conversion done by the compiler.
• In the following example, the value of a float variable is
assigned to an integer variable which in turn is assigned to
another float variable.
#include <stdio.h>
int main() {
int intvar;
float floatvar = 3.14;
float floatvar2;
intvar = floatvar;
floatvar2 = intvar;
printf("%d : %.2f : %1.3f\n", intvar, floatvar, floatvar2);
return 0; }
• or with #define as in
#define TRUE 1
#define FALSE 0
34
Composite data types
• A composite type is one that is composed by primitive types
or other composite types.
35
Arrays
• An array is a collection of values (called its elements), all of
the same type, held in a specific order (and in fact stored
contiguously in memory).
36
Arrays: Example
• In the following program, we declare and initialize an array,
numbers, and then pass it as an argument, together with its
size, to function getAverage() that will compute and return the
average of the elements of the array.
#include<stdio.h>
float getAverage(float[], int);
int main() {
float numbers[5] = {1, 2.5, 9, 11.5, 23.5};
printf("Array average: %.1f.\n", getAverage(numbers, 5));
return 0; }
float getAverage(float list[], int size) {
int i;
float sum = 0.0;
float average = 0.0;
for (i=0; i<size; i++)
sum = sum + list[i];
average = (sum/size);
return average; }
38
Pointers: Example 1
• The following code segment declares an integer variable a
which is assigned to 42 (line 3), and a pointer p that points to
an integer object (line 4).
• In line 5, the pointer p is assigned the address of variable a,
and in line 6 we display the contents of the object pointed to
by p.
• Accessing the object being pointed at is called dereferencing.
1 #include <stdio.h>
2 int main() {
3 int a = 42;
4 int *p;
5 p = &a;
6 printf("p: %d\n", *p);
7 return 0;
8 }
40
Pointers: Example 2
• In this example an integer pointer ptr points to an integer
variable my_var.
#include<stdio.h>
int main() {
int my_var = 13;
int *ptr = &my_var;
*ptr = 17;
printf("my_var: %d\n", my_var);
}
41
Pointers: Example 2 /cont.: Illustration
42
Pointers: Example 2 /cont.
• Note that a statement such as *my_var would have been
illegal as it asks C to obtain the object pointed to by my_var.
However, my_var is not a pointer.
43
Aliasing
• Aliasing is a situation where a single memory location can be
accessed through different variables.
• Modifying the data through one name implicitly modifies the
values associated to all aliased names.
• Consider the program below:
#include<stdio.h>
int main() {
int a = 7;
int *ptr;
ptr = &a;
printf("a: %d\n", a);
printf("a: %d\n", *ptr);
a = 9;
printf("a: %d\n", a);
printf("a: %d\n", *ptr);
*ptr = 11;
printf("a: %d\n", a);
printf("a: %d\n", *ptr);
44
return 0; }
Aliasing /cont.
• In this example, we create an integer variable a and an
integer pointer ptr that points to a.
• We then verify that the two variables contain the same value:
int a = 7;
int *ptr;
ptr = &a;
printf("a: %d\n", a);
printf("a: %d\n", *ptr);
45
Aliasing /cont.
• We then proceed to modify the value of a, first directly
a = 9;
printf("a: %d\n", a);
printf("a: %d\n", *ptr);
46
Constant pointers and pointers to constants
• This section covers:
– constant pointers,
– pointers to constants, and
– constant pointers to constants.
47
Constant pointers
• Consider the statements below
int a = 3;
int const b = 5;
int c = 7;
int * const ptr1 = &a;
48
Constant pointers /cont.
• The content of a constant pointer once assigned cannot
change. In other words, the pointer cannot change the
address it holds.
49
Pointers to constants
• The statement
int const * ptr2 = &b;
*ptr2 = 7;
50
Pointers to constants /cont.
• We can change the content of this pointer but we cannot
modify the value of the object pointer to.
51
Constant pointers to constants
• The statement
• We can change neither the address the pointer holds nor the
value of the object it is pointed at.
52
Constant pointers: Putting everything together
• Let us put everything together:
#include <stdio.h>
int main () {
int a = 3;
int const b = 5;
int c = 7;
int * const ptrl = &a; /* a constant pointer of integer type */
int const * ptr2 = &b; /* a pointer of constant integer type */
int const * const ptr3 = &b;
printf("Pointers: ptrl: %d, ptr2: %d, ptr3: %d.\n",
*ptrl, *ptr2, *ptr3);
return 0;
}
53
Pointers and arrays
• The elements of an array are assigned consecutive
addresses. We can use a pointer to an array in order to iterate
through the array's elements. Suppose we have the following:
int arr[5];
int *ptr;
54
Pointers and arrays: Example
• In this example we explore pointer arithmetic to assign an
array to a pointer, and then use the pointer to display the
values of the first three elements of the array.
• We say that we are displaying the contents of the array by
dereferencing the pointer.
#include <stdio.h>
int main() {
int arr[5] = {1, 3, 5, 7, 11};
int *ptr;
ptr = &arr[0];
printf("arr[0]: %d, arr[1]: %d, arr[2]: %d\n",
*ptr, *(ptr + 1), *(ptr + 2));
return 0;
}
• The statement
will display
arr[1]: 2
56
Pointers as function parameters
• In the program below, we deploy function swap that defines
two integer formal parameters, and with the help of a
temporary variable it swaps their values:
#include <stdio.h>
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int first, second;
printf("Enter two integers: ");
scanf("%d%d", &first, &second);
printf("First: %d, Second: %d.\n", first, second);
printf("Swap in progress...\n");
swap(first, second);
printf("First: %d, Second: %d.\n", first, second);
return 0;
} 57
Pointers as function parameters /cont.
• Let us execute the program:
Enter two integers: 5 7
First: 5, Second: 7.
Swap in progress...
First: 5, Second: 7.
58
Pointers as function parameters /cont.
• The correct program is shown below:
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int first, second;
printf("Enter two integers: ");
scanf("%d%d", &first, &second);
printf("First: %d, Second: %d.\n", first, second);
printf("Swap in progress...\n");
swap(&first, &second);
printf("First: %d, Second: %d.\n", first, second);
return 0;
}
59
Pointers as function parameters /cont.
• We can execute the program as follows:
Enter two integers: 5 7
First: 5, Second: 7.
Swap in progress...
First: 7, Second: 5.
60