CS 120 Quiz 2

¡Supera tus tareas y exámenes ahora con Quizwiz!

A C program is composed of one or more source files with each source file consisting of one or more functions. At one level, we can visualize a C program as a collection of functions that communicate with other functions in the program by passing and returning values. A C compiler independently processes each source file for correctness and translates the C code into machine language instructions. If the programmer has made an error in the C code that the compiler can detect, then the compiler issues an error message. Otherwise, the output of the compiler consisting of machine language instructions is called object code and is stored in an object file. When all source files are compiled, the object files are given to a program called linker. The linker resolves references between the object files, adds functions from the standard run-time library, and detects some programming errors such as the failure to define a needed function. The output of the linker program is a single executable program, which can then be invoked or run. Let's understand the idea that compiling is a process associated with an individual source file while linking is a collective process requiring the linker to process all necessary object files to generate an executable. Suppose we'd like to build a program consisting of two source C files greeting.c and main.c. The file greeting.c consists of these lines: /* greeting.c */ #include <stdio.h> /* declares function printf() */ void greeting(void) { printf("Hello World!\n"); } Compile but not link greeting.c for C89 with the full suite of warnings enabled. Warnings are diagnostic messages that report constructions that are not inherently erroneous but that are risky or suggest there may have been an error. In gcc, we use the following flags to enable the full suite of warnings: -Werror - this makes all warnings as errors. In this case, the programmer cannot successfully compile a source file until constructs that are not inherently erroneous are fixed. -Wall - this enables all the warnings about constructions that some programmers consider questionable, and that are easy to avoid or modify to prevent the warning. -Wextra - this enables some extra warning flags that are not enabled by -Wall. -Wpedantic or just -pedantic - this issues all the warnings demanded by strict ISO C, reject all programs that use forbidden extensions, and some other programs that do not follow ISO C and ISO C++. For ISO C, follows the version of the ISO C standard specified by any -std option used. Recall that ISO C is just C89 that was ratified by the International Standards Organization in 1990. Source file main.c consists of these lines: /* main.c */ int main(void) { greeting(); return 0; } main.c defines function main() which in turn calls a function greeting() that is implemented elsewhere (in source file greeting.c which the compiler is unaware of). Compile but not link main.c for C89 with the full suite of warnings enabled. Does source file main.c successfully compile? Select one: True False

False (correct)

Now, let's run a coding experiment. We leave the code in source file greeting.c unchanged: /* greeting.c */ #include <stdio.h> /* declares function printf() */ void greeting(void) { printf("Hello World!\n"); } Now, we modify source file main.c by adding the definition of function greeting() and also including <stdio.h>: /* main.c */ /* the original declaration of greeting() */ void greeting(void); /* main's definition of greeting() */ #include <stdio.h> void greeting(void) { printf("Greetings from main!"); } int main(void) { greeting(); return 0; } Now, check if you're able to compile (but not link) both source files: gcc -c main.c -Wall -Werror -Wextra -o main.o gcc -c greeting.c -Wall -Werror -Wextra -o greeting.o Both source files can be individually compiled without the extra typing by adding both filenames to the command-line: gcc -c main.c greeting.c -Wall -Wextra -Werror Assuming that both source files were successfully compiled, are you able to create an executable by linking together object files main.o and greeting.o? Select one: True False

False (correct)

When all source files are compiled, the object files are given to a program called linker. The linker resolves references between the object files, adds functions from the standard run-time library (such as function printf()), and detects some programming errors such as the failure to define a needed function. The output of the linker program is a single executable program, which can then be invoked or run. First, compile source files main.c and greeting.c separately: gcc -c main.c -Wall -Werror -Wextra -ansi -pedantic -o main.o gcc -c greeting.c -Wall -Werror -Wextra -ansi -pedantic -o greeting.o The separate compilations of main.c and greeting.c to generate corresponding object files main.o and greeting.o respectively, should again reinforce the idea that the process of compiling takes as input a single source file. Now, that we've individually compiled source files main.c and greeting.c to object files main.o and greeting.o respectively, let's understand the idea that linking is a collective process requiring the linker to process all necessary object files together to create an executable program. Create the executable program test.exe by calling the linker and supply it only the object file main.o: gcc main.o -c test.exe Were you able to successfully create the executable program test.exe? Select one: True False

False (correct)

/* main.c */ int main(void) { greeting(); return 0; } Source file main.c will not compile - instead the compiler terminates with the error message: "implicit declaration of function 'greeting'". The compiler signals an error because main() is using a name greeting that is not known to the compiler thereby preventing it from understanding and then translating the actions represented by the unknown object greeting. Therefore, all objects such as variables and functions must be made known to the compiler before their first use. This allows the compiler to correctly translate the values represented by these objects in the object program. In short, the programmer must declare objects before they're used in a function. A function declaration introduces a reference to a function implemented elsewhere. The function declaration makes known to the compiler the name and type of the function along with the number and type of inputs (or, in programming terms parameters) received by the function. Once the compiler has "seen" the declaration, it can ensure the function is being used correctly in subsequent text in the source file. For example, consider a function foo() that requires three int parameters and it is being called with only two int values. If the compiler has seen the function's declaration, it would be able to flag the call as an incorrect use of foo(). Notice that function greeting() is declared in file greeting.c. /* greeting.c */ #include <stdio.h> /* declares function printf() */ /* We're declaring the function greeting() because we're providing the compiler the following information: 1) name of the function (greeting), 2) type of the function (void), 3) number of parameters or inputs (one), and 4) type of the parameter (void) */ void greeting(void) { printf("Hello World!\n"); } Similarly, function main() is being declared in main.c. However, main.c doesn't contain a declaration of function greeting(): /* main.c */ /* We're declaring the function main() because we're providing the compiler the following information: 1) name of the function (main), 2) type of the function (int), 3) number of parameters or inputs (one), and 4) type of the parameter (void) */ int main(void) { greeting(); return 0; } Based on this discussion, what change would you make to main.c so that it can be successfully compiled? Were you able to successfully compile main.c and generate a corresponding output object file main.o? Select one: True False

True (correct)

A header file is a file containing related C declarations and macro definitions (which will be discussed later) to be shared between several source files. Why do programmers use header files? The first reason is that errors generated by repeated typing or "copying and pasting" of declarations can be removed. Consider a source file stats.c defining functions that perform statistical analysis on data. Let find_median(), find_mean(), find_max(), find_min(), sort_array(), and print_stats() be some of the functions defined in stats.c. Many other source files in the program may wish to call some or all of these functions. Just as with main.c (where we had to declare function greeting()), every source file must declare the functions it is using. One method is to copy and paste the appropriate declaration. However, copying and pasting declarations in many different source files is tedious, time-consuming and error-prone. Instead, it would be better to group these related declarations in a header file called stats.h that is somehow included by source files (more on this later) requiring these declarations. Collecting related declarations in a single header file stats.h localizes any changes to declarations to that file. When the header file gets updated, source files including stats.h will automatically use the new version when next recompiled. Otherwise, programmers will have to identify, locate and update every source file declaring these functions grouped in header file stats.h. Robust and well-designed libraries can be implemented by separating implementation details (in a source file) from the interface (the grouping of related declarations in a header file). Header file stats.h provides the interface (what can the statistical package do?) while the object file stats.o contains the implementation details (how does the statistical package do what it can do?). Other programmers can use the statistical package (see an example source file stats_driver.c) without concerning themselves with the implementation details by linking with stats.o (notice that users have access to only the object file not the source file stats.c). Would you agree that grouping related declarations in a header file is a major convenience to programmers because clutter in source files is reduced, repeated typing of the same declarations in multiple source files is eliminated, changes to declarations are localized to the header file, and robust and well-designed libraries can be implemented? Select one: True False

True (correct)

Although function greeting() was declared in both source files main.c and greeting.c, there is a difference between the two declarations. Function greeting() is implemented in source file greeting.c while main.c contains only a declaration - notice the implementation of function greeting() in file greeting.c: /* greeting.c */ #include <stdio.h> /* declares function printf() */ /* We're defining the function greeting() because we're providing the compiler the following information: 1) name of the function (greeting), 2) type of the function (void), 3) number of parameters or inputs (one), and 4) type of the parameter (void) 5) the implementation details or the body of the function */ void greeting(void) { printf("Hello World!\n"); } In source file main.c, function main() is defined while function greeting() is declared: /* main.c */ /* We're declaring the function greeting() because we're providing the compiler the following information: 1) name of the function (greeting), 2) type of the function (void), 3) number of parameters or inputs (one), and 4) type of the parameter (void) */ void greeting(void); /* We're defining the function main() because we're providing the compiler the following information: 1) name of the function (main), 2) type of the function (int), 3) number of parameters or inputs (one), and 4) type of the parameter (void) 5) the implementation details or the body of the function */ int main(void) { greeting(); return 0; } Function implementation means that memory will be reserved for the instructions of that function. A function declaration simply announces to the compiler the name and type of the function as well as the number and type of the function's inputs. We need terminology to distinguish between these two declarations. The term definition is introduced to distinguish a function's implementation from its declaration. We can then differentiate the meaning behind the terms function declaration and function definition by suggesting that function declaration plus function implementation equals function definition. From the above discussion, would you agree that function greeting() is being declared but not defined in main.c? Select one: True False

True (correct)

Based on our discussion so far, would you agree that every C program must have one and only one function called main. Select one: True False

True (correct)

Recall that a function declaration introduces a reference to a function object defined elsewhere. The function declaration makes known to the compiler the name and type of the function along with the number and type of parameters received by the function. This allows the compiler to determine if the function is being referenced correctly in the subsequent text in the source file. For example, consider a function foo() that requires three int parameters and it is being called with only two int values. Since the compiler has seen the function's declaration, it will be able to flag the call as an error because of the incorrect use of foo() by the caller. Now, consider the updated source file main.c: /* main.c */ /* We're declaring the function greeting() because we're providing the compiler the following information: 1) name of the function (greeting), 2) type of the function (void), 3) number of parameters or inputs (one), and 4) type of the parameter (void) */ void greeting(void); int main(void) { greeting(); return 0; } And, consider the source file greeting.c: #include <stdio.h> /* declares function printf() */ /* We're declaring the function greeting() because we're providing the compiler the following information: 1) name of the function (greeting), 2) type of the function (void), 3) number of parameters or inputs (one), and 4) type of the parameter (void) */ void greeting(void) { printf("Hello World!"); } Would you agree that function greeting() is being declared in both source files? Select one: True False

True (correct)

Recall that a function declaration is just a reference to a function object defined (that is, implemented) elsewhere. The function declaration makes known to the compiler the name and type of the function along with the number and type of inputs (or, in programming terms parameters) received by the function. This allows the compiler to determine if the function is being referenced correctly in the subsequent text in the source file. Since a declaration doesn't actually implement a function but simply tells the compiler the type and name of the function implemented elsewhere, can a source file have multiple declarations of the same function with redundant declarations being ignored by the compiler? Check if main.c successfully compiles after the addition of multiple declarations of function greeting(). To answer this question, copy and paste the line in main.c containing the declaration of function greeting() multiple times and then recompile main.c. Select one: True False

True (correct)

Since header files are a major convenience, how can programmers have header files inserted into source files so that the compiler can see the declarations? A special program called the preprocessor was created to perform this and many other basic text editing tasks. The preprocessor is a text editing program that is invoked by the C compiler to edit the original source file and write out a new "preprocessed" source file that that can then be used as input to the C compiler. In addition to inserting files, the preprocessor can also be used to remove code and perform text substitution. The preprocessor is controlled by special preprocessor directive (or command) lines, which are lines of the source file beginning with the character #. The preprocessor typically removes all preprocessor directive lines from the source file and makes additional transformations on the source file as specified by the directives, such as text substitution. Note that the syntax of preprocessor directives is completely independent of the syntax of the rest of the C language. A comprehensive reference of the C/C++ preprocessor can be found here. For now, we'll look at the #include directive. Programmers request use of a header file in a source file by including it with the preprocessing directive '#include'. There are two forms of the #include directive. The form #include <filename> searches for the file filename in certain standard places according to implementation-defined search rules. We've used this form in the source greeting.c when we had to include the standard C library header file stdio.h. The form #include "filename" will also search for filename in the standard places, but usually after searching some local places, such as the programmer's current directory. The general rule-of-thumb is that the #include "filename" form is used to refer to header files written by the programmer, while the #include <filename> form is used to refer to standard implementation files. Now, create a header file greetings.h with the following text: /* greetings.h - declarations for all types of greetings */ void greetings(void); Replace the declaration of greetings() in main.c with the preprocessor #include directive for header file greetings.h: #include "greetings.h" int main(void) { greetings(); return 0; } Are you able to compile (but not link) main.c after replacing the declaration of greetings() with the preprocessor #include directive for header file greetings.h? Are you able to link main.o and greeting.o to create an executable? Select one: True False

True (correct)

Standard C requires the compiler to replace a comment in a source file with a single space character. Select one: True False

True (correct)

Which option should be used with gcc to compile a C99 source file? Use this gcc reference page to determine the correct answer. Select one: a. -std=c99 b. -std=c90 c. -ansi d. -pedantic e. -std=c89

a. -std=c99 (Correct)

Which option should be used with gcc for verbose compilation so that detailed information about the exact sequence of commands used to compile and link a program is displayed? Use this gcc reference page to determine the correct answer. Select one: a. -v b. -E c. -o d. -c e. -S

a. -v (Correct)

The original ANSI (American National Standards Institute) C standard was ratified in 1989 and published in 1990. This standard was ratified as an ISO (International Standards Organization) standard in 1990. There are no differences between these standards and they are commonly known as C89 and sometimes C90. A second major revision occurred in 1995 and this standard is known as C94 and sometimes C95. A third revision occurred in 1999 and is commonly known as C99. A fourth version of the standard was published in 2011 and it is commonly referred to as C11. Throughout this semester, we'll be concentrating on using C89 (also labeled as C90). Since C programs written for C89 will compile with newer versions of C and also with C++, we can learn how to program quickly without worrying about differences between versions. Second, you can fit in without a problem with organizations still using C89 and those that have migrated to newer versions of C or to C++. By default gcc doesn't compile for the C89 standard. Which option(s) must be specified for gcc to compile for the C89 standard? Use this gcc reference page to research the correct answer. You must provide all correct options for full credit. Select one or more: a. -ansi (Hint: Use the following command in Cygwin to display a summary of top-level gcc command-line options: gcc --help) b. -pedantic c. -std=c90 (Hint: Use the following command in Cygwin to display a summary of top-level gcc command-line options: gcc --help) d. -std=iso9899:1990 (Hint: Use the following command in Cygwin to display a summary of top-level gcc command-line options: gcc --help) e. -std=c89 (Hint: Use the following command in Cygwin to display a summary of top-level gcc command-line options: gcc --help)

a.-ansi c.-std=c89 d.-std=c90 e.-std=iso9899:1990

gcc implicitly assumes that files with suffix .c are C source files and will automatically invoke the C compiler. Assume that we've invoked the preprocessor on a C source file main.c and redirected its output to a text file main.i. How can we now use gcc to compile (but not link) using the file main.i generated by the preprocessor? Use the GNU C Compiler options page to determine the correct answer. Select one: a. gcc -E -c main.i b. gcc -o -x c main.i c. gcc -x c main.i d. gcc -c -x c main.i

d. gcc -c -x c main.i (correct)

Which option must be used to inform gcc to stop after the preprocessing stage without running the compiler proper? Use the GNU Compiler options page to find out the correct answer. Select one: a. -S b. -c c. -x d. -o e. -E

e. -E (correct)

Compile and link the following C99 program and write the exact result displayed when the executable program is run. #include <stdio.h> void foo(int *pa, int *pb); int main() { int num1 = 0x11, num2 = 0x77; foo(&num1, &num2); printf("%d,%d", num1, num2); return 0; } inline void foo(int *pa, int *pb) { int tmp = *pa; *pa = *pb; *pb = tmp; }

119,17

Link object files stats_driver.o (generated by the assembler) and stats.o (the implementation of the statistics library) to generate an executable stats_test.exe. Run the executable and report the comma-separated maximum, minimum, mean, and median values.

250,2,93,87 (correct)

How many valid C99 comments does the following program contain? // program to compute the square of the first 10 numbers #include <stdio.h> int main(void /* no arguments */) { int i; /* //Loop from 1 to 10, printing out the squares */ for (i = 1; i <= 10; ++i) { printf("%d //square is// %d\n", i, i*i); } return 0; }

3 (correct)

Assume you've implemented source file stats_driver.c that uses the statistics functions declared in stats.h and implemented in object file stats.o. Compile source file stats_driver.c and link with stats.o to create an executable program stats_test.exe. Execute the program stats_test.exe and write the comma separated mean and median values printed to standard output. If your installation of Cygwin is preventing linking with stats.o, compile the source file stats.c to create your own version of stats.o.

93,87 (correct)

All C programs must define a single external function named main. This function will become the entry point of the program - that is, the first function executed when the program is started. Returning from this function terminates the program, and the returned value is treated as an indication of program success or failure. Edit file main.c and amend the name of function main() to foo(). Next, compile main.c and supply object files main.o and greeting.o to the linker. Were you able to create an executable program? Select one: True False

False (correct)

The linker is unable to link object files main.o and greeting.o because it finds two definitions (or implementations) of the function greeting() - one in main.o and the second in greeting.o. And, further, the linker is not programmed to choose one of the two definitions and discard the other. Instead, the linker is programmed to stop the linking process and flag an error about the presence of multiple definitions. Let's review our terminology. A definition is a special kind of declaration that creates an object (function or variable); a declaration indicates a name that allow you to refer to an object created here or elsewhere. definition occurs in only one place specifies the type of an object; reserves storage for it; is used to create new objects example: int foo(void) { /* implementation */ } declaration can occur multiple times describes the type of an object; is used to refer to objects defined elsewhere (e.g., in another file) so that the compiler can correctly translate the use of this object example: int foo(void); Based on our discussions so far, would you agree that although there can be multiple declarations of a function in a program's source files, a function can have only a single definition in these source files? Select one: True False

True (correct)

The output generated by the preprocessor processor can be saved to file using the -E option: gcc -E main.c -o main.i By viewing the file main.i generated by the preprocessor stage, can you confirm that comments are replaced by single space and the line #include"greeting.h" is replaced by the contents of header file greeting.h? Select one: True False

True (correct)

Let's review the different compilation stages one more time. The exact sequence of compilation stages processed by a single invocation of gcc is: preprocessing, compilation, assembling, and linking. Let's use the statistics program (stats.h, stats.o, stats_driver.c) that was previously introduced to examine the output from each compilation stage beginning with the preprocessing stage. If for some reason, you're unable to use the object file stats.o, use the stats.c source file to generate a corresponding object file stats.o. Which of the following commands write the output of the preprocessing stage into file stats_driver.i? Examine the output file stats_driver.i and confirm that the #include (and the #define) commands were correctly executed by the preprocesser. Assume that we're using the C89 standard. Use this page to determine the correct answer. Select one: a. gcc -std=c89 -E stats_driver.c -Wall -Wextra -Werror -o stats_driver.i b. gcc -std=c89 -x stats_driver.c -Wall -Wextra -Werror -o stats_driver.i c. gcc -std=c89 -c stats_driver.c -Wall -Wextra -Werror -o stats_driver.i d. gcc -std=c89 -S stats_driver.c -Wall -Wextra -Werror -o stats_driver.i

a. gcc -std=c89 -E stats_driver.c -Wall -Wextra -Werror -o stats_driver.i (correct)

Now, the next step is to translate the assembly code in stats_driver.s into machine language and generate the corresponding object file stats_driver.o. Using appropriate options to gcc, translate the assembly file stats_driver.s that was generated by the compiling stage into the corresponding object file stats_driver.o. Select one: a. gcc -S stats_driver.s -o stats_driver.o b. gcc -c -x assembler stats_driver.s -o stats_driver.o c. gcc stats_driver.s -o stats_driver.o d. gcc -c assembler stats_driver.s -o stats_driver.o

b. gcc -c -x assembler stats_driver.s -o stats_driver.o (correct)

How many different ways can you write a comment in C89? Select one: a. 5 b. 4 c. 1 d. 3 e. 2

c. 1 (correct)

The exact sequence of compilation stages processed by a single invocation of gcc is: Select one: a. preprocessing, compiling proper, linking, assembling b. preprocessing, assembling, compiling proper, linking c. preprocessing, compiling proper, assembling, linking d. preprocessing, assembling, linking, compiling proper

c. preprocessing, compiling proper, assembling, linking (correct)

Which option should be used with gcc to only compile a source file into an object file but not link the object file? Select one: a. -v b. -S c. -W d. -c e. -o

d. -c (correct)

You've successfully compiled source files main.c and greeting.c to object files main.o and greeting.o, respectively. Which of the following commands invoke only the linker so that object files main.o and greeting.o (along with standard library function printf()) can be linked to create executable file test.exe? Select one: a. gcc main.o greeting.o b. gcc -c main.o greeting.o -o test.exe c. gcc main.c greeting.c -c test.exe d. gcc main.o greeting.o -o test.exe

d. gcc main.o greeting.o -o test.exe (correct)

The preprocessing stage's output stats_driver.i is then passed to the compiling stage. Since writing a compiler is a non-trivial task, the compilation stage is divided into several phases with well-defined interfaces. Conceptually, these phases operate in sequence, each phase (except the first) taking the output from the previous phase as its input. A common division into phases is described below: Lexical analysis: This is the initial part of the reading and analyzing the program text in stats_driver.i. The text is read and divided into tokens, each of which corresponds to a symbol in the C programming language, e.g., a variable name, keyword, a function name, or number. Syntax analysis: This phase - also called parsing - takes the list of tokens produced by the lexical analysis and arranges them in a tree-like structure called the syntax tree that reflects the structure of the program. Type checking: This phase analyzes the syntax tree to determine if the program violates certain consistency requirements, e.g., if a variable is used but not declared or if it is used in a context that doesn't make sense given the type of the variable, such as using a function that returns nothing as an operand of the addition operator. Assembly code generation: The program is translated to assembly language for a specific machine architecture. Using appropriate options to gcc, compile the file stats_driver.i that was generated by the preprocessing stage but ensure that compilation stops after generating the assembly language version of the C89 source file. Select one: a. gcc -std=c89 -Wall -Wextra -Werror -x c -c stats_driver.i -o stats_driver.s b. gcc -Wall -Wextra -Werror -S -x c stats_driver.i -o stats_driver.s c. gcc -Wall -Wextra -Werror -c -x c stats_driver.i -o stats_driver.s d. gcc -std=c89 -Wall -Wextra -Werror -x c -S stats_driver.i -o stats_driver.s

d. gcc -std=c89 -Wall -Wextra -Werror -x c -S stats_driver.i -o stats_driver.s (correct)


Conjuntos de estudio relacionados

Human A/P lab 1 language of anatomy

View Set

Life Insurance Policy, Provisions, Riders, and Options

View Set

Microbiology Exam 1 Topic 5 Review

View Set

Chapter 4: Longevity, Health, and functioning

View Set

Histology complete exam revision

View Set

Cognitive Psychology- Ch. 2 Practice Questions

View Set

BMS 301 Exam #3 (Quiz Questions)

View Set