0% found this document useful (0 votes)
267 views55 pages

Sololearn C

C is a versatile programming language that is commonly used to write operating systems and complex programs. It relates closely to how computers work at a low level while still being relatively easy to learn. The document provides an overview of basic C concepts like data types, variables, operators, input/output functions, and comments. It explains how to write simple C programs, define constants, perform arithmetic, and take user input using functions like scanf and printf.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
267 views55 pages

Sololearn C

C is a versatile programming language that is commonly used to write operating systems and complex programs. It relates closely to how computers work at a low level while still being relatively easy to learn. The document provides an overview of basic C concepts like data types, variables, operators, input/output functions, and comments. It explains how to write simple C programs, define constants, perform arithmetic, and take user input using functions like scanf and printf.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 55

Sololearn C

Basic Concepts

C has been used to write operating systems (like windows), complex programs like the Python
interpreter, Git, Oracle database, and more. C is designed to be versatile, it relates closely to the
way machines work while still being easy to learn. Understanding how computer memory works is
an important aspect of C.

#include <stdio.h>

int main() {
printf("Hello, World!\n");
return 0;
}
This program prints Hello, World! To the console.
#include <stdio.h> the function used for generating output is defined in stdio.h. In order to
use the printf function we need to include the required file, also called a header file.
Int main() is the main function, entry point to a program. Uses curly brackets to form a code
block.
Printf() function is used to generate output. C uses semicolons to indicate end of statements.
return 0; terminates the main() function and returns the value 0 to the calling process. The
number 0 generally means that the program was successfully executed. Any other number
indicates failure.

C supports:
int: integer, whole number
float: floating point number
double: double-precision floating point value
char: single character.
The amount of storage required for each of these types varies by platform.
int: 4 bytes
float: 4 bytes
double: 8 bytes
char: 1 byte
The built-in sizeof operator gives the memory requirements for a particular data type:
#include <stdio.h>

int main() {
printf("int: %d \n", sizeof(int)); /* in bytes */
printf("float: %d \n", sizeof(float));
printf("double: %d \n", sizeof(double));
printf("char: %d \n", sizeof(char));

return 0;
}
The printf statements in this program have two arguments, first is the output string with a format
specifier (%d), while the next argument returns the sizeof value. %d is for decimal numbers.
C does not have a Boolean type. A printf statement can have multiple format specifiers with
corresponding arguments to replace the specifiers. They are also referred to as conversation
specifiers.

The name of a variable must begin with either a letter or an underscore and can be composed of
letters, digits and the underscore character. Using lowercase letters with an underscore to
separate words is a common convention.
int my_var;
my_var = 42;
//or:
int my_var = 42;
Defining and outputting variables:
#include <stdio.h>

int main() {
int a, b;
float salary = 56.23;
char letter = 'Z';
a = 8;
b = 34;
int c = a+b;

printf("%d \n", c);


printf("%f \n", salary);
printf("%c \n", letter);

return 0;
}
Declare multiple variables on a single line using a comma. %f is for float, %c is for char.
C is case sensitive.
Constants are declared using the const keyword and as convention use uppercase letters for
names. They must be initialized when declared.
#include <stdio.h>

int main() {
const double PI = 3.14;
printf("%f", PI);

return 0;
}
Another way to define a constant is with the #define pre-processor directive. The #define directive
uses macros for defining constant values.
#include <stdio.h>

#define PI 3.14

int main() {
printf("%f", PI);
return 0;
}
Before compilation, the pre-processor replaces every macro identifier in the code with its
corresponding value from the directive. In this case every occurrence of PI is replaced with 3.14.
Const uses memory for storage but #define does not. However with const you can control its
scope, like making it private or protected, whereas you cannot do the same with #define.

getchar() returns the value of the next single character input.


#include <stdio.h>

int main() {
char a = getchar();

printf("You entered: %c", a);

return 0;
}
gets() function is used to read input as an ordered sequence of characters, a string. A string is
stored in a char array.
#include <stdio.h>

int main() {
char a[100];

gets(a);

printf("You entered: %s", a);

return 0;
}
Here the input is stored in an array of 100 characters.
scanf() scans input that matches format specifiers
#include <stdio.h>

int main() {
int a;
scanf("%d", &a);

printf("You entered: %d", a);

return 0;
}
The & sign before the variable name is the address operator. It gives the address, or location in
memory, of a variable. Scanf places an input value at a variable address.
#include <stdio.h>

int main() {
int a, b;
printf("Enter two numbers:");
scanf("%d %d", &a, &b);

printf("\nSum: %d", a+b);

return 0;
}
Prompting for two integer inputs and outputting the sum.
Scanf() stops reading as soon as it encounters a space, so “Hello world” is two separate inputs for
scanf().
putchar() outputs a single character.
#include <stdio.h>

int main() {
char a = getchar();

printf("You entered: ");


putchar(a);

return 0;
}
puts() function is used to display output as a string, which is stored in a char array.
#include <stdio.h>

int main() {
char a[100];

gets(a);

printf("You entered: ");


puts(a);

return 0;
}
printf() outputs in a formatted way
#include <stdio.h>

int main() {
int a;
scanf("%d", &a);

printf("You entered: %d", a);

return 0;
}
& sign is the address operator, gives the address in memory of a variable.
scanf() function is used to assign input to variables. A call scans input according to format
specifiers that convert input as necessary. If input can’t be converted, then the assignment isn’t
made. scanf() waits for input and then makes assignments:
int x;
float num;
char text[20];
scanf("%d %f %s", &x, &num, text);
// Typing 10 22.5 abcd and then pressing Enter assigns 10 to x, 22.5 to
num, and abcd to text.
& must be used to access the variable addresses. The & isn’t needed for a string because a string
name acts as a pointer.
Format specifiers begin with % and are used to assign values to corresponding arguments after
the control string. Blanks, tabs and newlines are ignored. A format specifier can include several
options along with a conversion character:
%[*][max_width]conversion character
The optional * will skip the input field. The optional max_width gives the maximum number of
characters to assign to an input field.
The conversion character converts the argument, if necessary to the indicated type:
d decimal, c character, s string, f float, x hexadecimal.

int x, y;
char text[20];

scanf("%2d %d %*f %5s", &x, &y, text);


/* input: 1234 5.7 elephant */
printf("%d %d %s", x, y, text);
/* output: 12 34 eleph */
printf function requires a format string which can include escape sequences.
Escape sequences begin with a backslash \:
\n new line, \t horizontal tab, \\ backslash, \b backspace, \’ single quote, \” double quote.
Format sequences being with a % sign and can include several options:
%[-][width].[precision]conversion character
optional - specifies left alignment of the data in the string, optional width gives minimum number of
characters for the data, period separates width from precision, optional precision gives number of
decimal places for numeric data (rounds up from .5).
d decimal, c character, s string, f float, e scientific notation, x hexadecimal.
To print % use %% in the format string.
printf("Color: %s, Number: %d, float: %5.2f \n", "red", 42, 3.14159);
/* Color: red, Number: 42, float: 3.14 */

printf("Pi = %3.2f", 3.14159);


/* Pi = 3.14 */

printf("Pi = %8.5f", 3.14159);


/* Pi = 3.14159 */

printf("Pi = %-8.5f", 3.14159);


/* Pi = 3.14159 */

printf("There are %d %s in the tree.", 22, "apples");


/* There are 22 apples in the tree. */

Comments:
starts with /* and ends with */
#include <stdio.h>

/* A simple C program
* Version 1.0
*/
int main() {
/* Output a string */
printf("Hello World!");
return 0;
}

C++ introduced double slash comment // as a way to comment single lines. Some C compilers
also support this comment style.
#include <stdio.h>

int main() {
int x = 42; //int for a whole number

//%d is replaced by x
printf("%d", x);

return 0;
}

C supports arithmetic operators +, -, *, /, %.


Operators are used to form a numeric expression
#include <stdio.h>

int main() {
int length = 10;
int width = 5;
int area;

area = length * width;


printf("%d \n", area); /* 50 */

return 0;
}
The division operators performs differently depending on the data types of the operands. When
both operands are integers, truncated division happens and any remainder is removed. If one or
both are float or double type (real number) the result is a real number.
% returns only the remainder of integer division, not float or double.
#include <stdio.h>

int main() {
int i1 = 10;
int i2 = 3;
int quotient, remainder;
float f1 = 4.2;
float f2 = 2.5;
float result;

quotient = i1 / i2; // 3
remainder = i1 % i2; // 1
result = f1 / f2; // 1.68

return 0;
}

C has an operator precedence (left to right precedence)


*, /, %
+, -
You can change the order of operations using ().
#include <stdio.h>

int main() {
int a = 6;
int b = 4;
int c = 2;
int result;
result = a - b + c; // 4
printf("%d \n", result);

result = a + b / c; // 8
printf("%d \n", result);

result = (a + b) / c; // 5
printf("%d \n", result);

return 0;
}
C may not evaluate a numeric expression as desired when the associative property allows any
order. For example, x*y*z may be evaluated as (x * y) * z or as x * (y * z). If order is important,
break the expression into separate statements.
When a numeric expression contains operands of different data types, they are automatically
converted as necessary in a process called type conversion.
#include <stdio.h>

int main() {
float price = 6.50;
int increase = 2;
float new_price;

new_price = price + increase;


printf("New price is %4.2f", new_price);
/* Output: New price is 8.50 */

return 0;
}
4.2 indicates the float is to be printed in a space at least 4 characters wide with 2 decimal places.
Type casting is done with curly brackets ().
float average;
int total = 23;
int count = 4;

average = (float) total / count;


/* average = 5.75 */
Explicit type conversion is a good programming convention/style.
C has assignment operators: +=, -=, *=, /=, %=
int x = 2;
x += 1; // 3
x -= 1; // 2
x *= 3; // 6
x /= 2; // 3
x %= 2; // 1
x += 3 * 2; // 7
The expression on the right is evaluated before being assigned to x.
C allows the use of the increment and decrement operators: ++ and --
They can be used either prefix or postfix:
z = 3;
x = z--; /* assign 3 to x, then decrement z to 2 */
y = 3;
x = ++y; /* increment y to 4, then assign 4 to x */

Conditionals and Loops


The if statement takes the form:
if (expression) {
statements
}
//or
if (expression) statement;
Example:
#include <stdio.h>

int main() {
int score = 89;

if (score > 75) printf("You passed.\n");

return 0;
}
If there are multiple statements you use curly braces to contain the code block.
There are six relational operators used to form a Boolean expression: <, <=, >, >=, ==, !=.
int num = 41;
num += 1;
if (num == 42) {
printf("You won!");
}
A value that evaluates to a non-zero value is considered true.
int in_stock = 20;
if (in_stock)
printf("Order received.\n");
The if statement can include an optional else clause:
#include <stdio.h>

int main() {
int score = 89;

if (score >= 90) printf("Top 10%%.\n");


else printf("Less than 90.\n");

return 0;
}

You can use the ?: operator to form a ternary operator (short if else statement):
#include <stdio.h>
int main() {
int y;
int x = 3;

y = (x >= 5) ? 5 : x;

/* This is equivalent to:


if (x >= 5)
y = 5;
else
y = x;
*/

return 0;
}

if statements can be nested:


if (profit > 1000)
if (clients > 15)
bonus = 100;
else
bonus = 25;
make sure to use curly braces {} with if else statements:
if (profit > 1000) {
if (clients > 15)
bonus = 100;
}
else
bonus = 25;
The if-else if statement can be used:
int score = 89;

if (score >= 90)


printf("%s", "Top 10%\n");
else if (score >= 80)
printf("%s", "Top 20%\n");
else if (score > 75)
printf("%s", "You passed.\n");
else
printf("%s", "You did not pass.\n");
if-else if statements are preferred over nested statements for clarity.

The switch statement matches the result of an expression with a constant case value:
switch (expression) {
case val1:
statements
break;
case val2:
statements
break;
default:
statements
}
example:
int num = 3;

switch (num) {
case 1:
printf("One\n");
break;
case 2:
printf("Two\n");
break;
case 3:
printf("Three\n");
break;
default:
printf("Not 1, 2, or 3.\n");
}
There can be multiple cases with unique labels. The optional default case is executed when no
case matches. A break statement is needed to branch to the end of the switch statement.
Otherwise the program will check the next case statement.

Logical operators && and || are used to form a compound Boolean expression that tests multiple
conditions. ! is used to reverse the state of a Boolean expression.
&& AND operator:
if (n > 0 && n <= 100)
printf("Range (1 - 100).\n");
Logical operators can be used to join multiple expressions. A compound Boolean expression is
evaluated from left to right. Evaluation stops when no further test is needed for determining the
result, so be sure to consider the arrangement of operands.
|| OR operator:
if (n == 'x' || n == 'X')
printf("Roman numeral value 10.\n");
Any number of expressions can be joined:
if (n == 999 || (n > 0 && n <= 100))
printf("Input valid.\n");
Parentheses are used for clarity even though && has higher precedence than || and will be
evaluated first.

! NOT operator:
if (!(n == 'x' || n == 'X'))
printf("Roman numeral is not 10.\n");
reverses the value.
In C any non-zero value is considered true and 0 is false. Therefore it converts a true value to 0
and a false value to 1.

While statement:
while (expression) {
statements
}
Example:
#include <stdio.h>

int main() {
int count = 1;

while (count < 8) {


printf("Count = %d\n", count);
count++;
}

return 0;
}
Be careful of infinite loops.
Do-While loop:
do {
statements
} while (expression);
Example:
#include <stdio.h>

int main() {
int count = 1;

do {
printf("Count = %d\n", count);
count++;
} while (count < 8);

return 0;
}
Notice the semicolon after the while statement. It always executes at least once.

The break statement can be used to exit a loop:


int num = 5;
while (num > 0) {
if (num == 3)
break;
printf("%d\n", num);
num--;
}
When you want to remain in the loop but skip ahead to the next iteration, use the continue
statement:
int num = 5;

while (num > 0) {


num--;
if (num == 3)
continue;

printf("%d\n", num);
}
The break and continue statements should not substitute for a better algorithm.

For statement:
for (initvalue; condition; increment) {
statements;
}
Example:
int i;
int max = 10;

for (i = 0; i < max; i++) {


printf("%d\n", i);
}
The for loop can contain multiple expressions separated by commas in each part:
for (x = 0, y = num; x < y; i++, y--) {
statements;
}
Also you can skip the initvalue, condition and/or increment:
int i=0;
int max = 10;
for (; i < max; i++) {
printf("%d\n", i);
}

Loops can also be nested.


Multiplication Example:
int i, j;
int table = 10;
int max = 12;

for (i = 1; i <= table; i++) {


for (j = 0; j <= max; j++) {
printf("%d x %d = %d\n", i, j, i*j);
}
printf("\n"); /* blank line between tables */
}
A break can exit an inner loop while leaving you in the outer loop.

Functions, Arrays & Pointers


A function is a reusable block of code that performs a specific task that makes a program easier to
test and can be modified without changing the calling program.
int main() {
int x, result;

x = 5;
result = square(x);
printf("%d squared is %d\n", x, result);

return 0;
}
In order to use the square function, we need to declare it. This takes the form:
return_type function_name(parameters);
A return type of void can be used if no value is returned.
When the parameter types and names are included in a declaration, the declaration is called a
function prototype.
#include <stdio.h>

/* declaration */
int square (int num);

int main() {
int x, result;

x = 5;
result = square(x);
printf("%d squared is %d\n", x, result);

return 0;
}

The last step is defining the function. Function definitions appear after main() function.
#include <stdio.h>
/* declaration */
int square (int num);

int main() {
int x, result;

x = 5;
result = square(x);
printf("%d squared is %d\n", x, result);

return 0;
}

/* definition */
int square (int num) {
int y;

y = num * num;

return(y);
}
Values passed to a functions parameters are called arguments. By default arguments are passed
by value.
#include <stdio.h>

int sum_up (int x, int y); /* function declaration */

int main() {
int x, y, result;

x = 3;
y = 12;
result = sum_up(x, y);
printf("%d + %d\ = %d\n", x, y, result);

return 0;
}

int sum_up (int x, int y) {


x += y;
return(x);
}
The parameters in a function declaration are the formal parameters. The values passed to these
are the arguments or actual parameters.

Variable scope refers to the visibility of variables within a program. Variables declared in a function
are local to that block of code and cannot be referred to outside the function. Variables declared
outside all functions are global to the entire program. Constants declared with #define are global.
Program that uses both local and global variables:
#include <stdio.h>

int global1 = 0;

int main() {
int local1, local2;

local1 = 5;
local2 = 10;
global1 = local1 + local2;
printf("%d \n", global1); /* 15 */

return 0;
}
Parameters of a function act as local variables. When exiting a function parameters and local
variables are destroyed. Use global variables with caution.
Static variables have a local scope but are not destroyed when a function is exited. A static
variable retains its value for the life of the program and can be accessed every time the function is
re-entered. It is initialized when declared and requires the prefix static.
#include <stdio.h>

void say_hello();

int main() {
int i;

for (i = 0; i < 5; i++) {


say_hello();
}

return 0;
}

void say_hello() {
static int num_calls = 1;

printf("Hello number %d\n", num_calls);


num_calls++;

The factorial of a number can be calculated using recursion.


A recursive function is one that calls itself and includes a base case, or exit condition. Example:
#include <stdio.h>

//function declaration
int factorial(int num);

int main() {
int x = 5;

printf("The factorial of %d is %d\n", x, factorial(x));

return 0;
}

//function definition
int factorial(int num) {

if (num == 1) /* base case */


return (1);
else
return (num * factorial(num - 1));
}
Recursion works by stacking calls until the base case is executed.

An array is a data structure that stores a collection of related values that are all the same type.
They are useful because they can represent related data with one descriptive name rather than
using several separate variables.
int test_scores[25]; /* An array size 25 */
float prices[3] = {3.2, 6.55, 10.49}; /* initialize when declared */
Missing values are set to 0.
An array is stored in contiguous (in sequence) memory locations and cannot change size after
being declared.
An element of an array can be accessed through its index number:
int x[5] = {20, 45, 16, 18, 22};
printf("The second element is %d\n", x[1]); /* 45 */
In C index numbers start at 0.
Changing value of element:
int x[5] = {20, 45, 16, 18, 22};
x[1] = 260;
printf("The second element is %d\n", x[1]); /* 260 */
The index of an array is also referred to as the subscript.

You can traverse the array using a loop, such as the for loop as its loop control variable
corresponds to the arrays indexes.
float purchases[3] = {10.99, 14.25, 90.50};
float total = 0;
int k;

/* total the purchases */


for (k = 0; k < 3; k++) {
total += purchases[k];
}

printf("Purchases total is %6.2f\n", total);


/* Output: Purchases total is 115.74 */
The loop control variable iterates from 0 to one less than the number of elements to match index
values. They can also be used to assign:
int a[10];
int k;

for (k = 0; k < 10; k++) {


a[k] = k * 10;
}

Arrays can be multi-dimensional, such as 2D:


int a[2][3]; /* A 2 x 3 array */
Nested curly braces are used to initialize elements row by row like:
int a[2][3] = {
{3, 2, 6},
{4, 5, 20}
};
/* it can also take the form: */
int a[2][3] = { {3, 2, 6}, {4, 5, 20} };
To access both indexes are required:
int a[2][3] = {
{3, 2, 6},
{4, 5, 20}
};
printf("Element 3 in row 2 is %d\n", a[1][2]); /* 20 */
a[1][2] = 25;
printf("Element 3 in row 2 is %d\n", a[1][2]); /* 25 */

Nested loops are required to traverse a multi-dimensional array:


int a[2][3] = {
{3, 2, 6},
{4, 5, 20}
};
int k, j;
/* display array contents */
for (k = 0; k < 2; k++) {
for (j = 0; j < 3; j++) {
printf(" %d", a[k][j]);
}
printf("\n");
}

C is designed to be a low-level language that can easily access memory locations and perform
memory-related operations. The scanf() function places the value entered by the user at the
location, or address, of the variable. Accomplished using the & symbol:
int num;
printf("Enter a number: ");

scanf("%d", &num);

printf("%d", num);
&num is the address of variable num.
Each memory address is given as a hexadecimal number. Hex is a base-16 number that uses 0-9
and letters A through F (16 characters) to represent a group of four binary digits that can have the
value from 0 to 15. It’s much easier to read a hex number that is 8 characters long for 32 bits of
memory. Program to display memory addresses:
void test(int k);

int main() {
int i = 0;

printf("The address of i is %x\n", &i);


test(i);
printf("The address of i is %x\n", &i);
test(i);

return 0;
}

void test(int k) {
printf("The address of k is %x\n", &k);
}
Uses %x because it is in hex. Output may vary run to run.
The address of a variable stays the same from the time it is declared until the end of its scope.

Pointers are very important in C as they allow you to work with memory locations. They are
fundamental to data structures and algorithms. A pointer is a variable that contains the address of
another variable. Pointers are declared using the * symbol:
pointer_type *identifier
The actual pointer data type is a hexadecimal number, but when declaring a pointer, you must
indicate what type of data it will be pointing to.
int j = 63;
int *p = NULL;
p = &j;

printf("The address of j is %x\n", &j);


printf("p contains address %x\n", p);
printf("The value of j is %d\n", j);
printf("p is pointing to the value %d\n", *p);
Pointers should be initialized to NULL until they are assigned a valid location. They can be
assigned the address of a variable using the & sign. To see what a pointer is pointing to, use the *
again, as in *identifier. In this case the * is called the indirection or dereference operator. This
process is called dereferencing.
Some algorithms use a pointer to a pointer. This type of variable declaration uses **, and can be
assigned the address of another pointer, as in:
int x = 12;
int *p = NULL
int **ptr = NULL;
p = &x;
ptr = &p;
Pointers can be used in expression just as any variable. Arithmetic operators can be applied to
whatever the pointer is pointing to.
int x = 5;
int y;
int *p = NULL;
p = &x;

y = *p + 2; /* y is assigned 7 */
y += *p; /* y is assigned 12 */
*p = y; /* x is assigned 12 */
(*p)++; /* x is incremented to 13 */

printf("p is pointing to the value %d\n", *p);


Note that the parentheses are required for the ++ operator to increment the value being pointed to.
Same with -- operator.

With pointers we can point to the first element in an array and then use address arithmetic to
traverse the array: + to move forward, - to move back.
int a[5] = {22, 33, 44, 55, 66};
int *ptr = NULL;
int i;

ptr = a;
for (i = 0; i < 5; i++) {
printf("%d ", *(ptr + i));
}
The array name acts as a pointer to the first element of the array. Therefore ptr = a is the same
as ptr = &a[0].
Using assignment operators to change the address the pointer contains:
int a[5] = {22, 33, 44, 55, 66};
int *ptr = NULL;

ptr = a; /* point to the first array element */


printf("%d %x\n", *ptr, ptr); /* 22 */
ptr++;
printf("%d %x\n", *ptr, ptr); /* 33 */
ptr += 3;
printf("%d %x\n", *ptr, ptr); /* 66 */
ptr--;
printf("%d %x\n", *ptr, ptr); /* 55 */
ptr -= 2;
printf("%d %x\n", *ptr, ptr); /* 33 */
When a pointer is incremented, the memory address increases by the number of bytes being
pointed to. In the program above, the pointer increases by 4 because the pointer is pointing to an
int.

With pointer parameters, your functions can alter actual data rather than a copy of data. To
change the actual values of variables, the calling statement passes addresses to pointer
parameters in a function. For example the following swaps two variables:
void swap (int *num1, int *num2);
int main() {
int x = 25;
int y = 100;

printf("x is %d, y is %d\n", x, y);


swap(&x, &y);
printf("x is %d, y is %d\n", x, y);

return 0;
}

void swap (int *num1, int *num2) {


int temp;

temp = *num1;
*num1 = *num2;
*num2 = temp;
}

An array cannot be passed by value to a function. However an array name is a pointer, so just
passing an array name to a function is passing a pointe to the array.
int add_up (int *a, int num_elements);

int main() {
int orders[5] = {100, 220, 37, 16, 98};

printf("Total orders is %d\n", add_up(orders, 5));

return 0;
}

int add_up (int *a, int num_elements) {


int total = 0;
int k;

for (k = 0; k < num_elements; k++) {


total += a[k];
}

return (total);
}

A pointer to an array can be returned, like in:


int * get_evens();

int main() {
int *a;
int k;

a = get_evens(); /* get first 5 even numbers */


for (k = 0; k < 5; k++)
printf("%d\n", a[k]);

return 0;
}

int * get_evens() {
static int nums[5];
int k;
int even = 0;

for (k = 0; k < 5; k++) {


nums[k] = even += 2;
}

return (nums);
}
A pointer is declared to store the value returned by the function. When a local variable is being
passed out of a function, you need to declare it as static in the function.

Strings & Function Pointers

A string in C is an array of characters that ends with a NULL character ‘\0’. It can be declared in
several ways:
char str_name[str_len] = "string";
When you provide a string literal to initialize the string, the compiler automatically adds a NULL
character ‘\0’ to the char array. For this reason you must declare the array size to be at least one
character longer than the expected string length. If the declaration does not include a char array
size, then it will be calculated based on the length of the string in the initialization plus one for ‘\0’:
char str1[6] = "hello";
char str2[ ] = "world"; /* size 6 */
A string can also be declared as a set of characters:
char str3[6] = {'h', 'e', 'l', 'l', 'o', '\0'};

char str4[ ] = {'h', 'e', 'l', 'l', 'o', '\0'}; /* size 6 */


With this approach the NULL character must be added explicitly.
With any array, the name of the string acts as a pointer. A string literal is a text enclosed in double
quotation marks. Characters are single quotation marks.
A string pointer declaration such as char *str = “stuff”; is considered a constant and cannot be
changed from its initial value.
Use the Standard Library string functions to work with strings. Include <string.h>
strlen() - get length
strcat() - merge two strings
strcpy() - copy one string to another
strlwr() - lower case convert
strupr() - upper case convert
strrev() - reverse
strcmp() - compare two strings.
Use scanf() to read input according to the format specifiers:
char first_name[25];
int age;
printf("Enter your first name and age: \n");
scanf("%s %d", first_name, &age);
notice how first_name does not use &, as an array name acts as a pointer.
scanf() stops reading input when it reaches a space. To read a string with spaces, use the gets()
function. It reads input until terminating newline, enter key, is pressed.
char full_name[50];
printf("Enter your full name: ");
gets(full_name);
A safer alternative to gets() is fgets(), which reads upto a specified number of characters. This
helps prevent buffer overflow, when string array is not large enough.
char full_name[50];
printf("Enter your full name: ");
fgets(full_name, 50, stdin);
The fgets() arguments are string_name, number of characters to read, and a pointer to where you
want to read the string from. stdin means to read from the standard input-the keyboard.
fgets() stores the newline character whereas gets() does not.
Also fgets() reads only n-1 characters from stdin to make room for ‘\0’.
fputs() requires the name of the string and a pointer to where you want to point the string. To print
to the screen, use stdout which refers to the standard output.
#include <stdio.h>
int main()
{
char city[40];
printf("Enter your favorite city: ");
gets(city);
// Note: for safety, use
// fgets(city, 40, stdin);

fputs(city, stdout);
printf(" is a fun city.");

return 0;
}
puts() takes only a single argument and can be used to display output but adds a newline to the
end of output:
#include <stdio.h>
int main()
{
char city[40];
printf("Enter your favorite city: ");
gets(city);
// Note: for safety, use
// fgets(city, 40, stdin);

puts(city);

return 0;
}

Use sprintf() to create a formatted string. Used to build a string from other data types:
#include <stdio.h>
int main()
{
char info[100];
char dept[ ] = "HR";
int emp = 75;
sprintf(info, "The %s dept has %d employees.", dept, emp);
printf("%s\n", info);

return 0;
}

sscanf() is used to scan a string for values. It reads values from a string and stores them at the
corresponding variable addresses.
#include <stdio.h>
int main()
{
char info[ ] = "Snoqualmie WA 13190";
char city[50];
char state[50];
int population;
sscanf(info, "%s %s %d", city, state, &population);
printf("%d people live in %s, %s.", population, city, state);

return 0;
}
The string.h library contains numerous string functions.
#include <string.h> gives you access to these:
strlen(str) returns length of string not including NULL character ‘\0’
strcat(str1, str2) appends str2 to end of str1 and returns a pointer to str1.
strcpy(str1, str2) copies str2 to str1. Useful for assigning a string a new value.
#include <stdio.h>
#include <string.h>

int main()
{
char s1[ ] = "The grey fox";
char s2[ ] = " jumped.";

strcat(s1, s2);
printf("%s\n", s1);
printf("Length of s1 is %d\n", strlen(s1));
strcpy(s1, s2);
printf("s1 is now %s \n", s1);

return 0;
}
Additional functions:
strncat(str1, str2, n) Appends first n characters to str2 to the end of str1 and returns a pointer to
str1.
strncpy(str1, str2, n) Copies fist n characters of str2 to str1.
strcmp(str1, str2) Returns 0 when str1 is equal to str2, less than 0 when str1<str2 and greater
when str1>str2.
strncmp(str1, str2, n) Returns 0 when first n characters of str1 is equal to first n characters of str2,
less than 0 when str1<str2 and greater when str1>str2.
strchr(str1, c) Returns a pointer to the first occurrence of char c in str1, or NULL if character not
found.
strrchr(str1, c) Searches str1 in reverse and returns a pointer to the position of char c in str1, or
NULL if not found.
strstr(str1, str2) Returns a pointer to the first occurance of str2 in str1, or NULL if str2 not found.

The stdio.h library contains the following funcitons for converting a string to a number:
int atoi(str) Stands for ASCII to integer. 0 is returned if the first character is not a number or no
numbers are encountered.
double atof(str) Stands for ASCII to float. 0.0 is returned if the first character is not a number or no
numbers are encountered.
long int atoll(str) Stands for ASCII to long int. 0 returned if the first character is not a number or no
numbers are encountered.
#include <stdio.h>
int main()
{
char input[10];
int num;

printf("Enter a number: ");


gets(input);
num = atoi(input);

return 0;
}
atoi() lack error handling, use strtol() if you want error handling
long int strtol(str, endptr, base) returns the converted integral number as a long int value, else zero
is returned. str is the string,
endptr is reference to an object of type char, whose value is set by the function to the next
character in str after the numerical value.
base must be between 2 and 36 inclusive or be special value 0.
#include <stdio.h>
#include <stdlib.h>

int main () {
char str[30] = "2030300 This is test";
char *ptr;
long ret;

ret = strtol(str, &ptr, 10);


printf("The number(unsigned long integer) is %ld\n", ret);
printf("String part is |%s|", ptr);

return(0);
}
A two-dimensional array can be used to store related strings:
char trip[3][15] = {
"suitcase",
"passport",
"ticket"
};
The string length must be large enough to hold the longest string and it can be very cumbersome
to access the elements.

An easier, more intuitive way to deal with a collection of related strings is with an array of pointers:
char *trip[ ] = {
"suitcase",
"passport",
"ticket"
};
printf("Please bring the following:\n");
for (int i = 0; i < 3; i++) {
printf("%s\n", trip[ i ]);
}
The array of string pointers has a more ragged (uneven) structure and there is no limit to the string
length. Items can be referred to by a pointer to the first character of each string.
A declaration like char *items[3]; only reserves space for three pointers; the actual strings are
being referenced by those pointers.

Function pointers point to executable code for a function in memory. They can be stored in an
array or passed as arguments to other functions. It uses * as you would with any pointer:
return_type (*func_name)(parameters)
The parentheses around (*func_name) are important. Without them, the compiler will think the
function is returning a pointer.
After declaring the function pointer, you must assign it to a function.
#include <stdio.h>
void say_hello(int num_times); /* declare function */

int main() {
void (*funptr)(int); /* declare function pointer */
funptr = say_hello; /* assign function pointer to function */
funptr(3); /* call function using pointer */

return 0;
}

void say_hello(int num_times) {


int k;
for (k = 0; k < num_times; k++)
printf("Hello\n");
}
A function name points to the start of executable code, just as an array name points to its first
element. Therefore, although statements such as funptr = &say_hello and (*funptr)(3) are correct,
it isn't necessary to include the address operator & and the indirection operator * in the function
assignment and function call.

An array of function pointers can replace a switch or an if statement for choosing an action:
#include <stdio.h>

int add(int num1, int num2);


int subtract(int num1, int num2);
int multiply(int num1, int num2);
int divide(int num1, int num2);
int main()
{
int x, y, choice, result;
int (*op[4])(int, int); /* declare array of function pointers */

op[0] = add; /* assign function pointers to functions */


op[1] = subtract; /* note how & is not needed */
op[2] = multiply;
op[3] = divide;
printf("Enter two integers: ");
scanf("%d%d", &x, &y);
printf("Enter 0 to add, 1 to subtract, 2 to multiply, or 3 to divide:
");
scanf("%d", &choice);
result = op[choice](x, y); /* using function pointer */
printf("%d", result);

return 0;
}

int add(int x, int y) {


return(x + y);
}

int subtract(int x, int y) {


return(x - y);
}

int multiply(int x, int y) {


return(x * y);
}

int divide(int x, int y) {


if (y != 0)
return (x / y);
else
return 0;
}

A void pointer is used to refer to any address type in memory and has a declaration that looks like:
void *ptr;
Following program uses the same pointer for three different data types:
int x = 33;
float y = 12.4;
char c = 'a';
void *ptr;
ptr = &x;
printf("void ptr points to %d\n", *((int *)ptr));
ptr = &y;
printf("void ptr points to %f\n", *((float *)ptr));
ptr = &c;
printf("void ptr points to %c", *((char *)ptr));
When dereferencing a void pointer, you must first type cast the pointer to the appropriate data type
before dereferencing with * like:
#include<stdio.h>
int main()
{
int a = 10;
void *ptr = &a;
printf("%d", *(int *)ptr); /* notice cast */
return 0;
}
You cannot perform pointer arithmetic.
Void pointers are often used for function declarations, like:
void * square (const void *);
This return type allows for any return type. Similarly, parameters that are void* accept any
argument type. If you want to use the parameter without changing it declare it const.
You can leave out the parameter name to further insulate the declaration from its implementation.
This allows a function to be customized as needed without changing the declaration. Consider:
#include <stdio.h>

void* square (const void* num);

int main() {
int x, sq_int;
x = 6;
sq_int = square(&x);
printf("%d squared is %d\n", x, sq_int);

return 0;
}

void* square (const void *num) {


int result;
result = (*(int *)num) * (*(int *)num);
return result;
}
You can change the definition to multiply floats instead of ints by casting the parameter as floats.
You do not need to change the declaration.
You can pass a function pointer as an argument to another function. A function pointer used as an
argument is sometimes referred to as a callback function because the receiving function “calls it
back”. The qsort() function in stdlib.h header file uses this technique.
Quicksort is used for sorting an array, and can be implemented by including the stdlib.h file and
then writing a compare function that matches the declaration used in qsort:
void qsort(void *base, size_t num, size_t width, int (*compare)(const
void *, const void *))
To breakdown the qsort declaration:
void *base A void pointer to the array.
size_t num The number of elements in the array.
size_t width The size of an element.
int (*compare (const void *, const void *) A function pointer which has two arguments and returns 0
when the arguments have the same value, <0 when arg1 comes before arg2, and >0 when arg1
comes after arg2.
The actual implementation of the compare function is up to you.
The following program sorts an array of ints from low to high using qsort:
#include <stdio.h>
#include <stdlib.h>

int compare (const void *, const void *);

int main() {
int arr[5] = {52, 23, 56, 19, 4};
int num, width, i;

num = sizeof(arr)/sizeof(arr[0]);
width = sizeof(arr[0]);
qsort((void *)arr, num, width, compare); /* function name compare */
for (i = 0; i < 5; i++) /* acts as a pointer */
printf("%d ", arr[ i ]);

return 0;
}

int compare (const void *elem1, const void *elem2) {


if ((*(int *)elem1) == (*(int *)elem2))
return 0;
else if ((*(int *)elem1) < (*(int *)elem2))
return -1;
else
return 1;
}

Structures & Unions

A structure is a user-defined data type that groups related variables of different data types.
Structure declaration:
struct course {
int id;
char title[40];
float hours;
};
It includes the keyword struct, a structure tag for referencing and curly braces {} with a list of
variable declarations called members.
The example defines a new data type called course that has three members. They can be of any
data type even other structures. Do not forget the semicolon after the structure declaration as that
is where you may put instances like:
/* class NAME { constituents } instances ; */
class FOO {
int bar;
int baz;
} waldo;
You have to put the semicolon there so the compiler will know whether you declared any instances
or not.
https://stackoverflow.com/questions/1783465/why-must-i-put-a-semicolon-at-the-end-of-class-declaration-in-c
A structure is also called a composite or aggregate data type. Some languages refer to structures
as records.
Declaring variables of a structure:
struct student {
int age;
int grade;
char name[40];
};

/* declare two variables */


struct student s1;
struct student s2;
A struct varibles is stored in a contiguous block of memory. The sizeof operator must be used to
get the number of bytes needed for a struct, just as with the basic data types.
A struct variable can be initialized in the declaration:
struct student s1 = {19, 9, "John"};
struct student s2 = {22, 10, "Batman"};
You can initialize a structure using curly braces after declaration by type casting:
struct student s1;
s1 = (struct student) {19, 9, "John"};
You can use named member initialization when initializing a structure:
struct student s1
= { .grade = 9, .age = 19, .name = "John"};

You can access the members of a struct variable by using the .(dot operator) between the variable
name and the member name.
s1.age = 19;
You can also assign one structure to another of the same type.
struct student s1 = {19, 9, "Jason"};
struct student s2;
//....
s2 = s1;
Demonstrating using a structure:
#include <stdio.h>
#include <string.h>

struct course {
int id;
char title[40];
float hours;
};

int main() {
struct course cs1 = {341279, "Intro to C++", 12.5};
struct course cs2;

/* initialize cs2 */
cs2.id = 341281;
strcpy(cs2.title, "Advanced C++");
cs2.hours = 14.25;

/* display course info */


printf("%d\t%s\t%4.2f\n", cs1.id, cs1.title, cs1.hours);
printf("%d\t%s\t%4.2f\n", cs2.id, cs2.title, cs2.hours);

return 0;
}
String assignment requires strcpy() from the string.h library.
Format specifiers %4.2f include width and precision options.
The typedef keyword creates a type definition that simplifies code and makes programs easier to
read. It is commonly used with structures because it eliminates the need to use the keyword struct
when declaring variables.
typedef struct {
int id;
char title[40];
float hours;
} course;

course cs1;
course cs2;

The members of a structure may also be structures:


typedef struct {
int x;
int y;
} point;

typedef struct {
float radius;
point center; /* point is a struct */
} circle;
Nested curly braces are used to initialize members that are structs. The dot operator is used to
access members of members.
circle c = {4.5, {1, 3}};
printf("%3.1f %d,%d", c.radius, c.center.x, c.center.y);
/* 4.5 1,3 */
A struct definition must appear before it can be used inside another struct.
Pointers to structures can also be defined
struct myStruct *struct_ptr;
/* defines a pointer to the myStruct structure. */

struct_ptr = &struct_var;
/* stores the address of the structure variable struct_var in the
pointer struct_ptr. */

struct_ptr -> struct_mem;


/* accesses the value of the structure member struct_mem. */
For example:
struct student{
char name[50];
int number;
int age;
};

// Struct pointer as a function parameter


void showStudentData(struct student *st) {
printf("\nStudent:\n");
printf("Name: %s\n", st->name);
printf("Number: %d\n", st->number);
printf("Age: %d\n", st->age);
}

struct student st1 = {"Krishna", 5, 21};


showStudentData(&st1);
The -> operator allows to access members of the struct through the pointer.
(*st).age is the same as st->age.
When a typedef has been used to name the struct, then a pointer is declared using only the
typedef name along with * and the pointer name.

A function can have structure parameters that accept arguments by value when a copy of the
structure variable is all that is needed. For a function to change the actual values in a struct
variable, pointer parameters are required.
#include <stdio.h>
#include <string.h>

typedef struct {
int id;
char title[40];
float hours;
} course;

void update_course(course *class);


void display_course(course class);

int main() {
course cs2;
update_course(&cs2);
display_course(cs2);
return 0;
}

void update_course(course *class) {


strcpy(class->title, "C++ Fundamentals");
class->id = 111;
class->hours = 12.30;
}

void display_course(course class) {


printf("%d\t%s\t%3.2f\n", class.id, class.title, class.hours);
}
update_course() takes a pointer as the parameter, display_course() takes the structure by value.

You can have an array of structure. Each element is accessible with the index number, the dot
operator is then used to access members of the element:
#include <stdio.h>

typedef struct {
int h;
int w;
int l;
} box;
int main() {
box boxes[3] = {{2, 6, 8}, {4, 6, 6}, {2, 6, 9}};
int k, volume;

for (k = 0; k < 3; k++) {


volume = boxes[k].h*boxes[k].w*boxes[k].l;
printf("box %d volume %d\n", k, volume);
}
return 0;
}
Arrays of structures are used for data structures such as linked lists, binary trees and more.

A union allows to store different data types in the same memory location.
It is like a structure because it has members. A union variable uses the same memory location for
all its member’s and only one member at a time can occupy the memory location.
Union members can be of any data type:
union val {
int int_num;
float fl_num;
char str[20];
};
note end semicolon
After declaring a union you can declare its variables. You can also assign one union to another of
the same type:
union val u1;
union val u2;
u2 = u1;
Unions are used for memory management. The largest data type is used to determine the size of
the memory to share and all members use this one location. This process also helps limit memory
fragmentation.

You can access union members using the dot operator. When assignment is performed, the union
memory location will be used for that member until another member assignment is performed.
Trying to access a member that isn’t occupying the memory location gives unexpected results
union val {
int int_num;
float fl_num;
char str[20];
};

union val test;


test.int_num = 123;
test.fl_num = 98.76;
strcpy(test.str, "hello");

printf("%d\n", test.int_num);
printf("%f\n", test.fl_num);
printf("%s\n", test.str);
The last assignment overrides previous assignments, so str stores a value and accessing int_num
and fl_num is meaningless.
Unions are often used within structures because a structure can have a member to keep track of
which union member stores a value.
In this program a vehicle struct can use either a vehicle identification number (VIN) or an assigned
id, but not both:
typedef struct {
char make[20];
int model_year;
int id_type; /* 0 for id_num, 1 for VIN */
union {
int id_num;
char VIN[20];
} id;
} vehicle;

vehicle car1;
strcpy(car1.make, "Ford");
car1.model_year = 2017;
car1.id_type = 0;
car1.id.id_num = 123098;
When a union is declared inside a structure, a union name is required at the end of the
declaration. The dot operator is used twice to access union members of struct members.
/* display vehicle data */
printf("Make: %s\n", car1.make);
printf("Model Year: %d\n", car1.model_year);
if (car1.id_type == 0)
printf("ID: %d\n", car1.id.id_num);
else
printf("ID: %s\n", car1.id.VIN);
A union may also contain a structure.
A pointer to a union points to the memory location allocated to the union. Declared using keyword
union and the union tag along with * and the pointer name.
union val {
int int_num;
float fl_num;
char str[20];
};

union val info;


union val *ptr = NULL;
ptr = &info;
ptr->int_num = 10;
printf("info.int_num is %d", info.int_num);
The -> operator is used to access union members through a pointer.
(*ptr).int_num is the same as ptr->int_num.
A function can have union parameters that accept arguments by value when a copy of the union
variable is all that is needed. Pointer parameters are required to change the actual value in a
union memory location.
union id {
int id_num;
char name[20];
};

void set_id(union id *item) {


item->id_num = 42;
}

void show_id(union id item) {


printf("ID is %d", item.id_num);
}
An array can store unions. Only one member of the union can store data for each array element.
union val {
int int_num;
float fl_num;
char str[20];
};

union val nums[10];


int k;

for (k = 0; k < 10; k++) {


nums[k].int_num = k;
}

for (k = 0; k < 10; k++) {


printf("%d ", nums[k].int_num);
}

An array is a data structure that stores collection values that are all the same type. Arrays of
unions allow storing values of different types.
union type {
int i_val;
float f_val;
char ch_val;
};
union type arr[3];
arr[0].i_val = 42;
arr[1].f_val = 3.14;
arr[2].ch_val = 'x';
Memory Management

When you declare a variable using a basic data type, C automatically allocates space for the
variable in an area of memory called the stack. We can find out the number of bytes used to store
each type of variable with the sizeof operator:
int x;
printf("%d", sizeof(x)); /* output: 4 */
An array is allocated contiguous blocks of memory with each block the size for one element:
int arr[10];
printf("%d", sizeof(arr)); /* output: 40 */
Dynamic memory allocation is the process of allocating and freeing memory as needed. You could
for instance prompt at runtime for the number of array elements and then create an array with that
many elements. Dynamic memory is managed with pointers that point to newly allocated blocks of
memory in an area called the heap.
There is also statically managed data in main memory for variables that persist for the lifetime of
the program.
The stdlib.h library includes memory management functions. #include <stdlib.h> gives you access
to the following:
malloc(bytes) Returns a pointer to a contiguous block of memory that is of size bytes
calloc(num_items, item_size) Returns a pointer to a contiguous block of memory that has
num_items items, each of a certan size. Typically used for arrays, structures, and other derived
types. The memory is initialized to 0.
realloc(ptr, bytes) Resizes the memory pointed to by ptr to size bytes. The newly allocated
memory is not initialized.
free(ptr) Releases the block of memory, useful when you no longer need a block of allocated
memory.

malloc() allocates a specified number of contiguous bytes in memory:


#include <stdlib.h>

int *ptr;
/* a block of 10 ints */
ptr = malloc(10 * sizeof(*ptr));

if (ptr != NULL) {
*(ptr + 2) = 50; /* assign 50 to third int */
}
malloc returns a pointer to the allocated memory. sizeof was applied to *ptr instead of int, making
the code more robust should the *ptr declaration be changed to a different data type later.
The allocated memory is contiguous and can be treated as an array. Pointer arithmetic is used to
traverse the array. You are advised to use + to refer to array elements. Using ++ or += changes
the address stored by the pointer. If the allocation is unsuccessful, NULL is returned. You should
always check for a NULL pointer.
A simple two-dimensional array requires (rows*columns)*sizeof(datatype) bytes of memory.

free() is used to release memory.


int* ptr = malloc(10 * sizeof(*ptr));
if (ptr != NULL)
*(ptr + 2) = 50; /* assign 50 to third int */
printf("%d\n", *(ptr + 2));

free(ptr);

calloc() function allocates memory based on the size of a specific item, such as a strcture.
typedef struct {
int num;
char *info;
} record;

record *recs;
int num_recs = 2;
int k;
char str[ ] = "This is information";

recs = calloc(num_recs, sizeof(record));


if (recs != NULL) {
for (k = 0; k < num_recs; k++) {
(recs+k)->num = k;
(recs+k)->info = malloc(sizeof(str));
strcpy((recs+k)->info, str);
}
}
calloc used to allocated memory for a structure and malloc used to allocate memory for the string
within the structure.
calloc allocates blocks of memory within a contiguous block of memory for an array of structure
elements. You can navigate from one structure to the next with pointer arithmetic. After allocating
room for a structure, memory must be allocated for the string within the structure. Using a pointer
for the info member allows any length string to be stored. Dynamically allocated structures are the
basis of linked lists and binary trees.
realloc() expands a current block to include additional memory.
int *ptr;
ptr = malloc(10 * sizeof(*ptr));
if (ptr != NULL) {
*(ptr + 2) = 50; /* assign 50 to third int */
}
ptr = realloc(ptr, 100 * sizeof(*ptr));
*(ptr + 30) = 75;

When allocating memory for a string pointer, you may want to use string length rather than the
sizeof operator for calculating bytes.
char str20[20];
char *str = NULL;

strcpy(str20, "12345");
str = malloc(strlen(str20) + 1);
strcpy(str, str20);
printf("%s", str);
This approach is better memory management because you aren’t allocating more space than is
needed to a pointer. When using strlen to determine the number of bytes needed for a string, be
sure to include one extra byte for the NULL character ‘\0’. A char is always one byte.

A dynamic array can grow as needed. As elements are not allocated all at once, dynamic arrays
typically use a structure to keep track of current array size, current capacity and a pointer to the
elements, as in:
typedef struct {
int *elements;
int size;
int cap;
} dyn_array;

dyn_array arr;

/* initialize array */
arr.size = 0;
arr.elements = calloc(1, sizeof(*arr.elements) );
arr.cap = 1; /* room for 1 element */
To expand by more elements:
arr.elements = realloc(arr.elements, (5 + arr.cap) *
sizeof(*arr.elements));
if (arr.elements != NULL)
arr.cap += 5; /* increase capacity */
Adding an element to the array increases its size:
if (arr.size < arr.cap) {
arr.elements[arr.size] = 50;
arr.size++;
} else {
printf("Need to expand the array.");
}
To properly implement a dynamic array, sub-tasks should be broken down into functions such as
init_array(), increase_array(), add_element(), and display_array()+error checking.

Files & Error Handling

C includes the FILE type for defining a file stream. The file stream keeps track of where reading
and writing last occurred. The stdio.h library includes file handling functions: FILE Typedef for
defining a file pointer.
fopen(filename, mode) returns a FILE pointer to file filename which is opened using mode. If a file
cannot be opened, NULL is returned. Mode options are: r open for reading, w open for writing (file
need not exist), a open for append (file need not exist), r+ open for reading and writing from
beginning, w+ open for reading and writing, overwriting file, a+ open for reading and writing,
appending to file.
fclose(fp) Closes file opened with FILE fp, returning 0 if close was successful. EOF (end of file) is
returned if there is an error in closing.
Program opens a file for writing and then closes it:
#include <stdio.h>

int main() {
FILE *fptr; // declare a pointer named fptr of type FILE

fptr = fopen("myfile.txt", "w"); //assign to FILE pointer


if (fptr == NULL) { // checks if file has not been successfully opened
printf("Error opening file.");
return -1;
}
fclose(fptr); // fclose important to release memory space
return 0; // needs return otherwise error
}
When a string literal is used to specify a filename, the escape sequence \\ indicates a single
backslash. If there is an error, a -1 error code is returned to the system. Closing a file when you
are done with it frees up memory/space and is good practice.

A file can be read one character at a time or an entire string can be read into a character buffer,
which is typically a char array used for temporary storage.
fgetc(fp) returns the next character from the file pointed to by fp. If the end of the file has been
reached, then EOF is returned.
fgets(buff, n, fp) Reads n-1 characters from the file pointed to by fp and stores the string in buff. A
NULL character ‘\0’ is appened as the last character in buff. If fgets encounters a newline
character or the end of file before n-1 characters is reached, then only the characters up to that
point are stored in buffer.
fscanf(fp, conversion_specifiers, vars) Reads characters from the file pointed to by fp and assigns
input to a list of variable pointers vars using conversion_specifiers. As with scanf, fscanf stops
reading a string when a space or newline is encountered

program reading from a file:


#include <stdio.h>

int main() {
FILE *fptr; // FILE pointer
int c, stock; // initialise variables
char buffer[200], item[10];
float price;
/* myfile.txt: Inventory\n100 Widget 0.29\nEnd of List */

fptr = fopen("myfile.txt", "r");

fgets(buffer, 20, fptr); /* read a line */


printf("%s\n", buffer);

fscanf(fptr, "%d%s%f", &stock, item, &price); /* read data */


printf("%d %s %4.2f\n", stock, item, price);

while ((c = getc(fptr)) != EOF) /* read the rest of the file */


printf("%c", c);

fclose(fptr);
return 0;
}
gets() reads up until newline. fscanf() reads data according to conversion specifiers.
The while loop reads one character at a time until end of file.
When writing to a file, newline characters ‘\n’ must be explicitly added.
fputc(char, fp) Writes char to file pointed to by fp
fputs(str, fp) Writes string str to the file pointed to by fp
fprintf(fp, str, vars) Prints string str to the file pointed to by fp. str can optionally include format
specifiers and a list of variables vars.
FILE *fptr;
char filename[50];
printf("Enter the filename of the file to create: ");
gets(filename);
fptr = fopen(filename, "w");

/* write to file */
fprintf(fptr, "Inventory\n");
fprintf(fptr, "%d %s %f\n", 100, "Widget", 0.29);
fputs("End of List", fptr);

To write blocks of memory to a file, we can use the following binary functions:
Binary file mode options for the fopen() function are:
rb open for reading (file must exist)
wb open for writing (file need not exist)
ab open for append (file need not exist)
rb+ open for reading and writing from beginning
wb+ open for reading and writing, overwriting file
ab+ open for reading and writing, appending to file.
fwrite(ptr, item_size, num_items, fp) Writes num_items items of item_size size from pointer ptr to
the file pointed to by file pointer fp.
fread(ptr, item_size, num_items, fp) Reads num_items items of item_size size from the file pointed
to by file pointer fp into memory pointed to by ptr.
fclose(fp) Closes file opened with file fp, returning 0 if close was successful. EOF is returned if
there is an error in closing.
feof(fp) returns 0 when the end of the file stream has been reached.
FILE *fptr;
int arr[10];
int x[10];
int k;

/* generate array of numbers */


for (k = 0; k < 10; k++)
arr[k] = k;

/* write array to file, can also do array of structures */


fptr = fopen("datafile.bin", "wb");
fwrite(arr, sizeof(arr[0]), sizeof(arr)/sizeof(arr[0]), fptr);
fclose(fptr);

/* read array from file */


fptr = fopen("datafile.bin", "rb");
fread(x, sizeof(arr[0]), sizeof(arr)/sizeof(arr[0]), fptr);
fclose(fptr);

/* print array */
for (k = 0; k < 10; k++)
printf("%d", x[k]);
File extensions alone do not determine the format of data in a file, but they are useful for indicating
the type of data to expect. .txt for file, .bin for binary, .csv comma separated values, and .dat for
data.
ftell(fp) returns a long int value corresponding to the fp file pointer position in a number of bytes
from the start of the file.
fseek(fp, num_bytes, from_pos) moves the fp file pointer position by num_bytes bytes relative to
position from_pos, which can be one of the following constraints:
SEEK_SET start of file
SEEK_CUR current position
SEEK_END end of file.

typedef struct {
int id;
char name[20];
} item;

int main() {
FILE *fptr;
item first, second, secondf;
/* create records */
first.id = 10276;
strcpy(first.name, "Widget");
second.id = 11786;
strcpy(second.name, "Gadget");

/* write records to a file */


fptr = fopen("info.dat", "wb");
fwrite(&first, 1, sizeof(first), fptr);
fwrite(&second, 1, sizeof(second), fptr);
fclose(fptr);

/* file contains 2 records of type item */


fptr = fopen("info.dat", "rb");

/* seek second record */


fseek(fptr, 1*sizeof(item), SEEK_SET);
fread(&secondf, 1, sizeof(item), fptr);
printf("%d %s\n", secondf.id, secondf.name);
fclose(fptr);
return 0;
}
This program wrote two item records to a file. To read just the second record, fseek() moved the
file pointer to 1*sizeof(item) bytes from the start of the file. If you would want to get the fourth
record you would use 3*sizeof(item).

An exception is any situation that causes your program to stop normal execution.
C does not explicitly support exception handling, but there are ways to manage errors.
Write code to prevent errors In the first place, validate input and make sure division by 0 wont
occur. Use the exit statement to gracefully end program execution.
Use errno, perror() and strerror() to identify errors through code.
The exit command immediately stops execution of a program and sends an exit code back to the
calling process. Using exit to avoid a program crash is a good practice because it closes any open
file connections and processes. You can return any value through an exit statement, but 0 for
success and -1 for failure are typical. The predefined stdlib.h macros EXIT_SUCCESS and
EXIT_FAILURE are also commonly used.
int x = 10;
int y = 0;

if (y != 0)
printf("x / y = %d", x/y);
else {
printf("Divisor is 0. Program exiting.");
exit(EXIT_FAILURE);
}

Some library functions, such as fopen(), set an error code when they do not execute as expected.
The error code is set in a global variable named errno, which is defined in the errno.h header file.
When using errno you should set it to 0 before calling a library function.
To output the error code stored in errno, use fprintf to print to the strerr file stream, the standard
error output to the screen. Using strerr is a matter of convention and a good programming
practice. You can output the errno through other means, but it will be easier to keep track of your
exception handling if you only use strerr for error messages.
To use errno, you need to declare it with the statement extern int errno; at the top of your program,
or can include the errno.h header file.
#include <stdio.h>
#include <stdlib.h>
// #include <errno.h>

extern int errno;

int main() {
FILE *fptr;
int c;

errno = 0;
fptr = fopen("c:\\nonexistantfile.txt", "r");
if (fptr == NULL) {
fprintf(stderr, "Error opening file. Error code: %d\n", errno);
exit(EXIT_FAILURE);
}

fclose(fptr);
return 0;
}
When a library function sets errno, a cryptic error number is assigned. For a more descriptive
message about the error, you can use perror(). You can also obtain the message using strerror()
in the string.h header file, which returns a pointer to the message text.
perror() must include a string that will precede the actual error message. Typically there is no need
for both perror() and strerror() for the same error.
FILE *fptr;
errno = 0;
fptr = fopen("c:\\nonexistantfile.txt", "r");
if (fptr == NULL) {
perror("Error");
fprintf(stderr, "%s\n", strerror(errno));
exit(EXIT_FAILURE);
}
If there are more than a hundred error codes. Use these statements to list them:
for (int x = 0; x < 135; x++)
fprintf(stderr, "%d: %s\n", x, strerror(x));
Some mathematical functions in the math.h library set errno to the defined marcro value EDOM
when a domain is out of range. Similarly the ERANGE macro value is used when there is a range
error.
float k = -5;
float num = 1000;
float result;

errno = 0;
result = sqrt(k);
if (errno == 0)
printf("%f ", result);
else if (errno == EDOM)
fprintf(stderr, "%s\n", strerror(errno));

errno = 0;
result = exp(num);
if (errno == 0)
printf("%f ", result);
else if (errno == ERANGE)
fprintf(stderr, "%s\n", strerror(errno));
The feof() and ferror() functions can be used for determining file I/O errors:
feof(fp) Returns a nonzero value if the end of the stream has been reached, 0 otherwise. feof also
sets EOF.
ferror(fp) Returns a nonzero value if there is an error, 0 for no error.

FILE *fptr;
int c;

errno = 0;

fptr = fopen("myfile.txt", "r");


if (fptr == NULL) {
fprintf(stderr, "Error opening file. %s\n", strerror(errno));
exit(EXIT_FAILURE);
}

while ((c = getc(fptr)) != EOF) /* read the rest of the file */


printf("%c", c);

if (ferror(fptr)) {
printf("I/O error reading file.");
exit(EXIT_FAILURE);
}
else if (feof(fptr)) {
printf("End of file reached.");
}

The Pre-processor
The C pre-processor uses the # directives to make substitutions in program source code before
compilation. For example, the line #include <stdio.h> is replaced by the contents of the stdio.h
header file before a program is compiled.
Preprocessor directives and their uses:
#include including header files.
#define, #undef Defining and undefining macros.
#ifdef, #ifndef, #if, #else, #elif, #endif Conditional compilation.
#pragma Implementation and compiler specific.
#error, #warning Output an error or warning message An error halts compilation.
#include directive is for including header files in a program. A header file declares a collection of
functions and macros for a library, a term that comes from the way the collection of code can be
reused.
Useful C libraries include:
stdio input/output functions, including printf and file operations.
stdlib memory management and other utilities
string functions for handling strings
errno errno global variable and error code macros
math common mathematical funcitons
time time/date utilities
Corresponding header files for the libraries end with .h by convention. The #include directive
expects brackets around the header filename if the file should be searched for in the compiler
include paths.
A user-defined header file is also given the .h extension, but is referred to with quotation marks, as
in “myutil.h”. When quotation marks are used, the file is searched for in the source code directory.
#include <stdio.h>
#include “myutil.h”
Some devs use .hpp extension for header files.
The #define directive is used to create object-like macros for constants based on values or
expressions. #define can also be used to create function-like macros with arguments that will be
replaced by the pre-processor.
Be cautious with function-like definitions. Keep in mind that the pre-processor does a direct
replacement without any calculations, which can lead to unexpected results, as demonstrated:
#include <stdio.h>
#define PI 3.14
#define AREA(r) (PI*r*r)

int main() {
float radius = 2;
printf("%3.2f\n", PI);
printf("Area is %5.2f\n", AREA(radius)); // PI * r * r
printf("Area with radius + 1: %5.2f\n", AREA(radius+1));
return 0; //PI*r+1*r+1
}
The solution is to enclose each parameter in parentheses to obtain the correct order of operations
like:
#define AREA(r) (PI*(r)*(r))

When using pre-processor directives, the # must be the first character on a line. But there can be
any amount of white space before # and between the # and the directive.
If a # directive is lengthy, you can use the \ continuation character to extend the definition over
more than one line:
#define VERY_LONG_CONSTANT \
23.678901

#define MAX 100


#define MIN 0
# define SQUARE(x) \
x*x
In addition to defining your own macros, there are several standard predefined macros that are
always available in a C program without requiring the #define directive:
__DATE__ The current date as a string in the format Mm dd yyyy
__TIME__ The current time as a string in the format hh:mm:ss
__FILE__ The current filename as a string
__LINE__ The current line number as an int value
__STDC__ 1
char curr_time[10];
char curr_date[12];
int std_c;

strcpy(curr_time, __TIME__);
strcpy(curr_date, __DATE__);
printf("%s %s\n", curr_time, curr_date);
printf("This is line %d\n", __LINE__);
std_c = __STDC__;
printf("STDC is %d", std_c);

The #itdef, #ifndef, and #undef directives operate on macros created with #define.
For example, there will be compilation problems if the same macro is defined twice, so you can
check of this with an #itdef directive. Or if you may want to redefine a macro, you first use #undef.
#include <stdio.h>

#define RATE 0.08


#ifndef TERM // if TERM not defined
#define TERM 24
#endif
int main() {
#ifdef RATE /* this branch will be compiled */
#undef RATE
printf("Redefining RATE\n");
#define RATE 0.068
#else /* this branch will not be compiled */
#define RATE 0.068
#endif

printf("%f %d\n", RATE, TERM);

return 0;
}
Only the #ifdef clause will be compiled as RATE is defined at the top. The optional #else branch
compiles when #itdef RATE is false during preprocessing. An #endif is required to close the block
of code. An #elif directive is like an else if and can be used to provide additional alternatives after
#else.
Conditional compilation of segments of code is controlled by a set of directives: #if, #else, #elif,
and #endif.
#define LEVEL 4

int main() {
#if LEVEL > 6
/* do something */
#elif LEVEL > 5
/* else if branch */
#elif LEVEL > 4
/* another else if */
#else
/* last option here */
#endif

return 0;
}
This type of code should be used sparingly.
The defined() pre-processor operator can be used with #if, as in:
#if !defined(LEVEL)
/* statements */
#endif // the #if and if statements are not interchangeable
The C pre-processor provides the following operators
The # macro operator is called the stringification or stringizing operator and tells the pre-
processor to convert a parameter to a string constant.
White space on either side of the argument are ignored and escape sequences are recognized.
#define TO_STR(x) #x

printf("%s\n", TO_STR( 123\\12 ));


The ## operator is also called the token pasting operator because it appends, or “pastes”,
tokens together.
#define VAR(name, num) name##num
int x1 = 125;
int x2 = 250;
int x3 = 500;

printf("%d\n", VAR(x, 3));

You might also like