Home | Department of Computer Science



Review of CLast Updated:? 05/25/2018C program componentsInclude files C 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 flowreturnexits 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"int main(int argc, 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); return ERR_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 fileWe 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 programA 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.cThat 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.c The 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.txt100 90 100 1111 Bill Board50 30 40 2222 Marcus Absent100 100 100 333 Jean E Us# To execute p0 which is in the current directory: $ ./p0 < p0Input.txtID Name Exam 1 Exam 2 Final Average1111 Bill Board 100.00 90.00 100.00 96.672222 Marcus Absent 50.00 30.00 40.00 40.00333 Jean E Us 100.00 100.00 100.00 100.00Declarations of simple variablesIn 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 pointdouble8 byte floating pointcharsingle character; one byteNote 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 variablesint iStudentCount; // int, but its value would be garbageint iCount = 0; // int, initialized to 0double dSum = 0.0; // double, initialized to 0.0char cLetterGrade = 'F'; // single character, not a string int bFound = 0; // boolean, 0 is false, // non-zero is true.Declarations of ArraysdataType 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 bytesbName - string not terminated by zero byte. It might contain binary data.Example 5: example declarations of arraysdouble dExamWeightingM[10] = {200.0, 200.0, 300.};#define NUMBER_OF_EXAMS 3double 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 bytechar szFileName[] = "myInput.txt";char szABC[] = {'A', 'B', 'C', '\0'};char szABC[] = "ABC"; // automatically zero // byte terminatedABC\0char sbDelims[] = {',', ' ', '\t', '\n'};int iDelimCount = sizeof(sbDelims);printf("number of delims = %d\n", iDelimCount);Flow Control Statements in CC 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 statementThe 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 toLogical operators:&& and|| or! notExample 6: if statementsif (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 StringsSingle 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 comparisonsif (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 stringsThe 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 statementThe 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 statementThe 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 examfor (i = 0; i < iStudentCount; i+=1){ if (studentM[i].dFinalExam >= 90) printf("%s\n", studentM[i].szName);}Assigning Numeric ValuesThe = 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 assignmentsdouble dSum = 10.0;int iCount = 4;double dAverage = dSum / iCount;? // 2.5 since a value is a doubleint 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; // depositdBalance -= dAmount; // withdrawalChar, Strings, and arrays of stringsSome variables are declared to contain just one character. For example:char cGender; // F - female, M - maleYou 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 stringschar cGender;cGender = 'M';char szMyName[15] = "Larry Clark";Larry Clark\0???char dayWeekM[7][10] = { "Sunday", "Monday", "Tuesday" , "Wednesday", "Thursday", "Friday", "Saturday" };Sunday\0???Monday\0???Tuesday\0??Wednesday\0Thursday\0?Friday\0???Saturday\0?// Using a typedef for the day of the week */typedef char DayOfWeek[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 assignmentsStrings are assigned values using strcpy(target, source); assigns a null terminated string to the targetstrncpy(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 assignmentschar szWinnerFullName[15] = "Anita Break";char szContestantFullName[15] = "Bob Wire";Anita Break\0???strcpy(szWinnerFullName, szContestantFullName);Value of szWinnerFullName is ?Bob Wire\0ak\0???Warning! Warning! Warning! string assignments can overwrite memoryOne of the most common and frustrating bugs in C is overwriting memory.Example 13: overwrite of memorychar 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];01234567szNamePerry\0??What is strlen(szname)? 5Where will the 'P' go in szReversedName? 4Exercise #2: copy the reverse of a string into another array.#include <string.h>int i; // iterate through szNameint j; // location of characters in szReversedNamej = strlen(szName) – 1;for (i = 0; i < strlen(szName); i += 1){ szReversedName[j] = szName[i]; j--;}szReversedName[strlen(szName)] = '\0';switch statementThe 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.When there are multiple mutually exclusive conditions, a switch statement has better readability.Example 14: switch vs if .. else if// 14-a: switchswitch (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 ifif (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 exampleswitch (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 whileThe 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 winswhile (TRUE){ iResult = takeTurnForPlayer(player1); if (iResult == PLAYER1_WINNER) break; iResult = takeTurnForPlayer(player2); if (iResult == PLAYER2_WINNER) break;}continue statement in for or whileThe 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 leftwhile (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 typedeftypedef 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 typedef struct{ char szSSN[10]; char szFullName[40]; double dHourlyRate;} Employee;Employee employee, employeeMgr;Referencing an attribute of a structure variable uses dot notation.structureVariable.attributeAssignments 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 StructuresTo 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 AttributesIn the attribute definition, use either a struct reference or typedef reference// struct referencestruct structType{ attributeDef1; attributeDef2; struct anotherStructType attributeStructVariable; …} structureVariable;//typedef referencestruct structType{ attributeDef1; attributeDef2; typedefStruct attributeStructVariable; …} structureVariable;Example 20: Each element of the array is a structure typedef struct{ int iExemptionCnt; // Number of exemptions char cFillingStatus; // M - Married, S - Single // X - married but filling as single double dWithholdExtra; // extra amount to withhold} W2;typedef struct{ 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 StructuresWhen 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 structuresEmployee 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] = { { "123456789", "Bill Board", 12.50, 1, 'M', 100.00 } , { "222222222", "Peg Board", 15.00, 1, 'M', 150.00 } , { "333333333", "Emory Board", 10.00, 0, 'S', 0.00 }};employeeMgr = employee;employeeM[i] = employeeMgr;sizeof and strlensizeof ( 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. Example 22: strlen vs sizeofint 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 sizeof strleniCount 4 n/adPrice 8 n/aszNickName 21 5szAccountNr 15 9dBalance 8 n/aaccount 24 n/aExercise #3: Using the Customer typedef, assume the customerM array has already been populated and the number of customers is in iCustomerCnt. Print an array of customers with a heading and the values in a table format.typedef struct{ int iID; // Assume it is 6 digits char szName[20]; // 19 characters double dBalance; // allow 10 digits with 2 to the right } Customer;Customer customerM[30];Hint: use the same field width for the column heading and the value.Instead of this for the heading: printf("ID Name Balance\n");use printf("%-6s %-19s %10s\n", "ID", "Name", "Balance"); Exercise #3: printing a column heading and dataint iCustomerCnt;int i;// assume customerM and iCustomerCnt have been initialized.// print the column headingprintf("%-6s %-19s %10s\n", "ID", "Name", "Balance");// print the contents of the arrayfor (i = 0; i < iCustomerCnt; i++){ printf("%-6d %-19s %10.2lf\n" , customerM[i].iID , customerM[i].szName , customerM[i].dBalance);}Redirecting output to a fileIn unix, you can specify where to direct the standard output by specifying command > filename // 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.txtYou 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().Example 23: using fprintf with a file opened with fopen#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 #4: printing four values per lineGiven these variables:double dScoreM[20]; // scores on the examsint iScoreCount; // count of the scores in dScoreM.Assume that dScoreM has been populated with iScoreCount values. Show code which prints the contents of dScoreM, but only print 4 values per line.This means that we only want a line feed printed on every 4th value.Exercise#4: printing four values per line// additional variablesint i; // subscript for (i=0; i< iScoreCount; i++){ printf("%5.1lf", dScoreM[i]); if ((i+1)%4 == 0) printf("\n");}printf("\n"); // print a line feed after the last value fgets and scanfThese 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, …);Facts about scanf(): scanf reads from a filemust be passed addresses since it stores its results in those addressesreturns the number of successful conversions from its input to those addressesnull terminates resulting stringsformat codes are typically in the form:maxSize lengthModifier code Unlike printf(), scanf() doesn't have a precision. You cannot specify %10.2lf for scanf(). Example 24: fgets and scanfchar szStockNumber[21];char *pszGetsResult;printf("Enter stock number:");pszGetsResult = fgets(szStockNumber, 21, stdin);if (pszResult != NULL){ // do something}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);sscanfWe will usually read a line of text into a variable using fgets and then use sscanf to take the data from that variable to populate our target variables.sscanf(stringVariable, formatString, address1, address2, …);Example 25: fgets with sscanf// The following code reads a line of text until EOF. // We will then use sscanf to copy the values into variables // for ABC123 ID and two grades.// Print that ABC123 ID and the higher of the two grades.#include <stdio.h>int main(int argc, char *argv[]){char szInputBuffer[101];char szABC123[7];double dGrade1;double dGrade2;int iScanfCount;printf("%-6s %-6s\n", "ABC123", "Grade");// Read a line of text until EOF. // Note that fgets returns NULL when it reaches EOF.while (fgets(szInputBuffer, 100, stdin) != NULL){ iScanfCount = sscanf(szInputBuffer, "%s %lf %lf" , szABC123 , &dGrade1 , &dGrade2); if (iScanfCount < 3) { // show an error and continue with the next line printf(" *** Invalid data\n"); continue; } // Print the ABC123 ID and the higher grade if (dGrade1 > dGrade2) printf("%-6s %6.2lf\n", szABC123, dGrade1); else printf("%-6s %6.2lf\n", szABC123, dGrade2); } return 0;}Why not just use scanf instead of the combination of fgets and sscanf?When scanf encounters an error while reading data, it can't be told to ignore the current line of input text and start with the next. It would continue from wherever it left off. If an error is encountered when using scanf, we usually must terminate the input process.If we use fgets to read a line of input text and then use sscanf to take the data out of the input line, we can simply do another fgets if the sscanf encountered bad data. Example 26: using scanf – we exit the input loop with an error…// stay in the loop while we get the three valueswhile (scanf("%s %lf %lf", szABC123, &dGrade1, &dGrade2) == 3){ // Print the ABC123 ID and the higher grade if (dGrade1 > dGrade2) printf("%-6s %6.2lf\n", szABC123, dGrade1); else printf("%-6s %6.2lf\n", szABC123, dGrade2);} Warning!!! Warning !!! Warning !!!scanf() and sscanf() 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 character variables. Since the addresses of arrays are automatically passed, it isn't necessary to use the & with arrays.Example 27: error – not passing the address of numeric variables to scanf (or sscanf)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.scanf format codes do not have the same form as printf. There are several forms:maxSize lengthModifier code[acceptValues][^delimiters]For the first form (maxSize code) of scanf format codes:maxSize specifies the maximum number of characters to read. After scanf sees a non-white space character, the next white space (blank, tab, newline) character will be the delimiter for the value. If you want to skip over a delimiter after you read a value, show the delimiter in the formatString. Blanks in the formatString will match zero to many white space characters.lengthModifier can be a letter l to specify a longer value (e.g., long, double).The values for (lengthModifier and) code include:%sread a string and null terminate it%dread an integer value%ldread a long integer value%fread a floating point value%lfread a double value%eread a floating point value using scientific notation%cread single character%xread a hex value%nreceives an integer value of the number of characters read so farNotice that scanf doesn't have a precision. Do not specify %10.2lf.Example 28: scanf format codesFormat codes like "10.2lf" do not work with scanf since it doesn't understand the ".2".Seq szInputBuffer Format Cnt szV1 iV2 dV3 1 123456 123 45.67 %s %d %lf 3 '123456' 123 45.67 2 123456 123 45.67 %5s %d %lf 3 '12345' 6 123.00 3 123456 123 45.67 %[1-9] %d %lf 3 '123456' 123 45.67 4 123456 123 45.67 %[1-9],%d,%10lf 1 '123456' n/a n/acomma in format didn’t match space 5 123456,123,45.67 %[1-9],%d,%10lf 3 '123456' 123 45.67 6 123456,123,45.67 %[^,],%d,%10lf 3 '123456' 123 45.67 7 123456,123,45.67 %6[^,] ,%d,%10lf 3 '123456' 123 45.67 8 123456 , 123,45.67 %[^,],%d,%10lf 3 '123456 ' 123 45.67 9 123456 , 123,45.67 %[^, ],%d,%10lf 1 '123456' n/a n/a comma in format didn't match first space 10 123456 , 123,45.67 %[^, ] ,%d,%10lf 3 '123456' 123 45.6711 123456 , 123,45.67 %[^, ] ,%d,%10lf 3 '123456' 123 45.6712 123456 , 123,45.67 %[^, ] ,%d,%10lf 3 '123456' 123 45.6713 123456,123,45.67 %[^,] %d %10lf 1 '123456' n/a n/acomma in data isn't a valid numeric value for %dFor second form ([acceptValues]) of scanf format codes:acceptValues is a list of acceptable values. To specify a range of characters, a dash can be used. Some interesting scanf format codes:%[a-z]reads the lower case characters a-z and stops when any other character is read.%[a-zA-Z]reads any alphabetic character and stops when any other character is read.%[^,]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.Example 29: scanf format codes// 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);FunctionsIn C, functions are used to modularize coding. A function has the following syntax:dataType functionName (parameterList){ functionBodyStatements return expression;}If the function doesn’t functionally return a value, we specify void functionName (parameterList){ functionBodyStatements}To use those functions, we must show a prototype function declaration before using them.// Example code using those functions// prototype declarationsdouble calculateAverage(double dScoreM[], int iScoreCount);void showScores(char szName[], double dScoreM[], int iScoreCount);int main(){ char szName[] = "Marcus Absent"; double dScoreM[] = { 40, 0, 30, 55, 0, 25 }; int iScoreCount = 6; printf("Average is %lf\n" , calculateAverage(dScoreM, iScoreCount); showScores(szName, dScoreM, iScoreCount); return 0;} Example 30: example function definitions// calculateAverage returns the average value of an array // as its functional valuedouble 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;}// showScores doesn't return a functional value. Instead, it// simply prints the name and then each score from an array.void showScores(char szName[], double dScoreM[] , int iScoreCount){ int i; printf("Scores for %s\n", szName); for (i = 0; i < iScoreCount; i++) { printf("%3lf\n", dScoreM[i]); }}Parameter PassingIn example 30, we didn't return any values through the parameters. What if we want to change the value of a parameter?To change the value of a parameter to return it through the parameter list, an address must be passed.Numeric variables are passed by value. To pass the address:In the calling function, 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. When referencing the value, it must be dereferenced using a *Arrays automatically pass the address. The use of pointers and dereferencing is one of the most difficult concepts to understand about C.// Example code using determineMinMax// prototype declaration so that the compiler knows what the // determineMinMax needs as parameters and what it functionally// returnsvoid determineMinMax(double dScoreM[], int iScoreCount , double *pdMin, double *pdMax);int main(){ double dMinimum; double dMaximum; char szName[] = "Marcus Absent"; double dScoreM[] = { 40, 0, 30, 55, 0, 25 }; int iScoreCount = 6; determineMinMax(dScoreM, iScoreCount, &dMinimum , &dMaximum); printf("For %s, minimum = %lf and maximum = %lf" , szName, dMinimum, dMaximum); return 0;}Example 31: determineMinMax must return two values so it returns them through the parameter list. void determineMinMax(double dScoreM[], int iScoreCount , double *pdMin, double *pdMax){ int i; *pdMin = 200.0; // arbitrary high value *pdMax = 0.0; for (i = 0; i < iScoreCount; i++) { if (dScoreM[i] < *pdMin) *pdMin = dScoreM[i]; if (dScoreM[i] > *pdMax) *pdMax = dScoreM[i]; }} Exercise #5Create the function, calculateTotalCost, which is passed two arrays and a count of the number of entries. Does it need another parameter? YesdUnitPriceM[] - each entry represents a price per unitlOrderQuantiyM[] - each entry represents an order quanityAs a functional value, it should return the total cost which is the sum of the cost (unit price * order quantity) of all of the entries. What is the prototype?double calculateTotalCost(double dUnitPriceM[], long lQuantityM[] , int iItemCount);// Code in the callerint main(){double dUnitPriceM[]={19.99, 50.50, 2.10};long lOrderQuantityM[] = {10, 2, 4};int iItemCount = 3;double dTotalCost;dTotalCost = calculateTotalCost(dUnitPriceM , lOrderQuantityM, iItemCount);printf("Total cost is %10.2lf\n", dTotalCost);}Exercise #5// code for calculateTotalCost function double calculateTotalCost(double dUnitPriceM[], long lQuantityM[] , int iItemCount){ int i; double dTotal = 0.0; for (i = 0; i < iItemCount; i += 1) { dTotal += dUnitPriceM[i] * lQuantityM[i]; } return dTotal;}Exercise #6Same as previous exercise, but return that total cost as a parameter instead of the functional value.// the call of calculateTotalCost is differentcalculateTotalCost(dUnitPriceM , lOrderQuantityM, iItemCount, &dTotalCost);printf("Total cost is %10.2lf\n", dTotalCost);Exercise #6: returning total cost as a parameter// code for calculateTotalCost function ??void calculateTotalCost(double dUnitPriceM[], long lQuantityM[] , int iItemCount, double *pdTotalCost){ int i; *pdTotalCost = 0.0; for (i = 0; i < iItemCount; i += 1) { *pdTotalCost += dUnitPriceM[i] * lQuantityM[i]; }}Exercise #7: Create the function averageGrades which is passed the number of students and an array of structures based on the following typedef:typedef struct{ int iNumGrades; double dGradeM[MAX_GRADES]; char szStudentId[7];} StudentGrade;It returns (via another parameter) an array of averages (one for each student). Exercise #7: Pass studentGradeM and avgGradeM to averageGrades// Example caller of averageGradesStudentGrade studentGradeM[] ={ {3, {100, 90, 80}, "111111"} // subscript 0 ,{3, {50, 60 , 70}, "222222"} // subscript 1 ,{2, {65, 30}, "333333"} // subscript 2};int iStudentCnt = 3;double avgGradeM[100];averageGrades(iStudentCnt, studentGradeM, avgGradeM);// code for the function averageGradesvoid averageGrades(int iStudentCnt, StudentGrade studentGradeM[] , double avgGradeM[]){ int iS; int j; double dSum; for (iS = 0; iS < iStudentCnt; iS++) { dSum = 0.0; for (j = 0; j < studentGradeM[iS].iNumGrades; j++) { dSum += studentGradeM[iS].dGradeM[j]; } avgGradeM[iS] = 0.0; if ( studentGradeM[iS].iNumGrades > 0) avgGradeM[iS] = dSum / studentGradeM[iS].iNumGrades; } } Exercise #8: Same as previous exercise, but the average for a student is also in the StudentGrade structure:typedef struct{ int iNumGrades; double dGradeM[MAX_GRADES]; char szStudentId[7]; double dAverage;} StudentGrade;Since dAverage is part of StudentGrade, we don't need the additional array parameter.Exercise #8: Pass studentGradeM to averageGrades// Example caller of averageGradesStudentGrade studentGradeM[] ={ {3, {100, 90, 80}, "111111"} // subscript 0 ,{3, {50, 60 , 70}, "222222"} // subscript 1 ,{2, {65, 30}, "333333"} // subscript 2};int iStudentCnt = 3;double avgGradeM[100];averageGrades(iStudentCnt, studentGradeM);// code for the function averageGradesvoid averageGrades(int iStudentCnt, StudentGrade studentGradeM[]){ int iS; int j; double dSum; for (iS = 0; iS < iStudentCnt; iS++) { dSum = 0.0; for (j = 0; j < studentGradeM[iS].iNumGrades; j++) { dSum += studentGradeM[iS].dGradeM[j]; } studentGradeM[iS].dAverage = 0.0; if ( studentGradeM[iS].iNumGrades > 0) studentGradeM[iS].dAverage = dSum / studentGradeM[iS].iNumGrades; } } Passing StructuresBy default, structures are passed by value which means that any changes you make are not known to the caller. Depending on the size of the structure, this can be expensive. typedef struct{ char szSSN[10]; char szFullName[40]; double dHourlyRate;} Employee;Employee employee = {"123456789", "Anita Rays", 10.00};Example 32-A: Incorrectly attempting to change a value for the corresponding argument.printf("Old Hourly Rate=%lf.2\n", employee.dHourlyRate;calculateRaise (employee);printf("New Hourly Rate=%lf.2\n", employee.dHourlyRate;void calculateRaise (Employee employee){ employee.dHourlyRate = employee.dHourlyRate * 1.10;}Output:Old Hourly Rate=10.00New Hourly Rate=10.00Passing the Address of a Structure to Allow Changes to be known to the CallerTo change the values and have those changes known in the caller, you must pass the address of the structure. 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.When referencing the attributes within the structure, you must use pointer notation (->).Example 32-B: Correctly changing a value for the corresponding argument.printf("Old Hourly Rate=%lf.2\n", employee.dHourlyRate;calculateRaise (&employee);printf("New Hourly Rate=%lf.2\n", employee.dHourlyRate;void calculateRaise (Employee *pEmployee){ pEmployee->dHourlyRate = pEmployee->dHourlyRate * 1.10;}Output:Old Hourly Rate=10.00New Hourly Rate=11.00Another Example Passing the Address of a StructureSuppose we have the following typedefs and prototypes:typedef struct{ char szName[31]; double dScoreM[50]; int iScoreCount; double dMinimum; double dMaximum;} StudentScores;void determineMinMax(StudentScores *pstudentScores);Since dMinimum and dMaximum are inside the structure and we are passing a simple structure (not an array of one structure), we must pass the address of thestructure for the changes to be known.int main(){ StudentScores studentOne = { "Marcus Absent" , { 40, 0, 30, 55, 0, 25 } , 6}; studentOne.iScoreCount = 6; determineMinMax(&studentOne); printf("For %s, minimum = %lf and maximum = %lf\n" , studentOne.szName , studentOne.dMinimum , studentOne.dMaximum); return 0;}Example 33: determineMinMax passed the address of the studentOne structure so that we can return the minimum and maximum as values in the structure.void determineMinMax(StudentScores *pstudentScores){ int i; pstudentScores->dMinimum= 200.0; // arbitrary high value pstudentScores->dMaximum = 0.0; for (i = 0; i < pstudentScores->iScoreCount; i++) { if (pstudentScores->dScoreM[i] < pstudentScores->dMinimum) pstudentScores->dMinimum = pstudentScores->dScoreM[i]; if (pstudentScores->dScoreM[i] > pstudentScores->dMaximum) pstudentScores->dMaximum = pstudentScores->dScoreM[i]; }} Output:For Marcus Absent, minimum = 0 and maximum = 55 Storage ClassificationsautoAutomatic 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 ofA functionA 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 the runtime memory stack. void funcA (){ int iVar1; // auto allocation of iVar1 double dVar2; // auto allocation of dVar2 statements1 if (iVar1 > 5) { char szString1[50]; // auto allocation of szString1 int iLength; // auto allocation of iLength statements2 } // frees szString1 and iLength statements3} // frees iVar1 and dVar2Storage ClassificationsexternExtern 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./* file partA.c */double dVectorM[300] //basis = {100, 200, 150, 300, 20, 5, 100, 30};int iVectorLength = 8; // basis/* functions in partA.c *//* file partB.c */extern double dVectorM[300]; // extern referenceextern int iVectorLength; // extern reference/* functions in partB.c *//* file partC.c */extern double dVectorM[300]; // extern referenceextern int iVectorLength; // extern referenceStorage ClassificationsstaticStatic variables are declared with the static keyword. They are given memory at program load. Memory is freed on program exit.Global static variables are declared at the top of the file and are global to the entire file, but they are private to the file. They are not shared with other source files. Local static variables are declared in a C function. Unlike automatic variables, they retain their value on function return. /* file partD.c */static dVectorM[200] = {50, 60, 80}; // static global to filestatic int iVectorLength = 3; // static global to filevoid myFunc(){ static int iRetainValue; // static local to myFunc int iAutoValue; // automatic local to myFunc statements;} // iAutoValue is freed, iRetainValue is not freed Storage ClassificationsdynamicPointer 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.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. typedef struct{ char szName[30]; double dScore[50]; int iScoreCount;} StudentScores;StudentScores *pstudentScores;pstudentScores = malloc(sizeof(StudentScores));if (pStudentScores == NULL) ... error…free(pstudentScores);?2018 Larry W. Clark, UTSA CS students may make copies for their personal use?? ................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download