Intermediate C-Programming PDF
Document Details
Uploaded by Deleted User
Tags
Summary
This document provides an introduction to intermediate C programming concepts and compilation. It covers various aspects that are useful for learning C programming.
Full Transcript
Starting of Intermediate C-programming ▪ Many programs that run on Linux systems are written in C, which is a language that has been around for almost 50 years, and is still widely used to this day. The Linux system and many o...
Starting of Intermediate C-programming ▪ Many programs that run on Linux systems are written in C, which is a language that has been around for almost 50 years, and is still widely used to this day. The Linux system and many of its commands are written in the C language. C is a compact and efficient general-purpose programming language that has evolved together with UNIX and Linux. Thus, C is regarded as the native language for Linux. ▪ C is a language that allows for programmers to access low level system commands, yet still be human readable, and offers a fair amount of abstraction! C is compiled down to system level binaries. ▪ Because of its importance, C has been standardized by the American National Standards Institute (ANSI) and later by the International Organization for Standardization (ISO). The latest standard is known as ISO C99. The C99 standard specifies language constructs and a Standard C Library API (Application Programming Interface) for common operations, such as I/O (input/output) and string handling. Compilation ▪ Before we do anything else, we need to know how to run our programs. C is not as simple as bash (where you just type the name of the program and it just runs). C is a compiled language. ▪ If you've used IDEs such as Eclipse, C-lion, XCode, VS Code, Dev C++ all of this compilation is hidden away from you! You press the "build" or "play" button, and the program just runs. However, what's going on behind the scenes is that a command is being run on some C code. ▪ So first, let's write a basic program, and it wouldn't be an introduction if our first program wasn't hello world. ▪ Just like C++ and Java, C requires semicolons after every clause. We need to include stdio.h to use the printf function defined in there. Since our main function has an integer as a return type, we need to return something, so we're going to return 0. ▪ If you remember back to Chapter 5 of mastering modern Linux, all programs in Linux return some exit status. These exit statuses range from 0 to 255. Later we will use predefined constants to illustrate our success and failure. 1 ▪ To compile the program, in your command line type: ▪ This will produce an executable file a.out. In Linux systems, it should be noted that compiled executables don't have the.exe file extension that windows executables do. ▪ To run an executable file in Linux, you want to write: ▪ The./ is just using the current directory, providing the shell with the full path name of the file (just like our bash scripts). ▪ Suppose, however, that you didn't want your executable file to be named a.out. You could always rename it with the mv command, but that will become tedious. You can just as easily use the gcc 's -o option: ▪ This command means the following: Execute the gcc command installed in Linux. Use hello.c as the input for the gcc command. Set the output file name to be hello (-o specifies the name of the output file). The output file is an executable file, meaning that the computer can run it. ▪ Now, to run your new file: ▪ Do not do this: ▪ This command erases the file hello.c. The GCC Compiler ▪ A compiler not only translates programs into machine code to run on a particular computer, it also takes care of arranging suitable run-time support for the program by providing I/O, file access, and other interfaces to the operating system. Therefore, a compiler is not only computer hardware specific, but also operating system specific. ▪ On Linux, the C compiler will likely be GCC, which is part of the GNU compiler collection. ▪ Use man gcc command to explore GCC Compiler. ▪ The C compiler breaks the entire compilation process into five phases 1) Preprocessing 2) Compilation 3) Optimization 4) Assembly 5) Linking 2 Preprocessing The first phase is performed by the cpp (C preprocessor) program. It handles constant definition, macro expansion, file inclusion, conditionals, and other preprocessor directives. The C preprocessor, often known as cpp, is a macro processor that is used automatically by the C compiler to transform your program before compilation. It is called a macro processor because it allows you to define macros, which are brief abbreviations for longer constructs. Use man cpp command to explore C preprocessor. The preprocessor also allows for us to create constants and macros with define Now, we've defined in the preprocessor phase that there exists a constant SUCCESS that will let us return 0 (and is a bit more human readable). We've additionally created a macro called SQUARE. Macro's work similarly to functions in that they take in an argument, and can return square of it. Conditional inclusion of header files ▪ Conditional inclusion is typically achieved through the use of preprocessor directives like #ifdef, #ifndef, #elif, #else, and #endif. ▪ #ifdef (if defined) and #ifndef (if not defined) are used to check if a macro or symbol is defined. If the specified macro or symbol is defined, or not defined, respectively, then the code enclosed by these directives will be included. 3 ▪ #else: Used in conjunction with #ifdef or #ifndef, #else specifies the code that should be included if the condition in the corresponding #ifdef or #ifndef is not met. ▪ Above we checked to see if there was a definition for EXIT_SUCCESS, if there is, then we'll just define SUCCESS as that, but otherwise, we can define SUCCESS as a -1. 4 ▪ In the next example, execute program with /without comment on #define TEST and compare the output. Class Work!!! 1. Write a C-program to calculate cube of a given number using the concept of C- preprocessor. 2. Write a C program to determine whether a given number is even or odd using the concept of C-preprocessor. ▪ Compilation: Taking the output of the previous phase as input, the compilation phase performs syntax checking, parsing, and assembly code (.s file) generation. ▪ Optimization: This optional phase specializes the code to the computer’s hardware architecture and improves the efficiency of the generated code for speed and compactness. Depending on whether or not a particular flag was set for the compiler, the code could be optimized, but the optimization phase is entirely optional. ▪ Assembly: The assembler program as takes (.s) files and creates object (.o) files containing binary code and relocation information to be used by the linker/loader. ▪ Linking: The linker/loader which combines all object files and links in necessary library subroutines as well as run-time support routines to produce an executable program (a.out). 5 Redirect Output It is useful to redirect the information from the program and save it to a file. Here are some scenarios when this may be useful: A program prints too much too fast and the computer screen cannot display everything printed. You do not want to wait while the program runs, but instead want to see the information later. Sometimes it is useful to check whether the program produces the same information when it is run again. If the program produces more than several lines of output checking the output line-by-line is too much work. The program may need to be run on many computers simultaneously. It may be impossible to watch many screens at once. You need to write a program that produces correct outputs based on given inputs. This is frequently the case when taking programming courses. The correctness of the program is evaluated by whether your program produces correct outputs. In many cases, the programs are graded by computer programs based on the input-output pairs. In this case, nobody reads the information on a computer screen. 6 Command Line Arguments Programs take input, either as command line arguments, files, or standard input. C programs do the exact same. Let's write a quick file to read through some command line arguments: What's happening above is that we have an integer i declared, which we then use as an index for our for loop. Inside, our loop, we're setting our stopping condition as argc, or the count of the arguments. Afterward, we're then printing the associated index of the argument along with the string of the argument itself: We passed in no arguments, however, we still saw Argument 0 is./result. Does this remind you of anything? Possibly something similar to positional parameters? Let's try it with a few more arguments (all whitespace delimited): 7 Standard Input / Output ▪ getchar(): Reads a single character from the standard input. ▪ putchar(): Writes a single character to the standard output. ▪ scanf(): Reads formatted input from the standard input (usually the keyboard) based on a format specifier. ▪ printf(): Writes formatted output to the standard output (usually the console). 8 9 cryptography.txt ecnodeText.txt decode.txt 10 Working with files ▪ Redirecting data from standard out is great, but piping input and output to and from files can get messy. There isn't much of a paper trail if you're moving things via stdin and std out. It's just data streams. If you wish to be more consistent and write your data to a file, then you'll want to use the fopen command. ▪ When working with files, you need to declare a pointer of type file. This declaration is needed for communication between the file and program. FILE *filePointer; ▪ Opening a file: Opening a file is performed using the library function in the "stdio.h" header file: fopen(). The syntax for opening a file in standard I/O is: filePointer = fopen ("filename","fileMode"); ▪ Reading File - fgetc () function: The fgetc () function is used to read a single character from a file. It gets a character from the stream. It returns EOF at the end of file. Syntax: ch=fgetc(filePointer); ▪ Writing File - fputc() function: The fputc() function is used to write a single character into file. It outputs a character to a stream. Syntax: fputc(ch, filepointer); This function writes the character variable ch to the files associated with file pointer. ▪ Writing File - fprintf() function: The fprintf() function is used to write set of characters into file. It sends formatted output to a stream. Syntax: fprintf(filepointer, “Control Strings”,list); ▪ Reading File - fscanf() function: The fscanf() function is used to read set of characters from file. It reads a word from the file and returns EOF at the end of file. Syntax: fscanf(filepointer, “Control Strings”, &list); File Modes 11 12 13 14 Class Work!!! 1. Write a C program that reads numbers via command-line arguments and saves an even number at "even.txt" and an odd number at "odd.txt." 2. Write a C program that reads the file's content "input.txt" and converts all the characters into uppercase characters in the file "case.txt." Assume that the "case.txt" has already some content. The program should also read file names as command-line arguments. 3. Write a C-program that reads the content of the file "input.txt" and encode content of file by "+5" and save as "encode.txt". The program should also read file names as command- line arguments. 15 Get Options with “getopt” While this is not in the book, this is crucial information to know. Attempting to enforce how users input data based on positional input parameters can be difficult. People will forget and input different parameters in different places and ultimately cause a mess. To get around this, we can use getopt to have our c program take flagged inputs! Let's change our final program to not use positional parameters for our files, but instead use getopt. First and foremost, we want to call getopt with a loop since we'll be iterating over options provided by the user. The getopt used for parsing commands-line arguments. It is commonly used for handling command line options and arguments. The getopt typically expects the command-line arguments to be in a certain format, where options are preceded by a hyphen (-) and they may or may not take arguments. myprogram -f input.txt -v -o=output.txt The getopt function is used to iterate through the command-line arguments and extract options and their arguments. It returns the next option character found and sets the optarg variable to the corresponding argument (if any). The optarg holds the options for argument value. In this example, the command getopt(argc, argv, "hi:o:e:d:") tells us that there are five potential options for our program! Option h is for help. Since it has no following “:” that means that it takes no arguments! Options i and o take in input files and output file names respectively, while options e and d update the encoder and decoder, where the user supplies a number key! In this example, if an invalid option or a missing argument is encountered, the code checks the value of optopt to determine which option character caused the error. This allows for specific error messages or actions to be taken based on the problematic option. When you use getopt to process command-line arguments, optarg is set to the argument value that follows an option that requires an argument. When getopt encounters an option that requires an argument (indicated by the optstring provided to getopt), it places the argument value in the optarg variable. Your program can access the value of optarg to retrieve and use the argument associated with the option. The optopt variable is a global variable in C used in conjunction with the getopt and related functions. It is used to retrieve the last option character (i.e., the last valid option) parsed by getopt when it encounters an unrecognized option or an option missing its required argument. isprint(): It is used to check if a given character is a printable character. Header files for getopt: In C, you include ; in C++, you include. 16 17 18 Class Work!!! 1. Write a C program that performs addition and subtraction of two numbers. Use the concept of getopt() function. Use -a for addition and -s for subtraction. Your program must read numbers and option via command line arguments. 19 Makefiles A Makefile is a special text file used in Unix-like operating systems to manage the building and compilation of programs and projects. Makefiles are essential for automating the build process, especially when a program consists of multiple source files or when the build process involves complex dependencies. The Makefile typically contains rules and instructions for building a program, specifying how source code files are compiled, linked, and turned into executable files. The make utility reads the Makefile and executes the rules, only rebuilding the parts of the program that have changed since the last build. This minimizes compilation time and optimizes the build process. As your programs get larger and larger, you'll a) want to separate your code into different files to make for an easier reading / coding experience, and b) compile all of these files (not just the main). 20 This program is getting pretty long. What if we broke it up a little bit? First, let's extract our addition function to a new file: 21 When we go to compile this file, we then write: All in all, this is fine. However, as programs grow in size, the file sizes also grow, let's extract our tests into a separate file too: 22 Now that we've extracted these tests, we still need declarations in our main, so let's add those: Using Header file concept Now we've extracted out tests and our functions into other files! The downside though, is that our main file is still a little crowded looking with the function declarations up top. If we had a ton of functions this would take up entire screens worth of real estate! What if we extracted those into header files for each of our files? If we did that, our whole program would look like: 23 Now we've got our files pretty clean, each is doing their own thing, but compiling this can huge pain: 24 The longer your program gets, the more files you'll need to add. In an IDE's environment, you'd only need to press the play button, but via the command line, you have to do a bit more! That's why make exists! make To use make, we'll need to have a special file called the Makefile. The Makefile is something that the make utility will use to determine what needs to be recompiled, and what doesn't. And it's a ton easier to type make than to type out gcc mathOperation.h mathOperation.c tests.h tests.c doMath.c -o math How a makefile works is that you first, indicate the dependencies. Because we're making the object file, we'll be using gcc 's flag'. The -o option in the context of the gcc compiler (or other compilers) specifies the output file or executable name. It is used to specify the name of the file that will be generated by the compilation process. In the context of a Makefile and the gcc compiler (or other C/C++ compilers), the -c flag is used to instruct the compiler to generate object files from source files without linking. When you compile a source file (e.g.,.c file) without the -c flag, the compiler performs two steps: it compiles the source code into an object file and then links it with other object files to create an executable program. When you compile with the -c flag, the compiler only performs the compilation step and generates an object file (e.g.,.o file) from the source file. It stops short of the linking step, so it does not create an executable. The -g flags includes debugging information in the executable binary. It is useful for debugging your program with tools like “GDB”. The -Wall flags enables set of warning message. The -Wshadow flags includes additional warning related to variable shadowing. Variable shadowing occurs when a variable in a nested scope. GCC: This is a variable that specifies the C compiler to be used. In this case, gcc is used. CFLAGS: This is a variable that specifies compiler flags. -Wall enables all compiler warnings, and -g includes debugging information. Makefile 25 Now if you then type: make You'll see you've created a doMathAgain2.o file in your project! Using macros like we did above is great, and it gives a bit more clarity as to what's happening, but we can even further abstract our makefile! We can take our dependencies and our object files, and make those into macros and let make do all the work for us! What this file does is lets us set all of our dependencies and our object files entirely to the DEPS and OBJ variables. We then let make do all of the work for us by using the automatic variables $@ which uses the filenames of the targets of the rule (our dependencies), and then are named with $< which targets the prerequisite. $