Le processus de compilation gcc et le combat de projet simple
1. Le processus de compilation gcc
En fait, lorsque nous utilisons c ou cpp, nous utilisons beaucoup de bibliothèques, qui sont écrites par d'autres pour que nous les utilisions directement, ce qui nous facilite grandement, mais lorsque nous faisons des projets, nous devons souvent séparer les fonctions nous-mêmes, puis Référence principale dans la fonction, vous devez utiliser le processus de compilation.
Comme le montre la figure: dans la
figure ci-dessus, notre fichier principal et les fichiers d'entrée et de calcul sont un dossier. Les fichiers d'entrée et de calcul sont implémentés séparément. L'un est l'entrée et l'autre est le calcul. Main implémente le calcul d'addition numérique, mais pour la fonction Nous allons clairement les séparer, de sorte qu'il s'agit d'une performance à faible couplage dans les projets de grande envergure.
Code source du projet: https://github.com/yjc-123/gcc-compile-process
Tout d'abord, nous devons comprendre le processus de compilation gcc:
Il y a quatre processus:
pré-compilation -> compilation -> assemblage -> liaison.
Ici, je parle principalement du processus de liaison
. La
raison pour laquelle la bibliothèque statique devient [bibliothèque statique] (.a) est que dans la phase de liaison, le l'assembly sera généré. Le fichier objet .o et la bibliothèque référencée sont liés et regroupés dans un fichier exécutable. Par conséquent, la méthode de liaison correspondante est appelée liaison statique.
- La liaison de la bibliothèque statique à la bibliothèque de fonctions est effectuée au moment de la compilation.
- Le programme n'a rien à voir avec la bibliothèque de fonctions lorsqu'il est en cours d'exécution, et il est facile à transplanter.
- L'espace et les ressources sont gaspillés, car tous les fichiers objets associés et les bibliothèques de fonctions impliquées sont liés dans un fichier exécutable.
Règles de dénomination de la bibliothèque statique Linux:
doit être "lib [votre_nom_library] .a": lib est le préfixe, le milieu est le nom de la bibliothèque statique et l'extension est .a
Bibliothèque dynamique
Étant donné que l'espace de la bibliothèque statique est gaspillé plus sérieusement, la bibliothèque dynamique ne sera pas liée au code cible lorsque le programme est compilé, mais sera chargée lorsque le programme est en cours d'exécution. Si différentes applications appellent la même bibliothèque, une seule instance de la bibliothèque partagée est nécessaire en mémoire, évitant ainsi le problème du gaspillage d'espace. La bibliothèque dynamique est chargée lorsque le programme est en cours d'exécution, ce qui résout également le problème causé par la page de mise à jour, de déploiement et de publication du programme par la bibliothèque statique. Les utilisateurs doivent uniquement mettre à jour la bibliothèque dynamique. Examinons ensuite la bibliothèque dynamique.
Ce sont les deux façons de lier, et enfin nous voyons ce qui se passe dans le combat réel.
2. Combat réel
Prenons mon exemple:
Nous écrivons d'abord la fonction principale.
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "input.h" //导入库
#include "calcu.h"
extern int input(); //引入input.h中的函数
extern int calcu(int a,int b); //引入calcu中的函数
int main(int argc, char ** argv){
int input_num_1 = input();
int input_num_2 = input();
printf("input over\n");
int sum;
sum = calcu(input_num_1, input_num_2);
printf("calcu successful ,the sum is :%d\n",sum);
return 0;
}
input.c
#include <stdio.h>
#include <stdlib.h>
int input(){
int a;
printf("input num:");
scanf("%d",&a);
return a;
}
calcu.c
#include <stdio.h>
#include <stdlib.h>
int calcu(int a, int b){
int sum;
sum = a+b;
return sum;
}
Ce qui précède est le fichier c qui implémente principalement la fonction, mais le fichier main ne fait pas référence au fichier c lorsqu'il est cité, mais utilise le fichier d'en-tête écrit par eux.
calcu.h
#ifndef _CALCU_H
#define _CALCU_H
int calcu(int a, int b);
#endif
input.h
#ifndef _INPUT_H
#define _INPUT_H
int input();
#endif
Ces deux fichiers d'en-tête sont responsables de la déclaration de la fonction de la fonction, car ces deux fichiers d'en-tête sont introduits dans main.c, mais l'implémentation se trouve dans input.c et calcu.c.
La prochaine étape est le Makefile pour chaque prix demandé.
Makefile_input
CC = gcc
libinput.so:
$(CC) -fPIC -shared input.c -o libinput.so
sudo cp libinput.so /usr/lib
sudo cp libinput.so /usr/bin
clean:
rm -rf libinput.so
Makefile_calcu
CC = gcc
libcalcu.so:
$(CC) -fPIC -shared calcu.c -o libcalcu.so
sudo cp libcalcu.so /usr/lib
sudo cp libcalcu.so /usr/bin
clean:
rm -rf libcalcu.so
La fonction des deux makefiles ci-dessus est de générer une bibliothèque dynamique et de copier la bibliothèque dynamique dans la bibliothèque système.
Ensuite, regardez le fichier qui génère le Makefile final:
CC = gcc
main:
$(CC) main.c \
-I ./input -L ./input -linput \
-I ./calcu -L ./calcu -lcalcu \
-o main
clean:
rm -rf main
Ici, vous devez utiliser les trois paramètres de gcc -I, -L et -l, qui sont respectivement l'emplacement du fichier joint, le dossier de la bibliothèque dynamique et la bibliothèque dynamique.
Pourquoi devrait-on insister sur le fait sudo cp libcalcu.so /usr/lib
qu'avec cette déclaration, parce que la valeur par défaut lorsque nous exécutons, le programme usr/lib
recherchera ce fichier de bibliothèque dynamique, si l'erreur n'existe pas, pourquoi nous devons également spécifier l'heure à exécuter en raison de la nécessité de utilisation.