Directory:
- GCC rules
- Start...
- Pre-compile
- Compile
- Assembly
- Connection
- Two other important options
- Debugging
- Summary
- Site Link
Summary: To read this article, you need to have a basic understanding of the C language. This article will introduce how to use the gcc compiler. First, we will introduce how to use the compiler to compile simple C source code in the command line mode. Next, let's briefly introduce what the compiler has done and how to control the compilation process. We also briefly introduced how to use the debugger.
GCC rulesCan you imagine using a private compiler with closed source code to compile free software? How do you know what the compiler adds to your executable file? Various backdoors and Trojans may be added. Ken Thompson is a famous hacker who compiled a compiler. When the compiler compiles itself, it leaves a backdoor and a permanent Trojan horse in the 'login' program. Read his description of this masterpiece here. Fortunately, we have gcc. When you proceedconfigure; make; make install Gcc has done a lot of heavy work behind the scenes. How can we make gcc work for us? We will start to write a card game, but we just want to demonstrate the functions of the compiler, so we try to streamline the code as much as possible. We will start from scratch step by step, in order to understand the compilation process, understand what needs to be done to make executable files, and in what order. Let's take a look at how to compile C programs, and how to use the compilation options to make gcc work as required. The steps (and tools used) are as follows: Pre-compilation (gcc-E), compilation (gcc), Assembly (as), and connection (ld ). Start...First, we should know how to call the compiler. In fact, this is very simple. We will start with the famous first C program. (Forgive me ). #include <stdio.h>int main() { printf("Hello World!\n");} Save this filegame.c . You can compile it in the command line: gcc game.c By default, the C compiler generatesa.out . Run the following command: a.outHello World Each time the program is compiled, the newa.out Will overwrite the original program. You cannot know which program is createda.out . We can use-o The compilation option tells gcc what name the executable file is. We will call this programgame , We can use any name, because C does not have naming restrictions like Java. gcc -o game game.c gameHello World So far, we are far from a useful program. If you are frustrated, you can think about a program that we have compiled and run. Because we add features for this program at, we must ensure that it can run. It seems that every programmer who just started learning programming wants to compile a 1000-line program and then modify all the errors at a time. No one. I mean no one can do this. You should first compile a small program that can be run, modify it, and then let it run again. This can limit the number of errors you modify at a time. In addition, You know what changes have been made to make the program unable to run, so you know where to focus your attention. This can prevent the occurrence of this situation: You think that what you write should be able to work, it can also be compiled, but it just cannot run. Remember that a program that can be compiled does not mean it is correct. Next, write a header file for our game. The header file gathers the data type and function declaration in one place. This ensures consistency of data structure definitions so that every part of the program can view everything in the same way. #ifndef DECK_H#define DECK_H#define DECKSIZE 52typedef struct deck_t{ int card[DECKSIZE]; /* number of cards used */ int dealt;}deck_t;#endif /* DECK_H */ Save this filedeck.h . Compile only.c File, so we must modify game. c. In row 3 of game. c, write#include "deck.h" . Write in row 5thdeck_t deck; . To ensure that there is no mistake, recompile it once. gcc -o game game.c If there is no error, there is no problem. If the compilation fails, modify it until it passes. Pre-compileHow does the compiler know?deck_t What is the type? During the pre-compilation, it actually copies the "deck. h" file to the "game. c" file. The pre-compilation instructions in the source code are prefixed. You can add-E To call the pre-compiler. gcc -E -o game_precompile.txt game.cwc -l game_precompile.txt 3199 game_precompile.txt There are almost 3200 rows of output! Most of them come fromstdio.h Contains files, but if you view this file, our statement is also there. If you do not need-o If the output file name is specified, it is output to the console. The pre-compilation process provides great flexibility for the code by completing three main tasks.
- Copy the "include" file to the source file to be compiled.
- Replace "define" text with actual values.
- Macro replacement is performed at the place where macro is called.
This enables you to use Symbolic constants (DECKSIZE is used to represent the number of cards in a pay card) in the entire source file, and symbolic constants are defined in one place, if its value changes, all the places where symbolic constants are used can be automatically updated. In practice, you almost do not need to use it independently.-E To the compiler. CompileAs an intermediate step, gcc translates your code into an assembly language. It must do this. It must analyze your code to find out what you want to do. If you make a syntax error, it will tell you that the compilation will fail. People sometimes misunderstand this step as a whole process. However, there is actually a lot of work to do with gcc. Assemblyas Converts the assembly language code to the target code. In fact, the target code cannot run on the CPU, but it is very close to completion. Compiler Options-c Convert the. c file to a target file with the. o Extension. If we run
gcc -c game.c We automatically created a file named game. o. Here we have encountered an important problem. We can use any. c file to create a target file. As we can see below, we can combine these target files into executable files in the connection steps. Let's continue to introduce our example. Because we are writing a card game, we have defined one carddeck_t , We will compile a shuffles function. This function accepts a pointer to the deck type and loads a random card into the deck type. It uses the 'drawn 'array to track those cards that have been used. This array with DECKSIZE elements prevents us from reusing a card. #include <stdlib.h>#include <stdio.h>#include <time.h>#include "deck.h"static time_t seed = 0;void shuffle(deck_t *pdeck){ /* Keeps track of what numbers have been used */ int drawn[DECKSIZE] = {0}; int i; /* One time initialization of rand */ if(0 == seed) { seed = time(NULL); srand(seed); } for(i = 0; i < DECKSIZE; i++) { int value = -1; do { value = rand() % DECKSIZE; } while(drawn[value] != 0); /* mark value as used */ drawn[value] = 1; /* debug statement */ printf("%i\n", value); pdeck->card[i] = value; } pdeck->dealt = 0; return;} Save this fileshuffle.c . We added a debugging statement to the Code so that the generated number can be output during running. This didn't add functionality to our program, but now it's time to see what happened. Because our game is still in its infancy, we have no other way to determine whether our function has implemented the features we require. With the printf statement, we can know exactly what is happening now so that we can know that the card has been washed before the next phase. After we are satisfied with its work, we can delete that line of statements from the code. This debugging program looks rough, but it uses the least statement to complete the debugging task. We will introduce more complex debuggers later. Pay attention to two issues.
- We use the address transfer method to pass Parameters. You can see from the '&' operator. The machine address of the variable is passed to the function, so the function can change the value of the variable itself. You can also use global variables to write programs, but use as few global variables as possible. Pointer is an important part of C. You should fully understand it.
- We use function calls in a new. c file. The operating system always looks for a function named 'main' and starts execution from there.
shuffle.c The 'main' function does not exist, so it cannot be compiled into an independent executable file. We must combine it with another program with the 'main' function and call 'shuffle.
Run commands gcc -c shuffle.c And make sure that it createsshuffle.o . Edit the game. c file. In row 7th, In the deck_t Type Variabledeck After the declaration, add the following line: shuffle(&deck); Now, if we create an executable file as before, we will get an error gcc -o game game.c/tmp/ccmiHnJX.o: In function `main':/tmp/ccmiHnJX.o(.text+0xf): undefined reference to `shuffle'collect2: ld returned 1 exit status The compilation is successful because our syntax is correct. However, the connection step fails because we didn't tell the compiler where the 'shuffle' function is. So what is connection? How can we tell the compiler where to find this function? ConnectionConnectorld , Use the following command to acceptas Create the target file and convert it to an executable file. gcc -o game game.o shuffle.o This will combine the two target files and create executable filesgame . The connector is found in the target shuffle. o file.shuffle Function and include it into the executable file. The real benefit of the target file is that if we want to use that function again, all we need to do is include the "deck. h" file andshuffle.o The target file is connected to the new executable file. Code reuse like this often happens. Although we have not compiled the previous Code called as a debugging statementprintf Function, connector can be used from us#include <stdlib.h> Find its declaration in the file contained in the statement, and connect the target code stored in library C (/lib/libc. so.6. In this way, we can use functions of other people who have been able to work correctly, and only care about the problems we want to solve. This is why the header file generally only contains data and function declaration, without the function body. Generally, you can create a target file or function library for the connector to connect to the executable file. Our code may cause problems because we didn't put any function declaration in the header file. What else can we do to ensure smooth operation? Two other important options-Wall Option to enable all types of syntax warnings to help us determine that the code is correct and to achieve portability as much as possible. When we use this option to compile our code, we will see the following warning:
game.c:9: warning: implicit declaration of function `shuffle' This makes us know that there is still some work to be done. We need to add a line of code to the header file to tell the compiler aboutshuffle Function, so that it can perform the necessary checks. It sounds like a sophistry, but in this way, we can separate the definition and implementation of functions so that we can use our functions anywhere, you only need to include the new header file and connect it to our target file. Next we will add this line to deck. h. void shuffle(deck_t *pdeck); This eliminates the warning information. Another common compiler option is optimization.-O# (-O2 ). This is to tell the compiler what level of optimization you need. The compiler has a complete set of techniques to make your code run faster. For small programs like ours, you may not be aware of the difference, but for large programs, it can greatly increase the running speed. You will often encounter it, so you should know what it means. DebuggingAs we all know, code compilation does not mean it works as required. You can use the following command to verify whether all numbers are used. game | sort - n | less And check whether there are any omissions. What should we do if there is a problem? How can we find errors at the underlying layer? You can use the debugger to check your code. Most releases provide the famous Debugger gdb. If you are confused by the numerous command line options, you can use KDbg, a good front-end tool provided by KDE. Other front-end tools are similar. To start debugging, select File> Executable and find yourgame Program. When you press F5 or choose Execution> run from menu, you can see the output in another window. What's going on? We can't see anything in that window. Don't worry. There is no problem with KDbg. The problem is that we didn't add any debugging information to the executable file, so KDbg cannot tell us what happened internally. Compiler Options-g You can add necessary debugging information to the target file. You must use this option to compile the target file (with the extension. o), so the command line is: gcc -g -c shuffle.c game.cgcc -g -o game game.o shuffle.o This puts the hook into an executable file so that gdb and KDbg can indicate the running condition. Debugging is a very important technology and it is worth your time to learn how to use it. The debugger helps programmers set "breakpoints" in the source code ". Now you can right-click to callshuffle The line of code of the function, and try to set the breakpoint. A small red circle appears on the side of the line. Now, when you press the F5 key, the program will stop running on that line. Press F8 to jump into the shuffle function. Now we can seeshuffle.c The code in! We can control the program step by step and see what happened. If you pause the cursor on a local variable, you can see the content of the variable. Great. This oneprintf The statement is much better, isn't it? SummaryThis article describes how to compile and debug C Programs. We have discussed the steps taken by the compiler and the options that should be passed to gcc for the compiler to do this. We briefly talked about the connection to the shared function library, and finally introduced the debugger. It takes a lot of effort to really understand your work, but I hope this article will help you get started correctly. You cangcc ,as Andld Ofman Andinfo Page to find more information. You can write your own code to learn more. As an exercise, you can write a game based on the card game in this article. Then you can learn how to use the debugger. It is easier to start with the KDbg of the GUI. If you only add a few features at a time, you will soon be able to complete it. Remember to keep the program running all the time! To write a complete game, you need the following content:
- Definition of a card player (that is, you can define deck_t as player_t ).
- A function that sends a certain number of cards to a specified player. Remember to increase the number of licensed cards in order to know which cards are available. Remember the number of cards in the player's hands.
- Some interactions with users ask if the player needs another card.
- A function that prints cards in the player's hands.CardEqual to value % 13 (numbers 0 to 12 ),SuitEqual to value/13 (numbers 0 to 3 ).
- A function that determines the value in the player's hands. The value of Ace is zero and can be equal to 1 or 11. The value of King is 12 and can be equal to 10.
Site Link
- Gcc gcc gnu Compiler Collection
- Gdb GNU Debugger
- KDbg KDE's GUI Debugger
- Award Winning Compiler Hack Ken Thompson's great compiler hack
|