c Questions de test écrit courantes avancées

c Questions de test écrit courantes avancées

  1. Quelle est la différence entre l'énumération et la macro #define?

Vue d'ensemble de l'énumération et des macros
(1) Énumération: fait référence à l'énumération des valeurs des variables une par une, et la valeur de la variable est limitée à la plage des valeurs répertoriées
(2) #define la définition de la macro consiste à utiliser un identifiant spécifié Représente une chaîne

La différence entre l'énumération et la macro
#define (1) Les variables d'énumération peuvent être déboguées dans le compilateur, mais les constantes de macro ne peuvent pas être déboguées
(2) Les constantes de macro #define sont simplement remplacées à l'étape de pré-compilation. Les constantes d'énumération sont déterminées au moment de la compilation.
(3) Les énumérations peuvent définir un grand nombre de constantes liées à la fois et la macro #define ne peut en définir qu'une à la fois

  1. Combien de mémoire occupe la structure vide?

La taille de la mémoire
occupée par la structure est la somme de la mémoire occupée par ses membres. La mémoire occupée par une structure vide est de 1 octet.
Étant donné que le compilateur pense que tout type de données a sa propre taille, elle ne peut être
allouée que lorsqu'une variable est définie avec ce type. La taille correcte de l'espace mémoire, char occupe la plus petite mémoire de tous les types de données, soit 1 octet.

  1. Avantages des types énumérés

(1) Une constante d'énumération équivaut à une constante symbolique, elle a donc l'avantage d'être connue par son nom et peut augmenter la lisibilité du programme.
(2) La plage de valeurs de la variable de type d'énumération est limitée à la plage de constantes d'énumération répertoriée. Si la valeur n'est pas dans la plage répertoriée, le système la traitera comme une erreur, ce qui peut aider le système à vérifier l'erreur et réduire la difficulté de compréhension du programme.
(3) Le type d'énumération facilite également le système pour effectuer une vérification de type sur les variables d'énumération, améliorant ainsi la sécurité

  1. Quelles sont les similitudes et les différences entre les macros typedef et #define?

(1) La similitude
peut généralement être comprise comme un alias pour un caractère, et un nouveau caractère est utilisé pour remplacer le caractère original dans le programme.
(2) Différence
a. La signification de fond est différente. #Define est un remplacement de chaîne et typedef est Le type a un alias
. B. Il n'y a pas de point-virgule à la fin de la définition de la macro comme signe de fin, et cela n'est pas fait dans le processus de compilation, mais a été terminé dans le processus de prétraitement. Il est difficile de trouver des erreurs potentielles

#define INT int* 
typedef int* my_intp;

INT i, i_p;  ==>    int* i, i_p; 
my_intp a, ap;
  1. Quel est l'effet des modes grand et petit boutiste sur les données de type union

    Mode
    grand-boutiste (1) Stockage en mode big-endian: les données de haut niveau sont stockées dans l'adresse basse
    (2) Stockage en mode petit-boutiste: les données de haut niveau sont stockées dans l'adresse haute L'effet du
    mode grand-boutiste sur les données de type union
    Mémoire occupée par les données de type Union Égal à la mémoire occupée par son membre le plus grand, les accès aux membres de type union commencent à un décalage de 0 par rapport à l'adresse de base de l'union, ce qui signifie que l'accès à l'union quelle que soit la variable accédée Tous commencent à partir de la première adresse du syndicat, de sorte que le stockage du mode grand et petit affectera directement la valeur du membre du syndicat.

  2. Comment prouver que tous les membres de la variable union partagent une unité de mémoire?

Déclarez qu'un type union a deux variables membres, l'une est un tableau de type int et l'autre est un tableau de type char. Attribuez d'abord une valeur en fonction de l'un des membres du tableau de type int, puis la sortie en fonction de l'autre membre du tableau de type char. Si le tableau d'entiers et le tableau de caractères partagent la même mémoire, le contenu de sortie et le contenu écrit doivent être cohérents, sinon les résultats sont incohérents.

  1. Problème d'alignement d'octets

Qu'est-ce que l'alignement d'octets?

L'unité de base de la taille de la mémoire d'un ordinateur est l'octet. Théoriquement, un certain type de données de base est accessible à partir de n'importe quelle adresse, mais en fait, l'ordinateur ne lit et n'écrit pas la mémoire octet par octet, mais utilise 2, 4, Ou des blocs d'octets multiples de 8 pour lire et écrire dans la mémoire, de sorte qu'il y aura des restrictions sur l'adresse légale du type de données de base, c'est-à-dire que son adresse doit être un multiple de 2, 4 ou 8. Ensuite, divers types de données doivent être organisés dans l'espace selon certaines règles, à savoir l'alignement. === "Habituellement, les systèmes 32 bits ou MCU sont alignés sur 4 octets.

Quelles sont les directives d'alignement?

  • La première adresse de la variable de structure peut être divisée uniformément par sa taille d'octet d'alignement.
  • Le décalage de chaque membre de la structure par rapport à la première adresse de la structure est un multiple entier de la taille du membre. S'il n'est pas satisfait, le membre précédent est rempli d'octets pour le satisfaire.
  • La taille totale de la structure est un multiple entier de la taille des octets d'alignement de la structure. Si elle n'est pas satisfaite, les octets sont finalement remplis pour la satisfaire.
#include<stdio.h>
#include<stdint.h>
struct test
{
    
    
    int a;
    char b;
    int c;
    short d;
};
int main(int argc,char *argv)
{
    
    
    printf("the size of struct test is %d\n",sizeof(struct test));
    return 0;
}

résultat de l'opération:

the size of struct test is 16

Une autre méthode de définition

#include<stdio.h>
#include<stdint.h>
struct test
{
    
    
    int a;
    int c;
    char b;
    short d;
};
int main(int argc,char *argv)
{
    
    
    printf("the size of struct test is %d\n",sizeof(struct test));
    return 0;
}

résultat de l'opération:

the size of struct test is 12

Conclusion: Lors de la conception de la structure, un réglage raisonnable de la position des membres peut considérablement économiser de l'espace de stockage

Pourquoi aligné sur les octets?

Améliorez les performances du système de mémoire

Programmation multiplateforme

Vous pouvez utiliser #pragma pack (n) pour aligner les structures sur un octet

#pragma pack(1) /*1字节对齐*/
struct test
{
    int a;
    char b;
    int c;
    short d;
};

Pour résumer:

  • Les membres de la structure sont raisonnablement disposés pour économiser de l'espace
  • La structure de données multiplateforme peut prendre en compte un alignement sur 1 octet, ce qui économise de l'espace mais affecte l'efficacité de l'accès
  • La structure de données multiplateforme est remplie artificiellement d'octets pour améliorer l'efficacité de l'accès mais n'économise pas d'espace
  • Les données locales adoptent l'alignement par défaut pour améliorer l'efficacité de l'accès
  1. Dans le makefile suivant, quel est le résultat d'impression du terminal après l'exécution de make
all:cd ef
	@echo 123
cd:
	@echo 456
ef:
	@echo 789

Réponse:

456
789
123
  1. Dans le makefile suivant, quel est le résultat d'impression du terminal après l'exécution de make

    Makefile

    x := foo
    y := $(x)b
    x := new  
    
    .PHONY : test
    
    test :
    	@echo "x => $(x)"
    	@echo "y => $(y)"
    
    
    

    résultat:

    x => new
    y => foob
    
    

    Makefile

    x = foo
    y = $(x)b
    x = new
    
    a = $(b)
    b = $(c)
    c = hello-makefile  
    
    .PHONY : test
    
    test :
    	@echo "x => $(x)"
    	@echo "y => $(y)"
    	@echo "a => $(a)"
    	@echo "b => $(b)"
    	@echo "c => $(c)"
    
    
    

    résultat:

    x => new
    y => newb
    a => hello-makefile
    b => hello-makefile
    c => hello-makefile
    
    
    

    Makefile

    x := foo
    y := $(x)b
    x ?= new   
    
    .PHONY : test
    
    test :
    	@echo "x => $(x)"
    	@echo "y => $(y)"
    
    
    
    

    résultat:

    x => foo
    y => foob
    
    
    

    Makefile

    x := foo
    y := $(x)b
    x += new  
    
    .PHONY : test
    
    test :
    	@echo "x => $(x)"
    	@echo "y => $(y)"
    
    
    

    résultat:

    x => foo new
    y => foob
    
    
    

    Pour résumer:

    Affectation simple (: =)

    Affectation récursive (=)

    Affectation conditionnelle (? =)

    Ajouter l'attribution (+ =)

  2. Dans le Makefile suivant, le résultat d'affichage du terminal après l'exécution de make est

    Makefile

    .PHONY : all first second third 
      
    all : first second third
    	@echo "\$$@ => $@"
    	@echo "$$^ => $^"
    	@echo "$$< => $<"
    
    
    

    résultat:

    $@ => all
    $^ => first second third
    $< => first
    
    
    

    Pour résumer:

    & @ La cible qui déclenche l'exécution de la commande dans la règle courante

    & ^ Toutes les dépendances dans la règle actuelle

    & <La première dépendance dans la règle actuelle

    application:

    CC := gcc
    TARGET := hello-world.out
    
    $(TARGET) : func.o main.o
    	$(CC) -o $@ $^
    
    func.o : func.c
    	$(CC) -o $@ -c $^
    
    main.o : main.c
    	$(CC) -o $@ -c $^
    
    .PHONY : rebuild clean all
    
    rebuild : clean all
    
    
    all : $(TARGET)
    
    clean :
    	rm *.o $(TARGET)
    
    	
    
    

Je suppose que tu aimes

Origine blog.csdn.net/weixin_48430195/article/details/108737875
conseillé
Classement