Introduction to the C Programming Language
/* file header block comment */ #include <stdio.h> int main( ) { // print the greeting ( // allowed with C99 ) printf( “Hello World\n”); return 0; }
cc
gcc
to compile C (and C++ programs)a.out
Program is executed by calling name of executable at Unix prompt:
E.g.
unix>./hello
C allows you to declare and define variables
A declaration puts the variables name in the namespace
A definition allocates memory
An initialization (optional) sets initial value to be
stored in variable
C Declaration Example
In C, combined declaration and definition is typical
char example1; //definition and declaration int example2 = 5; //def. decl. and init. void example3(void){ //def. and decl. of a function int x = 7; }
The “extern” keyword may be added to declare that definition will be provided elsewhere
extern char example1; extern int example2; void example3(void);
Line 3: A function which does not provide function definition is sufficient for the compiler. This declaration is called a prototype. From this, the compiler knows how to implement placeholder information for calls, after-which the linker will resolve how to make the calls.
=
character and ends with semicolon ;
temperature1 = 3; temperature2 = temperature1;
LDI
short
must not be larger than an int
int
must not be larger than a long int
short int
must be at least 16 bitsint
must be at least 16 bitslong int
must be at least 32 bitslong long int
must be at least 64 bitsTo avoid ambiguity of variable sizes on embedded systems, named types that make size apparent should be used
predefined fixed-width integer types:
int8_t
- signed char
uint8_t
- unsigned char
int16_t
- signed int
uint16_t
- unsigned int
int32_t
- signed long
uint32_t
- unsigned long
These are defined in stdint.h
, which is included through inttypes.h
, using typedef
#include <inttypes.h>
#include <stdint.h>
long double
provides at least as much precision as double
double
provides at least as much precision as float
float avg = 10.6; double median = 88.54; double homeCost = 10000;
char x = 'A';
char x = 65;
A
as 65
const double PI = 3.14159; const int myAge = 24; const float PI; //valid, PI=0 PI = 3.14159; //invalid
sizeof()
operator to#include <stdbool.h> bool isRaining = false; if(isRaining) printf(“Bring your umbrella\n”);
Logical Operators are closely similar in C and python and result in boolean value
Integral types may also be treated as boolean
expressions
0
considered falseCommon Error: assign instead of equality check
#include <stdio.h> int main(){ int a=0; if (a=1) printf("oops\n"); }
caught by -Wall
option:
gcc -Wall equalitycheck.c
equalitycheck.c: In function ‘main’:
equalitycheck.c:4:7: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
4 | if (a=1) printf("oops\n");
More Common Error
Ignoring warnings
int main(){ int i=7; int t; if(i==7) { i=i+j; int k; //forbidden by c89 standard (c99 okay) k=i*i; t=k*2; } }
if (expression) (statement)
e.g.
if(x>3) x+=1; //simple form
if(expression) { //simple form with {} to group statement; statement; } if(expression){ //full if/else form statement; } else { statement; }
if(expression1) { statement 1; } else if (expression2) { statement2; } else { statement3; }
K&R style
if(expression) { statement; }else { statement; }
Stroustrup
if (expression) { statement; } else { statement; }
Allman
if (expression) { statement; } else { statement; }
There are many spacing styles for logic blocks. Pick one and be consistent.
https://en.wikipedia.org/wiki/Indentation_style
Liunx indent
indent (sudo apt install indent
)- changes the appearance of a C program by inserting or deleting whitespace.
Can manipulate many aspects of indentation, braces, line spacing, etc...
Example KR Style:
indent -kr orig.c -o indented.c
Example Allman/BSD style (from https://en.wikipedia.org/wiki/Indent_(Unix)):
indent -st -bap -bli0 -i4 -l79 -ncs -npcs -npsl -fca -lc79 -fc1 -ts4 orig.c -o indented.c
switch (expression) { case const-expression-1: statement; break; case const-expression-2: statement; break; case <const-expression-3>: //combined case 3 and 4 case <const-expression-4>: statement; break; case <const-expression-5>: //no break mistake? maybe statement; case <const-expression-6>: statement; break; default: // optional statement; }
break;
is not providedOmitting the break statements is a common error
compiles, but leads to inadvertent fall-through behavior. This behavior is just like the assembly jump tables it implements.
while(expression){ //executes 0 or more times statement; }
do{ //executes 1 or more times statement; } while(expression)
for(initialization; continuation; action){ statement; }
for(; continuation; action){ statement; }
int i = 99; for(; i!=0;){ statement; i-=1; }
for (int i = 99; i!=0; i=i-1){ statement; }
These are equivalent.
The second one is much more readable.
The second one also uses the C99 variable declaration inside the for loop.
while(expression){ statement; statement; if(condition) break; statement; statement; } //control jumps here on break.
Continue
while(expression){ statement; if(condition) continue; statement; statement; //control jumps here on continue }
goto label1; //unconditional jump int n=99; //declares variable, and represents and initialization label1: //here n is not initialized to 99
int larger=(x>y ? x:y);
larger=x if x>y else y
int grades[30]; int areas[10] = {1,2,3}; long widths[12] = {0}; int IQs[] = {120, 121, 99, 154};
C99 syntax allows size of array to be a variable
int numStudents = 30; int grades[numStudents];
int board[4][5]; // 4 rows, 5 columns int x = board[0][0]; //1st row, 1st column int y = board[3][4]; //4th (last) row, 5th (last) column
#define PI 3.14159 … double area = PI * radius * radius;
Good practice involves a generous use of parenthesis with #define
#define NUMBER (2)
Examples:
Ex 1
const int NUMBER = -42; int main(){ int x = -NUMBER; }
#define NUMBER -42
#define NUMBER (-42)
Ex 2
#define NUMBER 5+2 int x = 3 * NUMBER;
Value of x
(int x = 3 * 5 + 2)
vs (int x = 3 * 7)
#define NUMBER (5+2)
Ex 3: be careful with semicolons
#define NUMBER 5+2; int x = NUMBER * 3;
Compiler error
int x = 5 + 2; * 3;
Longer Example
File: constvsdefine.c
#include <stdio.h> #define NEG_ONE_DEFINE (-1) const int NEG_ONE_CONST = -1; const unsigned int MAGIC = -1; #include <stdint.h> int main(){ uint32_t u32=0; int32_t i32; i32=NEG_ONE_DEFINE; u32=NEG_ONE_CONST; if (u32>NEG_ONE_DEFINE){ } if (u32>NEG_ONE_CONST){ } if (MAGIC > 1){ printf("magic\n"); } i32=1; if (MAGIC>i32){ printf("more magic\n"); } u32=NEG_ONE_DEFINE; return 0; }
robucci@sensi:~$ gcc -std=c11 -Wall -Wall -Wextra -Wconversion -Wsign-conversion constvsdefine.c constvsdefine.c:5:28: warning: unsigned conversion from ‘int’ to ‘unsigned int’ changes value from ‘-1’ to ‘4294967295’ [-Wsign-conversion] 5 | const unsigned int MAGIC = -1; | ^ constvsdefine.c: In function ‘main’: constvsdefine.c:15:7: warning: conversion to ‘uint32_t’ {aka ‘unsigned int’} from ‘int’ may change the sign of the result [-Wsign-conversion] 15 | u32=NEG_ONE_CONST; | ^~~~~~~~~~~~~ constvsdefine.c:17:10: warning: comparison of integer expressions of different signedness: ‘uint32_t’ {aka ‘unsigned int’} and ‘int’ [-Wsign-compare] 17 | if (u32>NEG_ONE_DEFINE){ | ^ constvsdefine.c:20:10: warning: comparison of integer expressions of different signedness: ‘uint32_t’ {aka ‘unsigned int’} and ‘int’ [-Wsign-compare] 20 | if (u32>NEG_ONE_CONST){ | ^ constvsdefine.c:28:12: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int32_t’ {aka ‘int’} [-Wsign-compare] 28 | if (MAGIC>i32){ | ^ constvsdefine.c:3:24: warning: unsigned conversion from ‘int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘-1’ to ‘4294967295’ [-Wsign-conversion] 3 | #define NEG_ONE_DEFINE (-1) | ^ constvsdefine.c:32:7: note: in expansion of macro ‘NEG_ONE_DEFINE’ 32 | u32=NEG_ONE_DEFINE; | ^~~~~~~~~~~~~~
Note attribution of line 32 expansion issue to line 3, whereas line 5 is flagged in at the onset
robucci@sensi:~$ ./a.out
magic
more magic
-E
option to the previous gcc to stop after preprocessing prints outputgcc -E -x c -P -C -traditional constvsdefine.c > preprocessoroutput.c
tail of preprocessoroutput.c:int main(){ uint32_t u32=0; int32_t i32; i32=(-1); u32=NEG_ONE_CONST; if (u32>(-1)){ } if (u32>NEG_ONE_CONST){ } if (MAGIC > 1){ printf("magic\n"); } i32=1; if (MAGIC>i32){ printf("more magic\n"); } u32=(-1); return 0; }
enum
as a list of named constantenum months{ JAN=1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC};
enum months thisMonth
thisMonth=SEP;
//okthisMonth=42;
//unfortunately, also ok#include <stdio.h> typedef double Radius; #define PI (3.1415) /* given the radius, calculates the area of a circle */ double CircleArea( Radius radius ){ return ( PI * radius * radius ); } // given the radius, calculates the circumference of a circle double Circumference( Radius radius ){ return (2 * PI * radius ); } int main( ){ Radius radius = 4.5; double area = circleArea( radius ); double circumference = Circumference( radius ); // print the results return 0; }
#include <stdio.h> typedef double Radius; #define PI (3.1415) /* function prototypes */ double CircleArea( Radius radius ); double Circumference( Radius radius ); int main( ){ Radius radius = 4.5; double area = circleArea( radius ); double circumference = Circumference( radius ); //print the results here return 0; } /* given the radius, calculates the area of a circle */ double CircleArea( Radius radius ){ return ( PI * radius * radius ); } // given radius, calcs circumference of circle double Circumference( Radius radius ){ return (2 * PI * radius ); }
• includes • typedefs • defines • function prototypes • main • function definitions