clark cs2123 Course lecture notes

advertisement
clark
cs2123
Course
Outline
lecture notes
programming assignments
homework
set up
Last Updated: 1/8/2015 (subject to change within 24 hours of lecture)
Complete Program Example
Compiling and Linking
To compile in unix, we use gcc to generate compiled object and
executables:
gcc -g -o executableName cSourceFileName.c
where -g tells the compiler to generate information suitable for
the debugger.
This generated an executable with the name specified after the
output switch (-o).
Until we have more complex assignments, we will use that form
of the gcc.
To compile a c source file, producing only a compiled object:
gcc -c -g -o cSourceFileName.o cSourceFileName.c
where specifying -c will cause it to only compile, producing a
compiled object named cSourceFileName.o
To link a compiled object file, producing an executable:
gcc -g -o executableName cSourceFileName.o anyOther.o ...
# compile first.c with debugging, producing first
# as executable
gcc -g -o first first.c
# only compile, producing an object (.o) file
gcc -c -g -o first.o first.c
# link a .o file, producing first as an executable
gcc -g -o first first.o
# you can link many .o files, producing an executable
gcc -g -o cs2123p1 cs2123p1Driver.o toPost.o
int main()
Within your linked module, there should be one int main(). This is the
function that begins execution. It is passed an array of pointers to strings
and a count of the number of items in the array. It returns an integer. By
convention, 0 is returned for normal return. The contents of argv
includes the command name and the command line arguments to this
program.
Example: average program
Suppose the program average is passed a list of integers and
returns the average. In unix, we would invoke it by keying
$ average 10 20 40 30
25.00000
variables
dataType variableName = initialValue;
The intialization is optional.
Some of the data types:
int - integer (could be 2 or 4 bytes depending on implementation)
long - 4 byte integer
float - 4 byte floating point
double - 8 byte floating point
char - single character
#include <stdio.h>
int main(int argc, char *argv[])
{
double dSum = 0.0;
int i;
int iScanfCount;
// count of the successful
// conversions in sscanf
int iNum;
// number from the command line
/* check for no arguments */
if (argc < 2)
{
fprintf(stderr, "Error: must pass values to average");
return 0;
}
for (i = 1; i < argc; i++)
{
// convert command argument to int
iScanfCount = sscanf(argv[i], "%ld", &iNum);
// sscanf returns the number of successful formats
if (iScanfCount < 1)
{
fprintf(stderr, "Invalid command argument: %s"
, argv[i]);
return -1;
}
dSum += iNum;
}
printf("%lf", dSum / (argc - 1));
return 0;
}
int iStudentCount;
int iStudentCount = 0;
int iOrderQuantity = 0;
double dAverage = 0.0;
char cLetterGrade = 'F';
int bEOFEncounteredInd = 0;
// 0 is false, non-zero
// is true.
Declaring an Array
dataType variableName[numberOfElments];
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.)
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 property. 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.
double dExamWeightingM[3] = {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);
if statement
The if statement is the most important statement for flow control (as it is
in most languages). Syntax options:
if (conditionalExpression)
truePart;
if (conditionalExpression)
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
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.
|| is the logical or.
A single & is a bitwise and.
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;
}
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.
A single | is a bitwise or.
Warning! Warning! Warning!
comparing strings
The equivalence operator (==) cannot be used to
compare strings. The compiler would think it is
comparing addresses.
while statement
if (cLetterGrade == 'W')
{
printf("last day to drop is %s\n", szDropDate);
dCourseGradePoints = 0.0;
dCourseCredits = 0.0;
}
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.
The while statement continues to loop while the condition is true (not
zero).
Syntax:
while (conditionalExpr)
whileBody;
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.
assigning values
The = operator is used for assigning values. Strings cannot be assigned
using the = operator in C.
variableReference = valueExpr;
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.
Warning! Warning! Warning!
string assignments can overwrite memory
One of the most common and frustrating bugs in C is overwriting
memory.
// read the student data until EOF (fgets returns null pointer)
pszGetsResult = fgets(szInputBuffer, 100, pfileStudentData);
while (pszGetsResult != NULL)
{
printf(%s\n", szInputBuffer);
pszGetsResult = fgets(szInputBuffer, 100, pfileStudentData);
}
// Print the names of students earning an A
for (i = 0; i < iStudentCount; i++)
{
if (studentM[i].dGradeAvrage >= 90)
printf("%s\n", studentM[i].szName);
}
dAverage = dSum / iCount;
dCircumference = 2.0 * PI * dRadius;
if (iCount % 5 == 0) // look for a count that is a
// multiple of 5
dBalance += dAmount;
dBalance -= dAmount;
// deposit
// withdrawal
char szWinnerFullName[15] = "Anita Break";
char szContestantFullName[15] = "Bob Wire";
strcpy(szWinnerFullName, szContestantFullName);
Value of szWinnerFullName is
B
o
b
W
i
r
e
\0 a
char szName[10];
char szSpouse[] = "Anita Break";
strcpy(szName, szSpouse);
k
\0 ?
?
?
sizeof and strlen
sizeof ( thing ) is a compile-time function which returns the compile-time
(not runtime) size of the thing.
strlen ( variable ) returns the length of a null-terminated string.
int iCount = 0;
double dPrice = 10.5;
char szNickName[21] = "champ";
struct Account
{
char szAccountNr[15];
double dBalance;
} account = { "123456789", 100.20 };
printf("%-20s %-10s %-10s\n", "Thing", "sizeof", "strlen");
printf("%-20s %-10d %-10s\n", "iCount", sizeof(iCount), "n/a");
printf("%-20s %-10d %-10s\n", "dPrice", sizeof(dPrice), "n/a");
printf("%-20s %-10d %-10d\n", "szNickName", sizeof(szNickName)
, strlen(szNickName));
printf("%-20s %-10d %-10d\n", "szAccountNr"
, sizeof(account.szAccountNr), strlen(account.szAccountNr));
printf("%-20s %-10d %-10s\n", "dBalance"
, sizeof(account.dBalance), "n/a");
printf("%-20s %-10d %-10s\n", "account", sizeof(account)
, "n/a");
Output:
Thing
iCount
dPrice
szNickName
szAccountNr
dBalance
account
Exercise:
Given a name, store the reverse of the name in another array.
char szName[21] = "Perry Scope";
char szReversedName[21];
sizeof
4
8
21
15
8
24
strlen
n/a
n/a
5
9
n/a
n/a
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.
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':
prinf("Below Average\n");
}
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 immediatley
surrounding switch, while or for.
// keep looping until a player wins
while (TRUE)
{
iResult = takeTurnForPlayer(player1);
if (iResult == PLAYER1_WINNER)
break;
iResult = takeTurnForPlayer(player2);
if (iResult == PLAYER2_WINNER)
break;
// another 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);
}
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
immediatley surrounding while or for.
printf
This prints output based on a specification.
printf(specification, value1, value2, …);
The specification contains strings and format codes. Some format codes:
%s
show a null terminated string
%d
show an integer value
%ld
show a long integer value
%f
show a floating point value
%lf
show a double value
%c
show single character
%x
show a hex value
We use \n to cause the output to do a line feed.
A format code can be in the following form:
flag minWidth precision lengthModifier code
We go over this in more detail later in the semester, but here are some
helpful examples:
%10.2lf where 10 is the minWidth, 2 is the precision, l is the
lengthModifier (long floating point), and f is the code.
}
// 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);
}
char szName[21] = "Bob Wire";
int iOrderQuantity = 5;
double dUnitCost = 7.25;
char
cGender = 'F';
printf("name is %s\n", szName);
printf("Order Quantity is %d\n", iOrderQuantity);
printf("Unit cost is %lf\n", dUnitCost);
printf("Gender is %c\n", cGender);
Output:
name is Bob Wire
Order Quantity is 5
Unit cost is 7.250000
Gender is F
printf("Unit
printf("Unit
printf("Unit
printf("Unit
printf("Unit
Cost
Cost
Cost
Cost
Cost
in
in
in
in
in
10.2 is '%10.2lf'\n", dUnitCost);
9.3 is '%9.3lf'\n", dUnitCost);
.2 is '%.2lf'\n", dUnitCost);
-10.2 is '%-10.2lf'\n", dUnitCost);
-9.3 is '%-9.3lf'\n", dUnitCost);
printf("Name '%10s' and '%-10s'\n", szName, szName);
%.2lf
%10s
%-10s
where this is the same as above, but it doesn't have a
minWidth.
where 10 is the minWidth and s is the code. The value
will be right-justified.
where - specifies to left-justify, 10 is the minWidth, and s
is the code. The value will be left-justified.
Redirecting output to a file
In unix, you can specify where to direct the standard output by specifying
command > filename
You can also use fprintf which has an additional parameter for the file.
fprintf(file, specification, value1, value2, …);
The file should be opened with an fopen().
Output:
Unit Cost in 10.2 is '
7.25'
Unit Cost in 9.3 is '
7.250'
Unit Cost in .2 is '7.25'
Unit Cost in -10.2 is '7.25
'
Unit Cost in -9.3 is '7.250
'
Name ' Bob Wire' and 'Bob Wire '
// Suppose the executable is one.exe. On the command line,
// we can specify where to direct the output when we run
// the program.
$ one.exe >myOutput.txt
#define OUT_FILE "myoutFile.txt"
FILE *fileOut;
fileOut = fopen(OUT_FILE, "w");
if (fileOut == NULL)
{
fprintf(stderr, "Could not open %s\n", OUT_FILE);
return 2;
}
fprintf(fileOut, "name is %s\n", szName);
Exercise
Given these variables:
double dScoreM[20]; // scores on the exams
int iScoreCount;
// count of the scores in dScoreM.
Show code which prints the contents of dScoreM, but only print 4 values
per line.
fgets and scanf
These are used to receive input from standard input.
fgets(stringVariable, maxLength, file); This reads from standard input
until either maxLength - 1 characters are read or until a line feed
character is encountered.
scanf(formatString, address1, address2, …);
char szStockNumber[21];
char *pszGetsResult;
printf("Enter stock number:");
pszGetsResult = fgets(szStockNumber, 21, stdin);
if (pszResult != NULL)
{
// do something
}
Facts about scanf():
 must be passed addresses since it stores its results in those
addresses
 returns the number of successful conversions from its input to
those addresses
 null terminates resulting strings
 format codes are typically in the form:
maxSize lengthModifier code
char szStockNumber[21];
double dInventoryUnitPrice;
int iStockCount;
int iScanfCount;
printf("Enter stockNumber unitPrice stockCount:\n");
iScanfCount = scanf("%20s %lf %d"
, szStockNumber
, &dInventoryUnitPrice
, &iStockCount);
printf("Stock Number = %s, Unit Price = %lf, Stock Count = %d\n"
, szStockNumber
, dInventoryUnitPrice
, iStockCount);
Warning!!! Warning !!! Warning !!!
scanf() must be passed addresses. Numeric variables pass
values instead of addresses. You must pass the address of
numeric variables using an &. This is also necessary for
single byte variables. Since the addresses of arrays are
automatically passed, it isn't necessary to use the & with
arrays.
Some interesting scanf format codes:
%[^,]
reads everything but commas into the corresponding variable.
This means that input terminates with a comma instead of a
space.
%20[^\n] reads everything but line feeds into the corresponding
variable. This means that the input terminates with a line feed
or \0. It places a maximum of 20 characters (plus a \0) in the
variable.
Functions
In C, functions are used to modularize coding. A function has the
following syntax:
dataType functionName (parameterList)
int iOrderQuantity;
char szCustomerId[10];
iScanfCount = scanf("%s %d", szCustomerId, iOrderQuantity);
Since iOrderQuantity is numeric, we must pass its address. In this
example, we passed its value which scanf will attempt to use as an
address. Depending on that value, it might have an addressing error
or store a value at that questionable address.
// sscanf gets its input from a variable (1st argument)
char szLongAddress [] = "123 Dirt Rd\n";
long lStreetNumber;
char szStreet[11];
iScanfCount = sscanf(szInputStr, "%ld %10[^\n]\n"
, &lStreetNumber
, szStreet);
double calculateAverage(double dScoreM[], int iScoreCount)
{
int i;
double dSum = 0.0;
if (iScoreCount <= 0)
return 0.0;
for (i = 0; i < iScoreCount; i++)
{
dSum += dScoreM[i];
}
return dSum / iScoreCount;
{
functionBodyStatements
return expression;
}
If the function doesn’t functionally return a value, we specify
void functionName (parameterList)
{
functionBodyStatements
}
}
void showScores(char *pszName, double dScoreM[]
, int iScoreCount)
{
int i;
printf("Scores for %s\n", pszName);
for (i = 0; i < iScoreCount; i++)
{
printf("%3f\n", dScoreM[i]);
}
}
Parameter passing
 Numeric variables are passed by value. To pass the address, you
must precede the variable name with a & reference operator. In
the invoked function, the parameter must be declared to be
pointer to receive the address.
 Arrays pass the address.
 Structures pass the value. This can hurt performance and is rarely
necessary. To pass the address, you must precede the variable
name with a &. In the invoked function, the parameter must be
declared to be pointer to receive the address.
// typedefs and prototypes
typedef struct
{
char
szName[31];
double dScoreM[50];
int
iScoreCount;
} StudentScores;
void determineMinMax(StudentScores *pstudentScores, double *pdMin
, double *pdMax);
int main()
{
double dMinimum;
double dMaximum;
StudentScores studentOne = { "Marcus Absent"
, { 40, 0, 30, 55, 0, 25 }
, 6};
determineMinMax(&studentOne, &dMinimum, &dMaximum);
return 0;
}
void determineMinMax(StudentScores *pstudentScores, double *pdMin,
double *pdMax)
{
int i;
*pdMin = 200.0; // arbitrary high value
*pdMax = 0.0;
for (i = 0; i < pstudentScores->iScoreCount; i++)
{
if (pstudentScores->dScoreM[i] < *pdMin)
*pdMin = pstudentScores->dScoreM[i];
if (pstudentScores->dScoreM[i] > *pdMax)
*pdMax = pstudentScores->dScoreM[i];
}
}
Exercise
Create the function, calculateTotalCost, which is passed two arrays and a
count of the number of entries. Does it need another parameter?
dUnitPriceM[] - each entry represents a price per unit
lOrderQuantiy[] - each entry represents an order quanity
It should return the total cost which is the sum of the cost (unit price *
order quantity) of all of the entries. Return that total cost as a
parameter.
Exercise
Same as previous exercise, but return the total cost as the functional
value.
Storage Classifications
auto
Automatic local variables receive memory when their
declaration is encountered at runtime. When the block of
code containing an automatic variable is exited, their memory
is freed. Automatic variables can be declared at the beginning
of
 A function
 A compound statement enclosed in {}
In C++ and Java, you can declare variables as you need them.
Automatic variables are allocated and freed from the top of
void funcA ()
{
int iVar1;
double dVar2;
statements1
if (iVar1 > 5)
{
char szString1[50];
int iLength;
statements2
}
statements3
}
// auto allocation of iVar1
// auto allocation of dVar2
// auto allocation of szString1
// auto allocation of iLength
// frees szString1 and iLength
// frees iVar1 and dVar2
the runtime memory stack.
Storage Classifications
extern
Extern global variables receive their memory when the
program is loaded. Global variables declared at the top of a
file (before function definitions) are a type of external global
variable.
 Multiple C files can reference the same external global
variables. One declaration must not contain the keyword
extern. It is considered the basis declaration. The basis
declaration can contain initializations.
 All references to the extern variable other than the single
basis declaration must use the extern keyword.
 Memory is freed when the program exits.
Storage Classifications
static
Static variables retain their values throughout execution.
Additionally, static variables are private to the C function or C
file where they are declared. They do not conflict with extern
variables in other files.
Note that static variables at the beginning of a file are also
global.
Storage Classifications
dynamic Pointer variables must be automatic, extern or static;
however, what they reference can be allocated memory
dynamically via malloc() or calloc(). A pointer also can be
contained within dynamically allocated memory.
 Memory is allocated from a heap.
/* file partA.c */
double dVectorM[300]
= {100, 200, 150, 300, 20, 5, 100, 30};
int iVectorLength = 8;
/* functions in partA.c */
/* file partB.c */
extern double dVectorM[300];
extern int iVectorLength;
/* functions in partB.c */
/* file partC.c */
extern double dVectorM[300];
extern int iVectorLength;
/* file partD.c */
static dVectorM[200] = {50, 60, 80};
file
static int iVectorLength = 3;
file
//basis
// basis
// extern reference
// extern reference
// extern reference
// extern reference
// static global to
// static global to
void myFunc()
{
static int iRetainValue;
// static local to
myFunc
int iAutoValue;
// automatic local to myFunc
statements;
}
// iAutoValue is freed, iRetainValue is not freed
typedef struct
{
char szName[30];
double dScore[50];
int iScoreCount;
} StudentScores;
StudentScores *pstudentScores;




Explicitly allocated with a malloc() or calloc()
Must be freed explicitly using a free().
Helpful for managing objects where the size varies
If memory is not managed well, memory leaks can occur.
Exercise
Consider the code for files exA.c and exB.c.
/* file exB.c */
extern int iMyVar;
static int iMyVar2;
void bfunc()
{
int iHey;
/* statements */
}
Variable
iMyVar in exA.c
iMyVar2 in exA.c
iParm in exA.c (myFunc)
iHello in exA.c
iElectricity in exA.c
iHey in exA.c
iKite in exA.c
iMyVar in exB.c
iMyVar2 in exB.c
iHey in exB.c
When does it get allocated?
pstudentScores = malloc(sizeof(StudentScores));
…
free(pstudentScores);
/* file exA.c */
int iMyVar;
int iMyVar2;
void myFunc (int iParm)
{
int iHello;
static int iElectricity;
/* statements */
}
int anotherFunc ()
{
int iHey;
static int iKite;
/* statements */
}
When is it freed?
Same as another variable?
ones?
Which
Download