Review of C
Last Updated:05/25/2018
C program componentsInclude filesC preprocessor includes the contents of an included file into your program. We include both builtin C ".h" files (surrounded by >) and our own ".h" files (surrounded by double quotes).
main functionJust like in Java, C needs a main function which is the first function launched. Later, our code will have many functions. The main is returning an integer value (0 indicates a normal return). The argc and argv are for command line arguments passed to the program. We aren't actually passing command line arguments so this will be ignored. The C source code for the main function is surrounded by {}.
DeclarationsWithin a function, we can define many variables. Variable declarations start with the data type for each variable. The variable student was declared to be of type Student which is defined in cs1713p0.h.
Function bodyThe detailed code for a function follows the variable declarations.
Some important statements in this example:
printfuses a format specifier to improve readability of formatted output. Notice that the first printf is printing a column heading.
whileloops while the specified condition is true.
ifprovides conditional flow
returnexits the function, returning the specified value.
It isn't important yet for you to understand the details of printf, fgets, and sscanf. / Example 1: complete program which reads student information containing three exams, and ID, and a student name. It prints that information and the average for each student.
#include<stdio.h>
#include"cs1713p0.h"
intmain(intargc, char *argv[])
{
Student student; // holds student data
FILE *pfileStudent; // FILE variable for student file
char szInputBuffer[MAX_LINE_SIZE + 1]; // input buffer for fgets
int iScanfCnt; // scanf returns the number of
// successful inputs
double dAverage; // grade average for a student
pfileStudent = stdin;
// Print a heading
printf("%-10s %-20s %10s %10s %10s %10s\n"
, "ID", "Name", "Exam 1", "Exam 2", "Final", "Average");
// read data input lines of text until EOF. fgets returns NULL at EOF
while (fgets(szInputBuffer, MAX_LINE_SIZE, pfileStudent) != NULL)
{
// if the line is just a line feed, skip it.
if (szInputBuffer[0] == '\n')
continue;
iScanfCnt = sscanf(szInputBuffer, "%lf %lf %lf %6s %20[^\n]\n"
, &student.dExam1
, &student.dExam2
, &student.dFinalExam
, student.szStudentIdNr
, student.szStudentFullNm);
// Check for bad input. scanf returns the number of valid conversions
if (iScanfCnt < 5)
{
printf("invalid input when reading student data, only %d valid values. \n"
, iScanfCnt);
printf("\tdata is %s\n", szInputBuffer);
returnERR_BAD_INPUT;
}
dAverage = (student.dExam1 + student.dExam2 + student.dFinalExam) / 3;
printf("%-10s %-20s %10.2f %10.2f %10.2f %10.2f\n"
, student.szStudentIdNr
, student.szStudentFullNm
, student.dExam1
, student.dExam2
, student.dFinalExam
, dAverage);
}
fclose(pfileStudent);
return 0;
}
Contents of the cs1713p0.h include file
We typically define constants, typedefs, and prototypes in ".h" files.
We defined two constants (MAX_LINE_SIZE and ERR_BAD_INPUT) that are used in the program.
This include file defined the Student data type. It is a structure that contains five attributes.
This include file didn't define any function prototypes since the program is only using builtin functions. / Example 2: cs1713p0.h include file
/* constants */
#define MAX_LINE_SIZE 100
#define ERR_BAD_INPUT 2
/* typedefs and function prototypes */
/* Student typedef contains exam scores, student IDs, and full name */
typedef struct
{
double dExam1; // exam #1 score
double dExam2; // exam #2 score
double dFinalExam; // final exam score
char szStudentIdNr[7]; // Student Identification Nr
char szStudentFullNm[21]; // Student full Nm
} Student;
Compiling and Executing a C program
A C program in Linux should have a .c file extension. To compile it, the gcc command is entered on a command line in a terminal window:
$ gcc -g -o p0 cs1713p0.c
That gcc command will compile (and link) cs1713p0.c producing an executable name p0. Note that the files for executable code in Linux doesn't have a file extension. Explanation of the arguments:
-gTells the compiler to generate code in the executable which will help debugging using the ddd command.
-oThis tells the compiler what to call its output which is the next command argument.
p0Since this follows the -o command switch, it is the name of the output created by gcc. This will be the name of the executable.
cs1713p0.cThe name of the C source file to compile. If a -c command switch had been included, gcc would only compile it. Without the -c, gcc will compile and link the C code, producing an executable. / Example 3: Compiling, linking, and executing a C program
# These examples assume that $ is the command prompt provided by the
# Linux terminal window.
# To only compile cs1713p0.c, producing cs1713p0.o:
$ gcc -g -c cs1713p0.c
# To link the object code file, producing p0:
$ gcc -g -o p0 cs1713p0.o
# The following gcc command will both compile and link cs1713p0.c,
# producing p0 which is an executable.
$ gcc -g -o p0 cs1713p0.c
# create a data file using the vi editor and enter the data shown
$ vi p0Input.txt
100 90 100 1111 Bill Board
50 30 40 2222 Marcus Absent
100 100 100 333 Jean E Us
# To execute p0 which is in the current directory:
$ ./p0 < p0Input.txt
ID Name Exam 1 Exam 2 Final Average
1111 Bill Board 100.00 90.00 100.00 96.67
2222 Marcus Absent 50.00 30.00 40.00 40.00
333 Jean E Us 100.00 100.00 100.00 100.00
Declarations of simple variables
In C, we must provide a data type for each variable.
dataType variableName = initialValue;
The initialization is optional.
Some of the data types:
intinteger (usually 4 bytes)
longlong integer (either 4 bytes on most 32 bit machines or 8 bytes on 64-bit machines)
float4 byte floating point
double8 byte floating point
charsingle character; one byte
Note that C doesn't provide a builtin boolean data type. We use the int data type for booleans. False is 0. Nonzero values are considered to be true. / Example 4: example declarations of simple variables
int iStudentCount; // int, but its value would be garbage
int iCount = 0; // int, initialized to 0
double dSum = 0.0; // double, initialized to 0.0
char cLetterGrade = 'F'; // single character, not a string
int bFound = 0; // boolean, 0 is false,
// non-zero is true.
Declarations of Arrays
dataType variableName[numberOfElements];
dataType variableName[] = {initialValueList};
Note that subscripts in C begin with 0. (Some languages have subscripts that begin with 1 such as COBOL and FORTRAN. PL/I by default begins with 1, but can be changed to any integer. Java and Python begin subscripts with 0.)
To declare a string of characters (note strings end with a zero byte terminator):
char variableName[sizePlus1];
char variableName[] = initialValue;
A zero byte is the constant '\0'. A literal character string surrounded in double quotes is automatically terminated with a zero byte.
Note that arrays of characters do not have to be zero byte terminated, but if they aren't, several C functions do not work properly. We try to be clear in coding for whether the string is zero-terminated or not. Our convention:
szName- string terminated by zero byte
sbName - string not terminated by zero byte. It might contain binary data. / Example 5: example declarations of arrays
double dExamWeightingM[10] = {200.0, 200.0, 300.};
#define NUMBER_OF_EXAMS 3
double dExamWeightingM[NUMBER_OF_EXAMS]
= {200.0, 200.0, 300.};
double dExamWeightingM[] = {200.0, 200.0, 300.};
char szFirstName[21]; // max of 20 characters
// plus zero byte
char szFileName[] = "myInput.txt";
char szABC[] = {'A', 'B', 'C', '\0'};
char szABC[] = "ABC"; // automatically zero
// byte terminated
A / B / C / \0
char sbDelims[] = {',', ' ', '\t', '\n'};
int iDelimCount = sizeof(sbDelims);
printf("number of delims = %d\n", iDelimCount);
Flow Control Statements in C
C and Java have very similar flow control statements. You have already seen the if,while, and for statements in Java. Most of you have also seen the switch statement.
if statement
The if statement is the most important statement for flow control (as it is in most languages). Syntax options:
if (conditionalExpr)
truePart;
if (conditionalExpr)
truePart;
else
falsePart;
Comparison operators:
== equal to
!= not equal to
> greater than
< less than
>= greater than or equal to
<= less than or equal to
Logical operators:
& and
|| or
! not / Example 6: if statements
if (cLetterGrade == 'W')
{
printf("last day to drop is %s\n", szDropDate);
dCourseGradePoints = 0.0;
dCourseCredits = 0.0;
}
if (dExamScore == 0)
iScoreCount += 1;
if (strcmp(szCommand,"DEPOSIT") == 0)
dBalance += dAmount;
else if (strcmp(szCommand, "WITHDRAWAL") == 0)
{
if (dBalance < dAmount)
{
printf("overdrawn\n");
return(ERROR_OVERDRAWN);
}
else
dBalance -= dAmount;
}
Comparison of Characters and Strings
Single characters are compared using ==, <, <=, >, >=, !=
To check whether two strings are equal, use
strcmp(szString1, szString2) == 0.
strcmp(szString1, szString2) returns a value < 0 if the value of szString1 is less than the value of szString2. / Example 7: character and string comparisons
if (cGender == 'M')
printf("stallion");
else
printf("mare");
if (strcmp(szTransaction, "DEPOSIT") == 0)
dBalance += dAmount;
else
dBalance -= dAmount;
if (strcmp(szName, "MARTIN") < 0)
printf("Go to line one\n");
else
printf("Go to line two\n");
if (szName[0] < 'K')
printf("Go to line one\n");
else
printf("Go to line two\n");
Warning! Warning! Warning!
== is the equivalence test operator. = is an assignment operator and must not be used for an equivalence test.
& is the logical and. A single & is a bitwise and.
|| is the logical or. A single | is a bitwise or. / Example of a common mistake:
if (iCount = 3)
That conditional is always true since an assignment returns its value (3) and 3 is not zero. Since some other programming languages (e.g., PL/I, SQL) use = for equivalence tests, this is a common mistake for people using multiple languages.
Warning! Warning! Warning!
comparing strings
The equivalence operator (==) cannot be used to compare strings. The compiler would think it is comparing addresses. / if (szCommand == "DEPOSIT")
would check to see if the address of szCommand is the same as the address for the literal "DEPOSIT". Use strcmp() or strncmp() for string comparisons.
while statement
The while statement continues to loop while the condition is true (not zero).
Syntax:
while (conditionalExpr)
whileBody; / Example 8: while not EOF
// read the student data until EOF (fgets returns null pointer)
while (fgets(szInputBuffer, 100, pfileStudentData)!= NULL)
{
printf(%s\n", szInputBuffer);
}
for statement
The for statement is the iterative loop in C. Unlike Java, you cannot declare a variable in the ().
for (initializationStmts; conditionalExpr; incrementStmts)
forBody;
When the for statement is encountered, the initalizationStmts are executed. The for loop continues while the conditionalExpr is non-zero. At the bottom of each iteration, the incrementStmts are executed. / Example 9: print the names of students who made an A on the final
Student studentM[50];
int i;
// assume the studentM array and iStudentCount have been populated
...
// Print the names of students earning an A on the final exam
for (i = 0; i < iStudentCount; i+=1)
{
if (studentM[i].dFinalExam>= 90)
printf("%s\n", studentM[i].szName);
}
Assigning Numeric Values
The = operator is used for assigning values. Strings cannot be assigned using the = operator in C.
variableReference = valueExpr;
Numeric operators:
+ plus
- minus
* multiplication
/ division
++ increment by 1 unit
-- decrement by 1 unit
%modulus (returns remainder)
Also, there are shortcuts for summing or subtracting values:
variableReference += valueExpr;
variableReference -= valueExpr;
Note that dividing an integer value by an integer value will truncate the result. If either operand in a division is float (or double), the result is not truncated. / Example 10: numeric operations and assignments
double dSum = 10.0;
int iCount = 4;
double dAverage = dSum / iCount; // 2.5 since a value is a double
int iVal1 = 10;
int iVal2 = 4;
double dVal;
dVal = iVal1 / iVal2; // This is an integer divide
// which is 10 / 4 with a
// truncated result (2).
// dVal is then 2.0
// How can we get that division to provide a double result?
dVal = (iVal1 + 0.0) / iVal2;
dVal = (double) iVal1 / iVal2;
dCircumference = 2.0 * PI * dRadius;
if (iCount % 5 == 0) // look for a count that is a
// multiple of 5
dBalance += dAmount; // deposit
dBalance -= dAmount; // withdrawal
Char, Strings, and arrays of strings
Some variables are declared to contain just one character. For example:
char cGender; // F - female, M - male
You can initialize it in the declaration:
char cGender = 'F';
You can assign single characters to single character variables.
Strings are stored as arrays that are zero byte terminated.
We can have arrays of strings (which are actually two-dimensional arrays).
In the examples on the right, ? indicates garbage. / Example 11: char, strings, and arrays of strings
char cGender;
cGender = 'M';
char szMyName[15] ="Larry Clark";
L / a / r / r / y / C / l / a / r / k / \0 / ? / ? / ?
char dayWeekM[7][10] = { "Sunday", "Monday", "Tuesday"
, "Wednesday", "Thursday", "Friday", "Saturday" };
S / u / n / d / a / y / \0 / ? / ? / ?
M / o / n / d / a / y / \0 / ? / ? / ?
T / u / e / s / d / a / y / \0 / ? / ?
W / e / d / n / e / s / d / a / y / \0
T / h / u / r / s / d / a / y / \0 / ?
F / r / i / d / a / y / \0 / ? / ? / ?
S / a / t / u / r / d / a / y / \0 / ?
// Using a typedef for the day of the week */
typedefcharDayOfWeek[10];
DayOfWeek dayWeekM[7] = { "Sunday", "Monday", "Tuesday"
, "Wednesday", "Thursday", "Friday", "Saturday" };
char szName[10] = "May King";
if (szName[0] == 'M')
szName[0] = 'R'; // replace it with a 'R'
string assignments
Strings are assigned values using
strcpy(target, source);
assigns a null terminated string to the target
strncpy(target, source, length);
assigns length characters from source to target. If the actual length of source is > length, the result in target won't be null terminated. If a null byte is encountered before length, no other characters are copied from source.
memcpy(target, source, length);
assigns exactly length characters from source to target. / Example 12: string assignments
char szWinnerFullName[15] = "Anita Break";
char szContestantFullName[15] = "Bob Wire";
A / n / i / t / a / B / r / e / a / k / \0 / ? / ? / ?
strcpy(szWinnerFullName, szContestantFullName);
Value of szWinnerFullName is
B / o / b / W / i / r / e / \0 / a / k / \0 / ? / ? / ?
Warning! Warning! Warning!
string assignments can overwrite memory
One of the most common and frustrating bugs in C is overwriting memory. / Example 13: overwrite of memory
char szName[10];
char szSpouse[] = "Anita Break";
strcpy(szName, szSpouse);
Exercise #1: Show code which counts the letter 's' in a string and prints that count. Assume the string is named szState.
char szState[] = "Mississippi"; / Exercise#1: code for counting the character 's' in a string.
int i;
int iCount=0;
for (i = 0; i < strlen(szState); i+= 1)
{
if (szState[i] == 's')
iCount += 1;
}
printf("count of 's' is %d\n", iCount);
Exercise #2: copy the reverse of a string into another array.
Given a name, store the reverse of the name in another array.
char szName[8] = "Perry";
char szReversedName[8];
0 / 1 / 2 / 3 / 4 / 5 / 6 / 7
szName / P / e / r / r / y / \0 / ? / ?
What is strlen(szname)? 5
Where will the 'P' go in szReversedName? 4 / Exercise #2: copy the reverse of a string into another array.
#include <string.h>
int i; // iterate through szName
int j; // location of characters in szReversedName
j = strlen(szName) – 1;
for (i = 0; i < strlen(szName); i += 1)
{
szReversedName[j] = szName[i];
j--;
}
szReversedName[strlen(szName)] = '\0';
switch statement
The switch statement is used when there are options of what should be done that are dependent on the value of a variable.
switch (variableReference)
{
case value:
…// possibly many case value:
statements;
break;
case value:
…// possibly many case value:
statements;
break;
default:
statements;
}
Notes:
- There may be multiple case value clauses followed by statements.
- A break statement is necessary to specify branching to immediately after the switch statement.
- default statements are optional. If present, the default will execute if none of the case values match the value of the variableReference.
// 14-a: switch
switch (cLetterGrade)
{
case 'A':
case 'B':
printf("Better than Average\n");
printf("You are exempt from the exam\n");
break;
case 'C':
printf("About Average\n");
break;
case 'D':
case 'F':
printf("Below Average\n");
}
// 14-b: if … else if
if (cLetterGrade == 'A' || cLetterGrade == 'B')
{
printf("Better than Average\n");
printf("You are exempt from the exam\n");
}
else if (cLetterGrade == 'C')
printf("About Average\n");
else if (cLetterGrade == 'D' || cLetterGrade == 'F')
{
printf("Below Average\n");
}
// 14-c: another switch example
switch (cLetterGrade)
{
case 'A':
case 'B':
case 'C':
case 'D':
printf("You passed\n");
break;
case 'F':
printf("You failed\n");
break;
default:
fprintf(stderr, "unknown grade %c\n"
,cLetterGrade);
}
break statement in for or while
The break statement can also be used to exit a for or while statement.
Unfortunately, if you have multiple nested looping statements, you can't specify which one to break out of. It always assumes the immediately surrounding switch, while or for. / Example 15: breaking out of a while or for statement
// keep looping until a player wins
while (TRUE)
{
iResult = takeTurnForPlayer(player1);
if (iResult == PLAYER1_WINNER)
break;
iResult = takeTurnForPlayer(player2);
if (iResult == PLAYER2_WINNER)
break;
}
continue statement in for or while
The continue statement allows you to skip the rest of the code in a loop and continue with the increment/test. It always assumes the immediately surrounding while or for. / Example 16: breaking or continuing a while loop
// keep looping until there is only one player left
while (iNumActivePlayers > 1)
{
// get the next player
iCurrentPlayer++;
if (iCurrentPlayer > iNumPlayers)
iCurrentPlayer = 0;
// if player cannot move, skip him
if (cannotMove(iCurrentPlayer, game) == TRUE)
continue; // skip this player
startTimer(iCurrentPlayer, game);
iResult = takeTurn(iCurrentPlayer, game);
if (iResult == WINNER_DETERMINED)
break;
prepareForNextPlayer(iCurrentPlayer, game);
}
struct and typedef
typedef statements are used to define data types. We can use them to define a data type for a struct.
typedef struct
{
attributeDef1;
attributeDef2;
…
} typedefStruct;
We can then use it in a declaration.
typedefStruct structureVariable; / Example 17: typedef
typedefstruct
{
char szSSN[10];
char szFullName[40];
double dHourlyRate;
} Employee;
Employee employee, employeeMgr;
Referencing an attribute of a structure variable uses dot notation.
structureVariable.attribute
Assignments also use the dot notation.
We can assign one structure variable into another when they are of the same type.
targetStructureVariable = sourceStructureVariable;
This copies all of the attributes of the source into the target. / Example 18: referencing an attribute in a structure
printf("SSN=%s, rate=%lf\n"
, employee.szSSN, employee.dHourlyRate);
printf("Manager is %s\n", employeeMgr.szFullName);
employee.dHourlyRate = 12.50;
strcpy(employee.szFullName, "Allen Wrench");
employeeMgr = employee;
Arrays of Structures
To declare an array of structures, specify the array size after the structure variable.
struct structType structureVariable[arraySize];
typedefStruct structureVariable[arraySize]; / Example 19: Each element of the array is a structure
Employee employeeM[20];
int i = 2;
printf("hourly rate for 3rd entry in the employeeM array is %lf\n"
, employeeM[i].dHourlyRate):
Structures as Attributes
In the attribute definition, use either a struct reference or typedef reference
// struct reference
struct structType
{
attributeDef1;
attributeDef2;
struct anotherStructType attributeStructVariable;
…
} structureVariable;
//typedef reference
struct structType
{
attributeDef1;
attributeDef2;
typedefStruct attributeStructVariable;
…
} structureVariable; / Example 20: Each element of the array is a structure
typedefstruct
{
int iExemptionCnt; // Number of exemptions
char cFillingStatus; // M - Married, S - Single
// X - married but filling as single
double dWithholdExtra; // extra amount to withhold
} W2;
typedefstruct
{
char szSSN[10];
char szFullName[40];
double dHourlyRate;
W2 w2;
} Employee;
Employee employee;
To reference dWithholdExtra, reference both structures:
dWithholding = dStandardWithholding
+ employee.w2.dWithholdExtra;
Employee *pEmployee;
pEmployee->w2.dWithholdExtra;
Initialization of Structures
When a structure variable is declared, it can be initialized by listing the values in order.
If there are few values that need to be initialized, those can be listed using
{.variable1=value1, .variable2=value2, …}
Arrays of structures can be initialized. The attributes can be initialized in order or you can surround an array item with braces.
As stated previously, a structure can be assigned into a structure of the same type. It will copy the entire structure. / Example 21: Initializing structures
Employee employee =
{ "123456789", "Bill Board"
, 12.50, 1, 'M', 100.00 };
Employee employeeMgr =
{ .szFullName = "I. M. Boss"
, .dHourlyRate = 70.00
, .w2.dWithholdExtra = 300.00
};
Employee employeeM[5] =