C Class Notes

Sometimes a textbook is too much, too many words, too much reading to find what you need. Sometimes online documentation isn't enough, too much detail, not enough perspective. These are class notes, filling the gap in between textbook and the documentation.

History and Basics of Computer Programming

What is a computer?

A tool with no specific purpose

Software gives it a purpose

What is a computer program (application, app, software, etc.)

Computer don't speak english (or any other human language)

How are non-binary languages made readable to a computer?

What is C?

C is a 3rd generation (procedural) compiled programming language

History of C

Software Setup

Compiling a First Program

/*
	A first sample Program
	author: Noah Singer
*/

#include <stdio.h>

int main(void) {
	printf("Welcome to the C Language!\n”); //this is the output
	return 0;
} //end main

save as welcome.c

With text editor

Write code and save Compile and link using: gcc welcome.c Compile should result in: a.out or a.exe depending on platform Run that program: a.out or a.exe depending on platform See output

With IDE (netbeans)

Start a new project

Select C/C++ as category Choose C/C++ Application as project Name project

Choose location

Select main file as being C, not C++ From source files select main.c Write code and save The hammer button compiles and links, errors shown in output window

The arrow button compiles, links, and runs, output shown in the output window

Text editor and command line vs IDE

IDE: realtime debugging, syntax help, heavy, slow, can cause problems itself

Text editor: edit / compile / run cycle, no hand holding, fast, simple so fewer problems

Online Documentation

http://www.acm.uiuc.edu/webmonkeys/book/c_guide/

http://en.cppreference.com/w/c

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf (official spec)

Dissecting a Simple C Program

Simple Sample Program

/*
	Another Sample Program
	by: Noah Singer
*/

#include <stdio.h>

void show_colors( void ) {
	printf("red\n"); /*using escape sequences for formatting*/
	printf("\tgreen\n");
	printf("\t\tblue\n");
}

int main( void ) {
	printf("The basic ");
	printf("colors are: \n");
	show_colors( ); //show the colors
	return 0;
}

rgb.c

Things to notice:

Comments

Include

Blank lines

void show_colors( void ) and int main( void )

printf statements

return

Another Sample Program

/*
	program to calculate the
	remaining days in the semester
*/

#include <stdio.h>

int main( void ) {
	int weeks_in_the_semester;
	int days_complete;
	int days_in_the_semester;
	weeks_in_the_semester = 16;
	days_complete = 7;
	days_in_the_semester = weeks_in_the_semester * 5; //5 days per week
	int days_remaining = days_in_the_semester - days_complete;
	printf( "There are %d days in the semester ", days_in_the_semester );
	printf( "and we've completed %d of them, ", days_complete );
	printf( "there are now %d days remaining\n\n", days_remaining );
	return 0;
}

days_remaining.c

Things to Notice:

Declarations

Move declarations around as an example

Change 3 lines of declarations to be one line

Change the good names to bad names to show the difference and added need for comments

Assignments

printf( )

change %d in one printf to x and then math will look wrong, change to f and there will be an error

Errors and debugging

/*
	Convert a F temp to C
*/ //leave this off for syntax error
#include <stdio.h> //typo this for syntax error

int main( void ) { //void in place of int for syntax error
	// C = (F-32)/1.8
	int temp_f = 32;
	//leave out parenthesis to cause a logic error
	int temp_c = (temp_f - 32) / 1.8;
	printf( "%d F equals %d C\n", temp_f, temp_c ); //missing , for syntax error
	return 0;
}

Getting Input from the User with scanf

/*
	scanf example
*/

#include <stdio.h>

int main( void ) {
	int age;
	printf( "Enter your age: " );
	scanf( "%d", &age ); //& required to show location of the variable
	printf( "Your age is %d\n", age );
	printf( "That's %d days old\n", (age * 365) );
} //end main

age.c

/* Find the area of a rectangle */
#include <stdio.h>

int main( void ) {
	int width, height;
	printf("Enter the rectangles width: ");
	scanf("%d",&width);
	printf("Enter the rectangles height: ");
	scanf("%d",&height);
	int area = width * height;
	printf( "The area of a %dx%d rectangle is %d\n", width, height, area );
} //end main

rect_area.c

Data and Datatypes

Int's and Float's

Integers

int x;
int x, y, z;
int x = 9;
int x = 9, y = 90;
int x, y = 90;
/*
	printf some int's
*/

#include <stdio.h>

int main( void ) {
	int x = 9;
	int y = x * 2;
	printf("orig=%d, times 2=%d, times 3=%d, times 4=%d\n", x, y, (x*3), 36);

	return 0;
} //end main

times.c

Octal and Hexadecimal

int d = 8;
int o = 010;
int h = 0x8;
printf("d=%d o=%d h=%d\n", d, o, h);

dec_oct_hex.c

Displaying Octal and Hexadecimal

d = 10;
o = 010;
h = 0x10;
printf("as decimal d=%d o=%d h=%d\n", d, o, h);
printf("as octal d=%o o=%o h=%o\n", d, o, h);
printf("as hexidecimal d=%x o=%x h=%x\n", d, o, h);
printf("\nnaturald=%d o=%#o h=%#x\n", d, o, h);

Add to dec_oct_hex.c

Overflow

/*
	overflowing int values
*/

#include <stdio.h>
#include <math.h>

int main( void ) {
	int i = 4294967296;
	printf( "16 bit: %f\n", pow(2, 16) ); //65536
	printf( "32 bit: %f\n", pow(2, 32) ); //4294967296
	printf( "64 bit: %f\n", pow(2, 64) ); //18446744073709551616
	printf("\ni is %d\n", i);

	return 0;
} //end main

overflow.c

Integer Type Modifiers

Large Integer Literals (Constants)

Printing Integer Types

/* big numbers */
#include <stdio.h>

int main( void ) {
	short planets = 10;
	int galaxies = 10000000; //10,000,000
	long stars = 1000000000000; //1,000,000,000,000
	long long atoms = 1000000000000000; //1,000,000,000,000,000
	printf( "number of planets: %hd\n", planets );
	printf( "number of galaxies: %d\n", galaxies );
	printf( "number of stars: %ld\n", stars );
	printf( "number of atoms: %lld\n", atoms );
	return 0;
} //end main

big_numbers.c

/* data type mashup */
#include <stdio.h>

int main( void ) {
	unsigned int u = 1000000000; //1,000,000,000
	short s = 100;
	long l = 65537;
	long long ll = 1234567890987654321;
	printf( "u as a u = %u, u as a d = %d\n", u, u );
	printf( "s as a h = %hd, s as a d = %d\n", s, s );
	printf( "l as a ld = %ld, l as a hd = %hd\n", l, l );
	printf( "ll as a lld = %lld, ll as a ld = %ld\n", ll, ll );
	return 0;
} //end main

mashup.c

Sometimes datatypes aren't explicitly stated

/* convert miles to inches */
#include <stdio.h>

int main( void ) {
	int miles;
	int feet_per_mile = 5280;
	int inches_per_foot = 12;
	int inches;
	printf( "Enter a number of miles: " );
	scanf( "%d", &miles );
	inches = inches_per_foot * feet_per_mile * miles;
	printf( "There are %d inches in %d miles\n", inches, miles );
	return 0;
} //end main

Works until around 100,000 miles on my computer, really fell apart at 1,000,000 Redeclare inches as long (it won't help) and change output type specifier The calculation is still done based on int Redeclare miles to be long and change input and output type specifiers

Having a long in the calculation causes everything to be done in terms of long

Char Type

Declaring and Printing

// char example
#include <stdio.h>

int main( void ) {
	char letter = 'C';
	printf( "the letter is: %c (with a value of %d)\n", letter, letter );
	letter = letter - 2;
	printf( "the letter is: %c (with a value of %d)\n", letter, letter );
	letter = letter + 19;
	printf( "the letter is: %c (with a value of %d)\n", letter, letter );
	return 0;
} //end main

chars.c

Buffered input problems

Demo problem when entering multiple concurrent characters needing " %c", buffered input problem.

#include <stdio.h>

int main(int argc, char const *argv[]) {
	char c;
	printf("Enter a letter:");
	scanf(" %c", &c);
	//     ^ space
	printf("You entered: %c\n", c );
	printf("Enter another letter:");
	scanf(" %c", &c); //space included to clear enter key from buffer
	//     ^ space
	printf("You entered: %c\n", c );
	return 0;
}

Nonprinting Characters

Sequence Meaning
\a alert
\b backspace
\f form feed
\n newline
\r carriage return
\t horizontal tab
\v vertical tab
\\ backslash
\' single quote
\” double quote
\? question mark
\0oo oct number (oo is the oct number)
\xhh hex number (hh is the hex number)
// escape sequences
#include <stdio.h>

int main( void ) {
	char back = '\b';
	printf( "Ruby" );
	printf( "%c%c%c%c", back, back, back, back );
	printf( "C is my favorite language\n" );

	return 0;
} //end main

Floating Point Numbers

number scientific notation C representation
1,000,000 1.0x106 1.0e6
435,678 4.35678x105 4.35678e6
123.456 1.23456x102 1.23456e2
.00001 1.0x10-5 1.0e-5

Declaring Float Variables and Using Float Literals

float average_price = 2.1234; //"normal" float value
float number_of_friends = .89;//small number, no leading integer
float gifts = 9e23; //big number, no fractional part
// no no's: don't leave out both the exponent and the decimal (looks like an int)
//leave out the fraction and integer parts of the number (nothing left)
//put spaces anywhere in the value

float1.c

float price_of_gifts_received = average_price * number_of_friends * gifts;
printf( "The price of all gifts received would be %f\n", price_of_gifts_received );
printf( "The price of all gifts received would be %e\n", price_of_gifts_received );

add to float1.c

formatting of float output, decimal places

// formatting float output
#include <stdio.h>

int main( void ) {
	float pi = 3.14159;
	//shows extra 0 because float naturally has 6 decimal places
	printf( "pi is %f\n\n", pi );
	printf( "pi is %.1f\n", pi ); //specify decimal places
	printf( "pi is %.2f\n", pi );
	printf( "pi is %.3f\n\n", pi );
	printf( "pi is %2.1f\n", pi ); //pads the beginning
	printf( "pi is %2.2f\n", pi );
	printf( "pi is %5.2f\n", pi );
	printf( "pi is %6.2f\n", pi );
	printf( "pi is %7.2f\n", pi );
	printf( "pi is %8.2f\n", pi );
	return 0;
} //end main

float2.c

double is assumed for literals

Float Overflows and Underflows

float a, b = 2e30 + 1.0;
a = b - 2e30;
printf( "what's left is: %f\n", a );

rounding.c, change type to a double to fix

Strings

// storing and showing strings
#include <stdio.h>
int main(void) {
	char first_name[15] = "Victrola";
	char last_name[15];
	printf( "The name is %s\n", first_name );
	//input needs to be 1 less than the size of the array for the null character
	printf( "What should %s's last name be? ", first_name );
	scanf( "%s", last_name );
	//if last_name input is too long, output is messed up
	printf( "Okay, so the whole name is %s %s\n", first_name, last_name );
	return 0;
} //end main

string_basics.c

#include <string.h>

int fn_length = strlen(first_name);
int ln_length = strlen(last_name);
int total_length = fn_length + ln_length;
printf( "first name: %d\nlast name: %d\ntotal: %d\n", fn_length, ln_length, total_length );

add to string_basics.c

Things That Aren't Variables

Compile Time Substitutions

// using pre-compiler directives for constants
#include <stdio.h>
#define PI 3.14159 //NO SEMICOLON or things get weird

int main(void) {
	float radius;
	char label[ 20 ];
	printf( "Enter a circles radius: " );
	scanf( "%f", &radius );
	printf( "How should this circle be labeled? " );
	scanf( "%s", label );
	float area = PI * radius * radius;
	float circ = 2 * PI * radius;
	printf( "The circle called %s with a radius of %.2f ", label, radius );
	printf( "has an area of %f and a circumference of %f.\n", area, circ );
	//PI = 1.234; //unassignable
	return 0;
} //end main

circle.c

Built-in Symbolic Constants

// some built in constants
#include <stdio.h>
#include <limits.h>
#include <float.h>

int main(void) {
	printf( "int max: %d, min: %d\n", INT_MIN, INT_MAX );
	printf( "charmax: %d, bit: %d\n", CHAR_MAX, CHAR_BIT );
	printf( "llong max: %lld, min: %lld\n", LLONG_MAX, LLONG_MIN );
	printf( "\n" );
	printf( "floatmantissa: %d, sig dig: %d, exp max: %d\n",
	FLT_MANT_DIG, FLT_DIG, FLT_MAX_10_EXP );
	return 0;
} //end main

builtin_constants.c

Constants

// convert miles to cm
#include <stdio.h>
int main(void) {
	long miles;
	const int FEET_PER_MILE = 5280;
	const int INCHES_PER_FOOT = 12;
	const float CM_PER_INCH = 2.54;
	float cm;
	printf( "Enter a number of miles: " );
	scanf( "%ld", &miles );
	cm = CM_PER_INCH * INCHES_PER_FOOT * FEET_PER_MILE * miles;
	printf( "There are %f centimeters in %ld miles\n", cm, miles );
	return 0;
} //end main

cm_per_mile.c

Operators

Expressions vs. Statements

// statements example

#include <stdio.h>
int main(void) {
	int num = 9;
	//plain expressions, will cause warning because their values aren't used
	4;
	4+5;
	4+5-num;
	num = 4+7-num;
	num;
	// proof of expression-ness
	printf( "output: %d\n", 4 );
	printf( "output: %d\n", (4 + 5) );
	printf( "output: %d\n", (4 + 5 - num) );
	printf( "output: %d\n", (num = 4 + 7 - num) );
	printf( "output: %d\n", num );
	// int num2 is not an expression so an error is generated
	//printf( "output: %d\n", int num2 );
	return 0;
} //end main

statements.c

Assignment

Addition

Subtraction

Signs

Multiplication

Division

Precedence

Modulus

// simulate rolling 2 dice

#include <stdio.h>
#include <stdlib.h> //gives srand and rand
#include <time.h> //gives time

int main( void ) {
	const int SIDES = 6;
	srand(time(NULL)); //init seed for random number
	int rolled_value = (rand( ) % SIDES) + 1;
	printf( "roll is %d\n", rolled_value );
	return 0;
} //end main

dice.c

Shortcut Operators

// example of shortcut operators

#include <stdio.h>

int main(void) {
	int new_number, total = 0;
	printf( "Enter the first number: " );
	scanf( "%d", &new_number );
	//total = total + new_number;
	total += new_number;
	printf( "Enter the second number: " );
	scanf( "%d", &new_number );
	total += new_number;
	printf( "Enter the third number: " );
	scanf( "%d", &new_number );
	total += new_number;
	printf( "The total of your three numbers is %d\n", total );
	return 0;
} //end main

shortcuts.c

Increment and Decrement

// inc and dec

#include <stdio.h>

int main(void) {
	int days = 3;
	printf( "the value of days is %d\n", days ); //3
	++days;
	printf( "the value of days is %d\n", days ); //4
	printf( "the value of days is %d\n", ++days ); //5
	printf( "the value of days is %d\n", days ); //5
	--days;
	printf( "the value of days is %d\n", days ); //4
	printf( "the value of days is %d\n", --days ); //3
	printf( "the value of days is %d\n", days ); //3
	printf( "\n-------------------------------\n\n" );
	printf( "the value of days is %d\n", days ); //3
	days++;
	printf( "the value of days is %d\n", days ); //4
	printf( "the value of days is %d\n", days++ ); //4
	printf( "the value of days is %d\n", days ); //5
	days--;
	printf( "the value of days is %d\n", days ); //4
	printf( "the value of days is %d\n", days-- ); //4
	printf( "the value of days is %d\n", days ); //3
	return 0;
} //end main

inc_and_dec.c

Casting

Go back and rework the miles to inches example from the integer section using casting to change the expression values instead of changing the data types of the involved variables.

// conversions and casting
#include <stdio.h>

int main(void) {
	int x = 9;
	float y = x; //widening conversion
	printf( "y is %f\n", y );
	float a = 1.234;
	int b = a; //narrowing conversion
	printf( "b is %d\n", b );
	float p = 2.33456;
	int q = (int)p; //cast, shows intention
	printf( "q is %d\n", q );
} //end main

casting.c

Compound Statements (Blocks)

// blocks

#include <stdio.h>

int main(void) {
	int seconds, minutes, hours;
	printf( "Enter a number of seconds: " );
	scanf("%d",&seconds);
	{
		minutes = seconds / 60;
		seconds = seconds % 60;
		hours = minutes / 60;
		minutes = minutes % 60;
	}
	printf( "that's %d hours, %d minutes, and %d seconds\n", hours, minutes, seconds );
	return 0;
} //end main

blocks.c

Control Structures

Structured vs Unstructured Programming Structured programs need to make decisions so we need comparisons

Comparison Operators

Booleans in C

<, >, <=, >=, ==, !=

&&, ||, !

// booleans

#include <stdio.h>

int main(void) {
	printf( "is 2 equal to 2: %d\n", (2 == 2) );
	printf( "is 2 equal to 3: %d\n\n", (2 == 3) );
	printf( "is 2 not equal to 2: %d\n", (2 != 2) );
	printf( "is 2 not equal to 3: %d\n\n", (2 != 3) );
	printf( "is 7 greater than to 9: %d\n", (7 > 9) );
	printf( "is 7 less than 9: %d\n\n", (7 < 9) );
	int true_val = (10 == 10);
	int false_val = (10 > 10);
	printf( "true value is: %d\n", true_val );
	printf( "false value is: %d\n", false_val );
	return 0;
} //end main

booleans.c

Repetition Structures

What is structured programming?

What are the 3 types of structures?

Terminology

while

Infinite Loops and how to prevent them

Example of validating user input, use return form scanf to check for blanks and check for number in range.

Example looping through a menu which should be a good lead in to to do..while loops

do..while

for

Nested Loops

Example: display all the letters of the alphabet 10 times

// multiplication table

#include <stdio.h>

int main(void) {
	const int MAX_ROWS = 10;
	const int MAX_COLS = 10;
	int rows = 0, cols = 0;
	//top header
	printf( " " ); //left padding
	while( cols <= MAX_COLS ) {
		printf( "%4d", cols++ );
	}

	//reset for underline
	printf( "\n" );
	cols = 0;
	//underline header
	printf( "-----" ); //left padding
	while( cols <= MAX_COLS ) {
		printf( "----" );
		cols++;
	}

	//reset for core table
	printf( "\n" );
	cols = 0;
	//core table
	while( rows <= MAX_ROWS ) {
		printf( "%2d | ", rows ); //left header
		while( cols <= MAX_COLS ) {
			printf( "%4d", (rows * cols) );
			cols++;
		}
		printf( "\n" );
		cols = 0;
		rows++;
	}
	return 0;
} //end main

multiplication_table.c

Selection Structures

if

if/else

nested if

Formatted time with leading zeros?

nested if/else

if with nested loop

loop with nested if(s)

If statements and flag variables

switch

switch in a loop

continue and break

Arrays

Arrays are a useful feature in most programming languages They allow for multiple values to be stored in a single variable (unlike typical scalar variables)

An array could be defined as a series of values of the same type stored sequentially

The individual values stored in an array are called elements and can be selected by using an integer position indicator called an index number or subscript where the first index number is always zero.

Declaring Arrays

float nums[12];
int scores[10];

char grades[5];

Accessing Array Elements

nums[0] = 1.1;
nums[1] = 7.9;
nums[0] = 3.7; //replace value
printf("Enter a number: ");
scanf("%f", &nums[2]);
printf("element 2 in the array is: %.2f\n", nums[2]);
printf("the first num in the array is: %.2f\n", nums[0]);
printf("element 3 in the array is: %.2f\n", nums[3]); //uninitialized
int i = 5;
nums[i] = 9.9;
printf("the value stored in element i is: %.2f\n", nums[i]);

Arrays and Loops

// arrays with loops

#include <stdio.h>

int main( void ) {
	const int ARRAY_SIZE = 10;
	int numbers[ARRAY_SIZE];
	int x, total = 0;
	for( x = 0 ; x < ARRAY_SIZE ; x++ ) {
		printf( "Enter number %d: ", (x+1) );
		scanf( "%d", &numbers[x] );
	}

	for( x = 0 ; x < ARRAY_SIZE ; x++ ) {
		total += numbers[x];
	}

	printf( "The total of your number is %d", total );
	printf( " and the average is %.2f\n", ((float)total/ARRAY_SIZE) );
	return 0;
} //end main

Arrays and Strings

/*
	strings and arrays
*/

#include <stdio.h>

int main( void ) {
	int x = 0;
	char first_name[20];
	char last_name[20];
	first_name[0] = 'B';
	first_name[1] = 'o';
	first_name[2] = 'b';
	first_name[3] = '\0';
	for( x = 0 ; x < 20 ; x++ ) {
		if( first_name[x] == '\0') {
			break;
		} else {
			printf("%c", first_name[x]);
		}
	}
	printf("\n");
	printf("%s\n", first_name);
	printf("--------------------\n");
	printf("Enter a last name: ");
	scanf("%s", last_name);
	printf("%s\n", last_name);
	for( x = 0 ; x < 20 ; x++ ) {
		if( last_name[x] == '\0' ) {
			break;
		} else {
			printf("%c\n", last_name[x]);
		}
	}
	return 0;
} //end main

Initializing an Array

// convert english to tut

#include <stdio.h>
#include <string.h>

int main( void ) {
	char input[50];
	int const NUMBER_OF_VOWELS = 6;
	char vowels[NUMBER_OF_VOWELS] = {'a','e','i','o','u', '!'}; //initializing
	int x;
	printf( "Enter an english word: " );
	scanf( "%s", input );
	for( x = 0 ; x < strlen( input ) ; x++ ) {
		char letter = input[x];
		// if( letter == 'a' || letter == 'e' || letter == 'i' || letter == 'o' || letter == 'u' ) {
			// printf( "%c ", letter );
		// } else {
			// printf( "%cut ", letter );
		// }

		int y, found = 0; //y is counter, found is flag (default false)
		for( y = 0 ; y < NUMBER_OF_VOWELS ; y++ ) {
			if( letter == vowels[y] ) {
				found = 1; //true
			}
		}

		if( found ) {
			printf( "%c ", letter );
		} else {
			printf( "%cut ", letter );
		}
	} //end for
	printf( "\n" );
	return 0;
} //end main
Example: array stores the number of a day's in each month, user picks month and is shown the number of days. Example: user inputs numbers to be stored in array, numbers in the array are totaled and averaged. Example: Multi-Dimensional Arrays
/*
	number_of_days
*/

#include <stdio.h>

int main( void ) {
	int days_per_month[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
	char month_names[13][10] = {"","January","February","March","April","May","June","July","August","September","October","November","December"};
	int month = 0;
	int days = 0;
	printf("Look up number of days for what month? (1-12) ");
	scanf("%d",&month);
	if( month < 1 || month > 12 ) {
		printf("That is not a valid month\n");
	} else {
		//processing
		days = days_per_month[month];
		printf("In month %s there are %d days\n", month_names[month], days);
	}
	return 0;
} //end main

Pointers

int *x_ptr;
// show memory addresses

#include <stdio.h>

int main( void ) {
	int x = 9;
	int *x_ptr = &x;
	printf( "the variable x's value is %d and its address is %p\n", x, &x );
	printf( "the variable x_ptr points to the value %d, ", *x_ptr );
	printf( "has a value of %p, ", x_ptr );
	printf( "and has an address of %p\n", &x_ptr );
	*x_ptr = 11;
	printf( "----\n" );
	printf( "the variable x's value is %d and its address is %p\n", x, &x );
	printf( "the variable x_ptr points to the value %d, ", *x_ptr );
	printf( "has a value of %p, ", x_ptr );
	printf( "and has an address of %p\n", &x_ptr );
	x = 15;
	printf( "----\n" );
	printf( "the variable x's value is %d and its address is %p\n", x, &x );
	printf( "the variable x_ptr points to the value %d, ", *x_ptr );
	printf( "has a value of %p, ", x_ptr );
	printf( "and has an address of %p\n", &x_ptr );
	return 0;
} //end main

pointer_example.c

// using scanf with pointers

#include <stdio.h>

int main( void ) {
	int num;
	int *num_ptr; //doesn't have to end in _ptr but it helps keep track
	printf( "Enter a number: " );
	scanf( "%d", &num ); //&num is the address
	printf( "You entered %d\n", num );
	printf( "Enter another number: " );
	scanf( "%d", num_ptr ); //num_ptr is also an address
	printf( "You entered %d\n", *num_ptr );
	//num and num_ptr aren't the same memory space (fooled by the name)
	printf( "num is %d and num_ptr is %d\n", num, *num_ptr );
	printf( "---\n" );
	//uninitialized pointers are dangerous because we don't know where they point
	num_ptr = &num;
	printf( "Enter a number: " );
	scanf( "%d", &num ); //&num is the address
	printf( "You entered %d\n", num );
	printf( "Enter another number: " );
	scanf( "%d", num_ptr ); //num_ptr is also an address
	printf( "You entered %d\n", *num_ptr );
	printf( "num is %d and num_ptr is %d\n", num, *num_ptr );
} //end main

scanfing.c

// pointers and arrays

#include <stdio.h>

int main( void ) {
	char letters[10] = {'a','b','c','d','e','f','g','h','i','j'};
	//name of the array is the address of the array's first element
	printf( "the arrays address is %p\n", letters );
	printf( "element 0's address is %p\n", &letters[0] );
	printf( "element 0 contains %c\n", letters[0] );
	printf( "element 0 contains %c\n\n", *letters );
	printf( "-----\n" );
	//when you add to a pointer (or sub) it uses the size of the ptr's type
	printf( "the arrays base address plus the size of one char is %p\n", (letters+1) );
	printf( "element 1's address is %p\n", &letters[1] );
	printf( "element 1 contains %c\n", letters[1] );
	printf( "element 1 contains %c\n\n", (*letters + 1) );
	printf( "-----\n" );
	int x;
	for( x = 0 ; x < 10 ; x++ ) {
		printf( "element %d: %c\n", x, letters[x] );
	}

	printf( "-----\n" );
	for( x = 0 ; x < 10 ; x++ ) {
		printf( "element %d: %c\n", x, *letters + x );
	}

	printf( "-----\n" );
	char *ptr;
	for( ptr = letters, x = 0 ; x < 10 ; x++) {
		printf( "element %d: %c\n", x, *ptr++ );
	}

	return 0;
} //end main

reverse an array with pointers, reverse.c

#include "stdio.h"

int main(void) {
	char letters[10] = {'a','b','c','d','e','f','g','h','i','j'};
	char r_letters[10];
	char * getter = letters + 9;
	char * setter = r_letters;
	int x;
	for(x=0 ; x < 10 ; x++, setter++, getter--) {
		*setter = *getter;
	}

	for(x=0 ; x < 10 ; x++) {
		printf("%c ", r_letters[x]);
	}

	printf("\n");
	return 0;
} //end main

pointers_and_arrays.c

Functions

Function - self-contained groups of statements that together perform a task Reasons to write functions example of a program that has repeated code and could benefit from functions
// simple math example

#include <stdio.h>

int main( void ) {
	int choice, num1, num2;
	do {
		printf( "1. Add two numbers\n" );
		printf( "2. Subtract two numbers\n" );
		printf( "3. Multiply two numbers\n" );
		printf( "4. Divide two numbers\n" );
		printf( "5. Exit\n" );
		printf( "What math do you want to do? " );
		scanf( "%d", &choice );

		switch( choice ) {
			case 1:
				printf( "Enter the first number: " );
				scanf( "%d", &num1 );
				printf( "Enter the second number: " );
				scanf( "%d", &num2 );
				printf( "%d + %d = %d\n", num1, num2, (num1+num2) );
				break;

			case 2:
				printf( "Enter the first number: " );
				scanf( "%d", &num1 );
				printf( "Enter the second number: " );
				scanf( "%d", &num2 );
				printf( "%d - %d = %d\n", num1, num2, (num1-num2) );
				break;

			case 3:
				printf( "Enter the first number: " );
				scanf( "%d", &num1 );
				printf( "Enter the second number: " );
				scanf( "%d", &num2 );
				printf( "%d * %d = %d\n", num1, num2, (num1*num2) );
				break;

			case 4:
				printf( "Enter the first number: " );
				scanf( "%d", &num1 );
				printf( "Enter the second number: " );
				scanf( "%d", &num2 );
				printf( "%d / %d = %f\n", num1, num2, ((float)num1/num2) );
				break;

			case 5: break; //blank
				default:
				printf( "That is not a choice\n" );
		} //end switch
	} while( choice != 5 );
	return 0;
} //end main

math.c

writing and calling

// function basics

#include <stdio.h>

void say_hello( void ) {
	printf( "Hello\n" );
}

int main( void ) {
	say_hello( ); //function call
	say_hello( );
	return 0;
} //end main

function_basics.c

prototyping

passing values

returning values

// arguments

#include <stdio.h>

void show_x( int );
int max_int( int, int );
float max_float( float, float );

int main( void ) {
	int x = 9;
	show_x( x );
	show_x( x + 11 );
	show_x( 3.14 );
	x = max_int( 188, x );
	show_x( x );
	printf( "%d\n", max_int( 22, 10 ));
	max_int( x, x ); //ignore returned value
	float f = max_float( 3.14, 4.13 );
	printf( "%.2f\n", f );
} //end main

void show_x( int value ) {
	printf( "X: %d\n", value );
}

int max_int( int num1, int num2 ) {
	if( num1 > num2 ) {
		return num1;
	}
	return num2;
}

float max_float( float num1, float num2 ) {
	if( num1 > num2 ) {
		return num1;
	} else {
		return num2;
	}
}

arguments.c

scope

// scope

#include <stdio.h>

void show_int( int );

int y = 10;
//int x = 9;

int main( void ) {
	int x = 11;
	printf( "%d\n", x );
	{
		printf( "%d\n", y );
	}
	show_int( x );
	printf( "%d\n", x );
	return 0;
} //end main

void show_int( int x ) {
	printf( "%d\n", x );
}

scope.c

passing by value vs passing by reference

// passing

#include <stdio.h>

void show_x( int );
int add10( int );
void add10p( int * );
void swap( int *, int * );

int main( void ) {
	int x = 9;
	show_x( x );
	x = add10( x );
	show_x( x );
	add10p( &x );
	show_x( x );
	int a = 1, b = 2;
	printf( "a: %d b: %d\n", a, b );
	swap( &a, &b );
	printf( "a: %d b: %d\n", a, b );
	return 0;
} //end main

void swap( int * num1, int * num2 ) {
	int temp = *num1;
	*num1 = *num2;
	*num2 = temp;
}

int add10( int num ) {
	num = num + 10; //num += 10
	return num;
}

void add10p( int * num ) {
	*num = *num + 10;
}

void show_x( int num ) {
	printf( "x: %d\n", num );
}

passing.c

passing arrays / pointers

// passing arrays

#include <stdio.h>

void show_array( int[], int );
void add_to_array( int[], int, int );
void add_to_array_p( int *, int, int );

int main( void ) {
	int numbers[5] = {10,20,30,40,50};
	show_array( numbers, 5 );
	add_to_array( numbers, 5, 7 );
	show_array( numbers, 5 );
	add_to_array_p( numbers, 5, 3 );
	show_array( numbers, 5 );
	return 0;
} //end main

void add_to_array_p( int * array, int size, int amount ) {
	int i;
	for( i = 0 ; i < size ; i++ ) {
		*(array + i) = *(array + i) + amount;
	}
}

void add_to_array( int array[], int size, int amount ) {
	int i;
	for( i = 0 ; i < size ; i++ ) {
		array[i] += amount;
	}
}

void show_array( int array[], int size ) {
	int i;
	for( i = 0 ; i < size ; i++ ) {
		printf( "%d: %d ", i, array[i] );
	}
	printf( "\n" );
}

passing_arrays.c

Returning Arrays

#include <stdio.h>

void dup_int_array( int [], int, int []);

int main(void) {
	int nums[5] = {2,4,6,8,10};
	//nums = embiggen(nums, 5, 10); // <-- won't work
	int new_nums[15];
	dup_int_array(nums, 5, new_nums);
	int x;
	for( x = 0 ; x < 15 ; x++ ) {
		printf("%d ", new_nums[x]);
	}
	printf("\n");
	return 0;
}

// doesn't work because we can't return with array notation
// int[] embiggen(int old_array[], int size, int increase) { ...
//
// also doesn't work because we're returning the address of a local variable
// int * embiggen(int old_array[], int size, int increase) { ...
	// int new_size = size + increase;
	// int new_array[new_size];
	//
	// int x;
	// for( x = 0 ; x < size ; x++ ) {
		// new_array[x] = old_array[x];
		// }
		//
	// return new_array;
// }

	void dup_int_array( int old_array[], int size, int new_array[]) {
		int x;
		for( x = 0 ; x < size ; x++ )
		new_array[x] = old_array[x];
	}
}

Static Values

// using the static keyword

#include <stdio.h>
//#include <stdlib.h> <-- for malloc

int * get_num( );
int * embiggen(int [], int, int);

int main(int argc, char const *argv[]) {
	int *num1 = get_num( );
	int *num2 = get_num( );
	printf("num1: %d\n", *num1);
	printf("num2: %d\n", *num2);
	int nums[3] = {4,3,2};
	int * new_array = embiggen(nums,3,7);
	int i;
	for( i = 0 ; i < 10 ; i++ ) {
		printf("%d ", new_array[i]);
	}
	printf("\n");
	return 0;
}

//warning because the address of a local variable is being returned
int * get_num( ) {
	//problem is that x is local so it's lifetime ends when the function ends
	int x = 9; //<-- make x static to extend it's lifetime (static int x = 9;)
	x = x + 2;
	return &x;
}

// the same trick doesn't help with returning pointers to arrays
int * embiggen(int old_array[], int size, int increase) {
	// this doesn't work because C(89?) doesn't allow variable length arrays
	//to be static, only in pure block scope
	// if this is what's needed it has to be created on the heap
	//static int * new_array;
	//new_array = (int*)malloc((size + increase) * sizeof(int));
	static int new_array[size + increase];
	int x;
	for( x = 0 ; x < size ; x++ ) {
		new_array[x] = old_array[x];
	}
	return new_array;
}
Redo math.c example with functions
// simple math example

#include <stdio.h>

int prompt_int( char[] );
void show_menu( void );
void get_two_ints( int *, int * );

int main( void ) {
	int choice, num1, num2;
	do {
		show_menu( );
		choice = prompt_int( "What math do you want to do? " );
		switch( choice ) {
			case 1:
				get_two_ints( &num1, &num2 );
				printf( "%d + %d = %d\n", num1, num2, (num1+num2) );
				break;

			case 2:
				get_two_ints( &num1, &num2 );
				printf( "%d - %d = %d\n", num1, num2, (num1-num2) );
				break;

			case 3:
				get_two_ints( &num1, &num2 );
				printf( "%d * %d = %d\n", num1, num2, (num1*num2) );
				break;

			case 4:
				get_two_ints( &num1, &num2 );
				printf( "%d / %d = %f\n", num1, num2, ((float)num1/num2) );
				break;

			case 5: break; //blank
			default:
				printf( "That is not a choice\n" );
		}
	} while( choice != 5 );
	return 0;
} //end main

int prompt_int( char msg[] ) {
	int num;
	printf( "%s", msg );
	scanf( "%d", &num );
	return num;
}

void get_two_ints( int *a, int *b ) {
	*a = prompt_int( "Enter the first number: " );
	*b = prompt_int( "Enter the second number: " );
}

void show_menu( void ) {
	printf( "1. Add two numbers\n" );
	printf( "2. Subtract two numbers\n" );
	printf( "3. Multiply two numbers\n" );
	printf( "4. Divide two numbers\n" );
	printf( "5. Exit\n" );
}

math.c

// bubble sort

#include <stdio.h>

void display_array( int[], int );
void sort_array( int*, int );
void swap( int*, int* );

int main(void) {
	int numbers[10] = {4,6,2,1,3,5,9,0,7,8};
	display_array( numbers, 10 );
	sort_array( numbers, 10 );
	display_array( numbers, 10 );
} //end main

void display_array( int array[], int size ) {
	int x;
	for( x = 0 ; x < size ; x++ ) {
		printf( "%d ", array[x] );
	}
	printf("\n");
}

void sort_array( int *array, int size ) {
	int swapped;
	do {
		swapped = 0; //false
		int x;
		for( x = 0 ; x < (size-1) ; x++ ) {
			printf( "comparing %d and %d...", array[x], array[x+1] );
			if( array[x] > array[x + 1] ) {
				swap( &array[x], &array[x + 1] );
				swapped = 1; //true, not done yet
			} else {
				printf("not swapped\n");
			}
		} //end for
	} while( swapped );
}

void swap( int *num1, int *num2 ) {
	printf( "swapping %d and %d\n", *num1, *num2 );
	int temp = *num1;
	*num1 = *num2;
	*num2 = temp;
}

sort_array.c

Header Files

Easy to make your own.

Just move function implementations over to a .h file then #include that .h file using quotes instead of angle brackets

int max_int( int num1, int num2 ) {
	if( num1 > num2 ) {
		return num1;
	}
	return num2;
}

float max_float( float num1, float num2 ) {
	if( num1 > num2 ) {
		return num1;
	} else {
		return num2;
	}
}

max.h

// arguments

#include <stdio.h>
#include "max.h"

void show_x( int );

int main( void ) {
	int x = 9;
	show_x( x );
	show_x( 11 );
	show_x( (int)3.14 );
	x = max_int( 188, x );
	show_x( x );
	printf( "%d\n", max_int( 22, 10 ));
	max_int( x, x ); //ignore returned value
	float f = max_float( 3.14, 4.13 );
	printf( "%.2f\n", f );
} //end main

void show_x( int value ) {
	printf( "X: %d\n", value );
}

args2.c

File IO

2 types of input and output 2 types of FileIO
// general stuff

#include <stdio.h>

int main( void ) {
	//outputs
	//buffered
	//show on console like normal and redirect to file
	// ./a.out > sample
	printf( "hello\n" );
	char greeting[] = {'h','e','l','l','o','\n'};
	int x;
	for( x = 0 ; x < 6 ; x++ ) {
		//unbuffered
		putchar( greeting[x] );
	}

	//inputs
	//buffered
	char input[10];
	scanf( "%s", input );
	//get from keyboard like normal and get by redirecting stdin from sample file
	// ./a.out < sample
	printf( "The input was: %s\n", input );
	int c;
	for( x = 0 ; x < 10 ; x++ ) {
		//unbuffered
		c = getchar( );
		printf( "%c", c );
	}
	return 0;
} //end main

buff_vs_unbuff.c

// read string

#include <stdio.h>

void readstring( char *, int );
int main( void ) {
	char input[ 10 ];
	printf( "Enter a string: " );
	readstring( input, 10 );
	printf( "You entered: %s\n", input );
	return 0;
} //end main

void readstring( char * array, int max ) {
	int x;
	char c = getchar( );
	for( x = 0 ; (x < (max-1)) && (c != '\n') ; x++ ) {
		*(array + x) = c;
		c = getchar( );
	}
	array[x] = '\0';
}

readstring.c

Unbuffered file IO

Hello
Doctor
Name
Continue
Yesterday
Tomorrow
//echo file

#include <stdio.h>

int main( void ) {
	FILE *fp;
	int c;
	fp = fopen( "messages.txt", "r" );
	c = fgetc(fp);
	// EOF is a constant
	while( c != EOF ) {
		printf( "%c", c );
		c = fgetc(fp);
	}
	fclose( fp );
	return 0;
} //end main

echo_file.c

// write to a file

#include <stdio.h>

int main( void ) {
	FILE *fp = fopen(“output.txt","w"); //also try with mode = “a”
	char letter;
	for( letter = 'A' ; letter <= 'Z' ; letter++ ) {
		fputc( letter, fp );
	}
	fclose( fp );
	return 0;
} //end main

write_file.c

//copy file

#include <stdio.h>

int main( int argc, char *argv[] ) {
	// printf( "there are %d arguments given\n", argc );
	if( argc != 3 ) {
		printf( "%s usage: %s source_file destination_file\n", argv[0], argv[0] );
		return 1;
	}

	FILE *source;
	source = fopen( argv[1], "r" );

	// fopen should return 0 if file can't be found
	if( source == 0 ) {
		printf( "%s error: %s does not exist\n", argv[0], argv[1] );
		return 2;
	}

	FILE *destination = fopen( argv[2], "w" );
	char c; //must be int? or binary files won't copy?
	c = fgetc(source);
	// EOF is a constant

	while( c != EOF ) {
		fputc( c, destination );
		c = fgetc(source);
	}

	fclose( source );
	fclose( destination );
	return 0;
} //end main

copy.c

Sample Data (tab delimited and don't forget the trailing \n):

luke\t90\t89\t95
han\t70\t80\t78
obi-wan\t100\t100\t100

grades.txt

Example of reading through a file and working with it's content in an unbuffered style (harder than buffered)

// unbuffered grade reader

#include <stdio.h>
#include <stdlib.h>

int main( void ) {
	FILE *grades_file = fopen( "grades.txt", "r" );
	int c;
	int field_num = 0;
	int char_num = 0;
	char field[20];
	char *end_ptr;
	long val, total = 0;
	float average = 0.0;
	while( (c = fgetc(grades_file)) != EOF ) {
		if( field_num == 0 ) {
			if( c == '\t' ) {
				field[char_num] = '\0';
				printf( "Name: %s\n", field );
				char_num = 0;
				field_num = 1;
			} else {
				field[char_num++] = c;
			}
		} else if( field_num != 0 ) {
			if( c == '\t' ) {
				val = strtol( field, &end_ptr, 10 );
				total += val;
				printf( "Grade: %ld Total: %ld\n", val, total );
				char_num = 0;
			} else if( c == '\n' ) {
				val = strtol( field, &end_ptr, 10 );
				total += val;
				average = total / 3.0;
				printf( "Grade: %ld Total: %ld Average: %.2f\n", val, total, average );
				total = 0;
				average = 0.0;
				char_num = 0;
				field_num = 0;
			} else {
				field[char_num++] = c;
			}
		}
	} //end while

	fclose( grades_file );
	return 0;
} //end main

grades_unbuffered.c

Buffered file IO

fprintf and fscanf can be used for doing buffered input and output to and from files. They work just like regular printf and scanf except they take a file pointer as their first argument.

#include <stdio.h>
int main( void ) {
	FILE *fp;
	fp = fopen( "messages.txt", "r" );
	char word[50];
	while( fscanf(fp, "%s", word) == 1 ) {
		printf( "%s\n", word );
	}
	fclose( fp );
	return 0;
} //end main

buffered_read.c

#include <stdio.h>

int main( void ) {
	FILE *fp;
	char letter;
	fp = fopen("output2.txt","w");
	for( letter = 'A' ; letter <= 'Z' ; letter++ ) {
		fprintf( fp, "%c", letter );
	}
	fclose( fp );
	return 0;
} //end main

buffered_write.c

Example of reading through the content of a file using a buffered technique (easier)

// buffered grade reader

#include <stdio.h>
#include <stdlib.h>

int main( void ) {
	FILE *grades_file = fopen( "grades.txt", "r" );
	char name[20];
	int grade1, grade2, grade3;
	float average = 0.0;

	while( fscanf( grades_file, "%s\t%d\t%d\t%d\n", name, &grade1, &grade2, &grade3 ) == 4 ) {
		// printf( "Name: %s Grade 1: %d Grade 2: %d Grade 3: %d\n", field, grade1, grade2, grade3 );
		average = (grade1 + grade2 + grade3) / 3.0;
		printf( "Name: %s Average: %.2f\n", name, average );
	} //end while

	fclose( grades_file );
	return 0;
} //end main

grades_buffered.c

fgets is another handy function for buffered input from file or command line

/*
	fgets to get input
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

int get_string( char [], int, FILE *);
long get_int(FILE *);

int main( void ) {
	char input[100];
	printf("Enter some input: ");

	//fgets returns null if read fails
	if( fgets(input,100,stdin) != NULL ) {
		if( (strlen(input) > 0) && (input[strlen(input) - 1] == '\n') ) {
			input[strlen(input) - 1] = '\0';
		}
		// \n will be at the end of the string unless the string passed the max length
		printf("You entered (%lu chars): %s\n", strlen(input), input);
	} else {
		printf("the input could not be read");
		return 1;
	}

	char sentence[50];
	printf("Enter another string: ");

	if( get_string( sentence, 50, stdin ) ) {
		printf("You entered (%lu chars): %s\n", strlen(sentence), sentence);
	} else {
		printf("the input could not be read");
	}

	printf("Enter a whole number: ");
	long num = get_int(stdin);
	printf("You entered: %ld\n", num);
	return 0;
} //end main

int get_string( char array[], int size, FILE * source ) {
	if( fgets(array,size,source) != NULL ) {
		if( (strlen(array) > 0) && (array[strlen(array) - 1] == '\n') ) {
			array[strlen(array) - 1] = '\0';
		}
		return 1; //true, success
	} else {
		return 0; //false, error
	}
}

long get_int( FILE * source ) {
	const int SIZE = 50;
	char input[SIZE];
	char * ptr; //get set by strtol to the position where the first non-number is found
	if( get_string( input, 50, source ) ) {
		////// remove commas from input
		int x, y;
		char buffer[SIZE];
		for( x = 0, y = 0 ; x < strlen(input) ; x++ ) {
			if( input[x] != ',' ) {
				buffer[y++] = input[x];
			}
		}
		return strtol(buffer, &ptr, 10); //replace line below
		//return strtol(input, &ptr, 10); //ptr is a char**
	} else {
		return 0;
	}
}

Basics of a CRUD for students:

start with just loading student data in to arrays.

Notice that functions have to be passed many arrays/arguments so are inconvenient to use

Have to guess at how many records will be read to declare size of array

Updating and saving wouldn't be too hard, but adding new and deleting students would be difficult because of having to resize the array

The “solution” to both problems, having to pass so many arrays, and having to work with the fixed sized arrays, is to use structs.

#include <stdio.h>

int load_grades( char *, char [][50], int [], int [], int [], int );
void display_students( char [][50], int [], int [], int [], int );

int main (int argc, char const *argv[]) {
	char student_names[10][50];
	int exam01[10];
	int exam02[10];
	int exam03[10];
	int number_read = load_grades( "grades.txt", student_names, exam01, exam02, exam03, 10 );
	printf("read %d students\n", number_read);
	display_students( student_names, exam01, exam02, exam03, number_read );
	return 0;
}

int load_grades( char * filename, char names[][50], int g1[], int g2[], int g3[], int size ) {
	int records_read = 0;
	FILE *grade_file = fopen(filename, "r");
	if( ! grade_file ) {
		printf("%s could not be opened.\n", filename);
		return records_read;
	}
	while( fscanf(grade_file, "%s\t%d\t%d\t%d", names[records_read], &g1[records_read], &g2[records_read], &g3[records_read]) == 4 ) {
		records_read++;
	}
	fclose(grade_file);
	return records_read;
} //end load_grades

void display_students(char names[][50], int g1[], int g2[], int g3[], int size ) {
	int x;
	for( x = 0 ; x < size ; x++ ) {
		printf("name: %s\nexam01: %d\nexam02: %d\nexam03: %d\n-----------\n", names[x], g1[x], g2[x], g3[x]);
	}
}

Structs

C structures are a new type of compound data structure Unlike arrays, structures can be created that will hold mixed types of data in a grouping To create a structure you have to begin by writing a structure definition that describes the data and types that the structure will hold when used.
struct car {
	char make[25];
	char model[25];
	int year;
};
The struct definition can then be used to declare variables, essentially the struct acts as a new datatype.
struct car family_car;
struct car sports_car;
struct car eco_car;
The individual values that are a part of each struct can then be accessed and used like any other variable by using the dot operator
printf(“Enter the family car's make: ”);
scanf(“%s”, family_car.make);
printf(“Enter the family car's model: ”);
scanf(“%s”, family_car.model);
printf(“Enter the family car's year: ”);
scanf(“%d”, &family_car.year);
printf(“family car: %d %s %s”, family_car.year, family_car.make, family_car.model);
structs can be initialized, similar to regular variables or arrays
struct eco_car = {
	“Ford”,
	“Microspec”,
	2009
};
Arrays of structs can also be created
#include <stdio.h>

struct student {
	char name[50];
	int exam01;
	int exam02;
	int exam03;
}
;
int load_grades( char *, struct student [], int );
void display_students( struct student [], int );

int main (int argc, char const *argv[]) {
	struct student all_students[10];
	int number_read = load_grades( "grades.txt", all_students, 10 );
	printf("read %d students\n", number_read);
	display_students( all_students, number_read );
	return 0;
}

int load_grades( char * filename, struct student students[], int size ) {
	int records_read = 0;
	FILE *grade_file = fopen(filename, "r");

	if( ! grade_file ) {
		printf("%s could not be opened.\n", filename);
		return records_read;
	}

	while( fscanf(grade_file, "%s\t%d\t%d\t%d", students[records_read].name, &students[records_read].exam01, &students[records_read].exam03, &students[records_read].exam03) == 4 ) {
		records_read++;
	}

	fclose(grade_file);
	return records_read;
} //end load_grades

void display_students(struct student students[], int size ) {
	int x;

	for( x = 0 ; x < size ; x++ ) {
		printf("name: %s\nexam01: %d\nexam02: %d\nexam03: %d\n-----------\n", students[x].name, students[x].exam01, students[x].exam02, students[x].exam03);
	}
}

By using structs in this way we fix one of the issues we had with the purely array based version of this program in that we now only have 1 array we have to pass to the functions.

Structures can also be nested

#include <stdio.h>

struct song {
	char title[50];
	int duration_minutes;
	int duration_seconds;
};

struct album {
	char title[50];
	struct song tracks[3];
};

void display_album_and_tracks(struct album);
int main (int argc, char const *argv[]) {
	struct album nonsuch = {
		"Nonsuch",
		{
			{"The Ballad of Peter Pumpkinhead",5,2},
			{"My Bird Performs",3,51},
			{"Dear Madam Barnum",2,48}
		}
	};
	display_album_and_tracks(nonsuch);
	return 0;
}

void display_album_and_tracks(struct album the_album) {
	int x;
	printf("%s\n", the_album.title);
	for( x = 0 ; x < 3 ; x++ ) {
		printf( "\t%s\t\t%d:%d\n", the_album.tracks[x].title,the_album.tracks[x].duration_minutes,the_album.tracks[x].duration_seconds );
	}
}
When structs are passed to functions they are passed by value like everything else, but unlike arrays, the name of a struct isn't a secret pointer.
#include <stdio.h>

struct rect {
	int width;
	int height;
};

void show_rect( struct rect );
void embiggen_rect( struct rect );

int main (int argc, char const *argv[]) {
	struct rect r1 = {2,3};
	show_rect(r1);
	embiggen_rect(r1);
	show_rect(r1); //point hasn't moved
	return 0;
}

void show_rect( struct rect shape ) {
	printf("Rectangle\n\tw: %d\n\th: %d\n", shape.width, shape.height);
}

void embiggen_rect( struct rect shape ) {
	shape.width = shape.width + 1;
	shape.height = shape.height + 1;
}

Changing the program to allow a pointer to a struct will get halfway to a solution

void embiggen_rect( struct rect * ); // prototype

embiggen_rect(&r1);// call

void embiggen_rect( struct rect * shape ) {// function
	*shape.width = *shape.width + 1;
	*shape.height = *shape.height + 1;
}
This should work but produces an error.

error: indirection requires pointer operand

The problem is with the precedence of the of the dereference operator and the dot operator. The dereference has a lower precedence than the dot so as a result the access to the struct is attempted before the pointer is dereferenced. This can be fixed with parenthesis:
void embiggen_rect( struct rect * shape ) {
	(*shape).width = (*shape).width + 1;
	(*shape).height = (*shape).height + 1;
}
but it can also be fixed with a special operator just for this purpose, the arrow.
void embiggen_rect( struct rect * shape ) {
	shape->width = shape->width + 1;
	shape->height = shape->height + 1;
}
The arrow does the same job as the combination of the deference and the dot operators with the correct order of operations.
#include <stdio.h>

#include <stdlib.h>

struct rect {
	int width;
	int height;
};

int main (int argc, char const *argv[]) {
	struct rect r1;
	struct rect *r2 = &r1;
	struct rect *r3 = malloc(sizeof(struct rect));
	r1.width = 1;
	r1.height = 2;
	printf( "r1 is %dx%d\n", r1.width, r1.height);
	printf( "r2 is %dx%d\n", r2->width, r2->height);
	r3->width = 3;
	r3->height = 4;
	printf( "r3 is %dx%d\n", r3->width, r3->height);
	free(r3);

	return 0;
}

Linked Lists Example

// list of nodes

#include <stdio.h>
#include <stdlib.h>

struct node {
int number;
struct node *next;
};

void add_node(struct node **, int);
void show_all(struct node *);
void show_next(struct node *);
void free_all(struct node *);
void free_next(struct node *);

int main( void ) {
	struct node *head = NULL;
	printf("adding nodes\n");
	//head = malloc(sizeof(struct node));
	//head->number = 11;
	//head->next = NULL;

	add_node(&head, 7);
	//head->next = malloc(sizeof(struct node));
	//head->next->number = 25;
	//head->next->next = NULL;

	add_node(&head, 9);
	add_node(&head, 11);
	add_node(&head, 13);
	add_node(&head, 15);

	//if( head == NULL ) {
		//printf("No nodes in the list\n");
		//return 1;
	//}

	printf("showing nodes\n");
	show_all(head);
	printf("freeing nodes\n");
	free_all(head);
	return 0;
} //end main

void add_node(struct node **current, int val ) {
	printf("adding %d: ", val);
	if( *current == NULL ) {
		*current = malloc(sizeof(struct node));
		(*current)->number = val;
		(*current)->next = NULL;
		printf("root node was null, created new root node, %p\n", *current);
	} else {
		if( (*current)->next != NULL ) {
			printf("not at end of list, recursing\n");
			add_node(&(*current)->next, val );
		} else {
			(*current)->next = malloc(sizeof(struct node));
			(*current)->next->number = val;
			(*current)->next->next = NULL;
			printf("at end of list, adding new node, %p\n", (*current)->next);
		}
	}
}

//for showing all items in the list
void show_all(struct node * head) {
	show_next( head );
}

void show_next(struct node * current) {
	printf("%d\n", current->number);
	if( current->next != NULL ) {
		show_next( current->next );
	}
}

//for freeing used memory
void free_all(struct node * head) {
	free_next( head );
}

void free_next(struct node * current) {
	if( current->next != NULL ) {
		free_next( current->next );
	}
	printf("freeing node: %d\n", current->number);
	free(current);
}