Questions d'entrevue intégrées (2)

Contenu provenant d'Internet, collationné en 2020

 

1. La sortie du code suivant est:

annuler le changement ( int * a, int & b, int c) 
{ 
    c = * a; 
    b = 30 ;
    * a = 20 ; 
} 
int main () 
{ 
    int a = 10 , b = 20 , c = 30 ; 
    changement ( & a , b, c); 
    printf (« % d,% d,% d», a, b, c);
    retourner  0 ; 
}

Réponse: 20, 30, 30

2. Les ports par défaut du service FTP et du service SMTP sont ()

Réponse: 20, 21; 25

3. Lorsque la table linéaire (a1, a2, ..., an) est stockée de manière liée, la complexité temporelle de l'accès au i-ème élément de position est ()

Réponse: O (n)

4. Quelle est la taille calculée avec sizeof (struct A) sous le compilateur 64 bits?

struct A {
     long a1;
    court a2;
    int a3;
    int * a4; 
};

Réponse: l'alignement sur 4 octets est égal à 16

L'alignement sur 8 octets est de 24

5. Effectuez une recherche binaire sur un tableau ordonné avec 20 éléments. L'index de départ du tableau est 1 et l'index de la séquence de comparaison de A [2] est ()

Réponse: 10, 5, 3, 2

6. Sur un arbre B d'ordre 10, le nombre de mots-clés dans chaque nœud racine peut être () au plus, et au moins ().

Réponse: 9; 4

7. Le système d'exploitation utilise la technologie de mise en mémoire tampon pour augmenter l'utilisation des ressources en réduisant le nombre de cycles CPU.

Réponse: interrompre

8. Quels sont les modes de déclenchement de l'oscilloscope?

Réponse: Il y a trois modes de déclenchement: déclenchement automatique, normal et simple.

 

9. La taille en octets des types de base courants

Système d'exploitation 32 bits

char: 1 octet (fixe)

* (À savoir variable pointeur): 4 octets (l'espace d'adressage de la machine 32 bits est de 4 octets)

short int: 2 octets (fixe)

int: 4 octets (fixe)

entier non signé: 4 octets (fixe)

float: 4 octets (fixe)

double: 8 octets (fixe)

long: 4 octets

non signé long: 4 octets (variation *, en fait, la valeur de la longueur d'adresse du contrôle d'adressage)

long long: 8 octets (fixe)

 

10. Pointeur vers une chaîne constante, pointeur constant vers une chaîne (const)

const char * p = "hello"; // pointe sur "string constant"

p [0] = 'X'; // Erreur! Vous souhaitez modifier le premier caractère de la chaîne. Mais la constante ne permet pas de modification

p = p2; // Correct! Laissez p pointer vers un autre pointeur.

char * const p = "bonjour"; // "pointeur const vers chaîne"

p [0] = 'X'; // Correct! Autorise la modification de la chaîne, car la chaîne n'est pas une constante

p = p2; // Erreur! Le pointeur est une constante, il n'est pas autorisé de modifier le pointeur de p

char const * est identique à const char *. La position de const est la même à gauche ou à droite de char.

La constante d'un pointeur constant doit être écrite à droite de l'astérisque *.

L'écriture d'un pointeur constant sur une chaîne constante est const char * const p = "xx"; 2 const

Cette analyse est meilleure que l'article précédent.

 

11. Le problème de typedef & #define

Il existe deux façons de définir le type de données pStr. Quelle est la différence entre les deux? Laquelle est la meilleure?

typedef char * pStr;

#define pStr char *;

Analyse: De manière générale, typedef est meilleur que #define, surtout quand il y a des pointeurs. Veuillez voir des exemples:

typedef char * pStr1;

#define pStr2 char *

pStr1 s1, s2;

pStr2 s3, s4;

Dans la définition de variable ci-dessus, s1, s2 et s3 sont tous définis comme char *, tandis que s4 est défini comme char, pas la variable pointeur que nous attendions

La raison fondamentale est que #define est simplement un remplacement de chaîne et typedef est de donner un nouveau nom à un type. Dans l'exemple ci-dessus, l'instruction define doit être écrite comme pStr2 s3, * s4; cela peut être exécuté normalement.

 

12. Le problème de const

(1) des constantes const peuvent être définies, avec immuabilité.

Par exemple:

const  int Max = 100 ; int Array [Max];

(2) Il est pratique pour la vérification de type, afin que le compilateur comprenne mieux le contenu du traitement, éliminant ainsi certains dangers cachés.

Par exemple:

 void f ( const  int i) {.........} // Le compilateur saura que i est une constante et ne permet pas de modification;

(3) L'apparition de nombres ambigus peut être évitée, et l'ajustement et la modification des paramètres peuvent également être facilement effectués.

Comme dans (1), si vous voulez modifier le contenu de Max, il vous suffit de: const int Max = vous voulez;

(4) Il peut protéger les choses modifiées, empêcher les modifications accidentelles et améliorer la robustesse du programme. Toujours l'exemple ci-dessus, si i est modifié dans le corps de la fonction, le compilateur signalera une erreur;

Par exemple:

 vide f ( const  int i) {i = 10 ; // erreur! }

(5) Il peut économiser de l'espace et éviter une allocation de mémoire inutile. Par exemple:

#define PI 3,14159 // macro constante 
const  Double Pi = 3,14159 ; // cette fois dans la RAM n'est pas le Pi
double i = Pi; // alloue de la mémoire pour Pi à ce moment, plus alloué à l'avenir! 
double I = PI; // Remplacement de macro lors de la compilation, allocation de mémoire
double j = Pi; // Pas d'allocation de mémoire 
double J = PI; // Remplacez la macro et allouez à nouveau de la mémoire!

const définit les constantes du point de vue de l'assemblage, mais ne donne que l'adresse mémoire correspondante, au lieu de donner des données immédiates comme #define,

Par conséquent, les constantes définies par const n'ont qu'une seule copie pendant l'exécution du programme, tandis que les constantes définies par #define ont plusieurs copies en mémoire.

(6) Améliorez l'efficacité.

Le compilateur n'alloue généralement pas d'espace de stockage aux constantes constantes ordinaires, mais les enregistre dans la table des symboles, ce qui en fait une constante pendant la compilation, sans opérations de stockage et de lecture de la mémoire, ce qui le rend très efficace.

 

13. La différence entre sizeof et strlen:

char str [ 20 ] = " 0123456789 " ; 

int a = strlen (str); // a = 10; strlen Calculez la longueur de la chaîne, avec \ 0 'comme fin de la chaîne. 

int b = sizeof (str); // b = 20; sizeof calcule la taille de l'espace mémoire occupé par le tableau alloué str [20], qui n'est pas affecté par le contenu qui y est stocké.

 

Ce qui précède est le résultat du traitement du tableau statique. S'il s'agit du pointeur, le résultat est différent.

char * ss = " 0123456789 " ; // sizeof (ss) résultat 4

 

ss est un pointeur de caractère pointant vers une constante de chaîne, sizeof est un pointeur pour occuper l'espace, il doit être un entier long, il vaut donc 4

sizeof (* ss) // Résultat 1; * ss est le premier caractère est en fait l'espace mémoire occupé par le premier bit '0' de la chaîne, qui est du type char et occupe 1 bit

 

strlen (ss) = 10  // Si vous voulez obtenir la longueur de cette chaîne, vous devez utiliser strlen

 

La structure Sizeof est l'espace total des types de données définis dans la structure (notez l'alignement des octets).

Sizeof (union) est la taille du plus grand type de données du type de données défini dans union.

 

14, auto, registre, analyse statique

Auto est l'attribut par défaut des variables locales dans le langage C. Le compilateur par défaut est que toutes les variables locales sont auto, et les variables définies se voient allouer de la mémoire sur la pile .

Le mot-clé statique indique l'attribut "statique" de la variable et a également la signification de "qualificateur de portée". Les variables locales modifiées sont stockées dans la zone statique du programme . Une autre signification de statique est l'identificateur de portée de fichier.

La portée de la variable globale modifiée statique est uniquement dans le fichier déclaré, la portée de la fonction modifiée statique est uniquement dans le fichier déclaré

Le mot-clé de registre indique que la variable est stockée dans le registre. Le registre ne demande que la variable de registre, mais la demande n'est pas nécessairement réussie. La variable de registre doit être une valeur que le registre CPU peut accepter,

Vous ne pouvez pas utiliser l'opérateur & pour obtenir l'adresse d'une variable de registre, ce qui présente l'avantage d'être rapide à traiter.

 

15, const, volatile modifier les variables en même temps

( 1 ) "Le compilateur n'alloue généralement pas de mémoire pour les variables const, mais l'enregistre dans la table des symboles, ce qui en fait une valeur pendant la compilation, sans opérations de stockage et de lecture de mémoire." 
( 2 ) Le rôle de volatile C'est "pour dire au compilateur que je peux changer à tout moment, et la valeur de i doit être récupérée de la mémoire chaque fois qu'elle est utilisée."

 

16, const, sens volatile

( 1 ) Le sens de const est "veuillez l'utiliser comme une constante", et non "rassurez-vous, ce doit être une constante". 
( 2 ) Le sens de volatile est "s'il vous plaît ne faites pas d'optimisation auto-justifiée, cette valeur peut changer", pas "vous pouvez modifier cette valeur".

 

17. Le rôle et le stade de const, volatile

(1) const n'est utile qu'au moment de la compilation, inutile au moment de l'exécution

const garantit qu'il n'y a pas de place pour modifier ses variables modifiées dans le "code source" C au moment de la compilation (s'il y a une erreur, la compilation échoue), et si la valeur de la variable est modifiée au moment de l'exécution n'est pas soumise à const Restrictions.

(2) Volatile est utile au moment de la compilation et de l'exécution

Dites au compilateur au moment de la compilation: Veuillez ne pas faire d'optimisations auto-justes, la valeur de cette variable peut changer;

Pendant l'exécution: chaque fois que la valeur de la variable est utilisée, la valeur de la variable est extraite de la mémoire.

Supplément: période de compilation - le processus par lequel le compilateur C convertit le code source en assembleur puis en code machine; runtime - le processus d'exécution du code machine dans la CPU.

 

18. const, volatile modifie une variable en même temps

(1) Légalité

La signification de "volatile" n'est pas "non-const", volatile et const ne constituent pas des antonymes, vous pouvez donc les assembler pour modifier une variable.

(2) Modifier la signification d'une variable en même temps

Indique qu'une variable ne peut pas être modifiée et ne peut pas être optimisée pendant la compilation du programme; la valeur de la variable peut être modifiée pendant l'exécution du programme, mais la valeur de la variable doit être lue dans la mémoire chaque fois qu'elle est utilisée pour éviter des erreurs inattendues.

 

19. Pile, tas, zone de stockage statique

Pile: utilisation des appels de fonction principale

La pile est utilisée de l'adresse haute à l'adresse basse et le tas est dans la direction opposée.

Dans un appel de fonction, la pile sera poussée dans l'ordre: paramètres, adresse de retour, EBP. Si la fonction a des variables locales, ouvrez ensuite l'espace correspondant sur la pile pour construire les variables.

Dans un programme en langage C, l'ordre des paramètres d'empilement est inversé. Par exemple, func (a, b, c). Lorsque les paramètres sont poussés dans la pile, c'est: appuyez d'abord sur c, puis appuyez sur b, et enfin a. Lors de la récupération des paramètres, en raison du premier entré, premier sorti de la pile, prenez d'abord a en haut de la pile, puis prenez b, et enfin prenez c.

Tas: allocation dynamique de la mémoire principale

Méthode de liste inactive, méthode bitmap, méthode de pool d'objets, etc.

Int * p = ( int *) malloc ( taille de ( int ));

 

Zone de stockage statique: enregistrez les variables globales et les variables statiques

La zone de stockage statique du programme alloue de l'espace pendant l'exécution du programme jusqu'à la fin du programme. La taille de la zone de stockage statique a été déterminée lors de la compilation du programme. La zone de stockage statique du programme est principalement utilisée pour enregistrer des variables globales et des variables statiques dans le programme. La pile et le tas sont différents, et les informations dans la zone de stockage statique seront éventuellement enregistrées dans le programme exécutable.

Point de connaissance: le segment de pile n'existe officiellement qu'après l'exécution du programme, qui est la base du programme.

1. La fonction est placée dans la section de code: section .Test. La section .text stocke le code exécutable dans le programme
 2. Les variables globales et les variables statiques avec des valeurs initiales sont dans la section data: la section .data. Enregistrer .data section sont ceux qui ont été initialisés variables globales et les variables statiques
 3 . Sans la valeur initiale des variables globales et les variables statiques dans .bss. La section .bss stocke les variables globales et les variables statiques non initialisées. La section 
rodata (lecture seule) stocke les valeurs constantes dans le programme, telles que les constantes de chaîne

 

20. Les deux sont des variables globales et des variables statiques. Pourquoi les variables initialisées et non initialisées sont-elles stockées dans différents segments?

Réponse: Afin de simplifier le code de démarrage, l'éditeur de liens du compilateur placera les variables initialisées dans le même segment: .data, l'image de ce segment (y compris la valeur initiale de chaque variable) est enregistrée dans le "segment de données en lecture seule" Le code de démarrage peut simplement copier cette image dans la section .data et toutes les variables initialisées sont initialisées. Les variables non initialisées sont également placées dans la même section: .bss. Le code de démarrage appelle simplement memset pour effacer toutes les variables non initialisées.

void * memset ( void * s, int ch, size_t n); 

Explication de la fonction: remplacer n octets (typedef unsigned int size_t) derrière la position actuelle dans s avec ch et retourner s. 

memset: le rôle est de remplir une valeur donnée dans un bloc de mémoire, c'est le moyen le plus rapide d'effacer une grande structure ou un tableau

21. Pointeur sauvage

Causes:

1. La variable de pointeur local n'est pas initialisée

2. Utilisez le pointeur qui a été libéré

3. La variable pointée par le pointeur est détruite avant le pointeur

Mesures d'évitement:

A. Après avoir demandé de la mémoire avec malloc, vous devez immédiatement vérifier si la valeur du pointeur est NULL pour empêcher l'utilisation d'un pointeur NULL:

B. Gardez à l'esprit la longueur du tableau pour empêcher son fonctionnement hors limites, pensez à utiliser un tableau flexible

C. L'opération d'application dynamique doit correspondre à l'opération de publication pour éviter les fuites de mémoire et les versions multiples

D. Le pointeur libre doit recevoir immédiatement une valeur NULL

22, pointeur de type vide

Le pointeur a deux propriétés: l'adresse et la longueur de la variable / objet, mais le pointeur ne stocke que l'adresse et la longueur dépend du type de pointeur;

Le compilateur adresse vers l'arrière à partir de l'adresse pointée par le pointeur en fonction du type de pointeur, et la plage d'adressage est différente pour différents types de pointeurs, tels que:

int * recherche 4 octets comme unité de stockage variable en arrière à partir de l'adresse spécifiée

double * recherche 8 octets comme unité de stockage variable en arrière à partir de l'adresse spécifiée

void est "non typé" et void * est "pointeur non typé", qui peut pointer vers n'importe quel type de données.

Un pointeur vide peut pointer vers n'importe quel type de données, c'est-à-dire qu'un pointeur de n'importe quel type de données peut être utilisé pour attribuer une valeur à un pointeur vide.

Par exemple

int * pint; 

void * pvoid; // Il n'a pas de type, ou ce type ne peut pas déterminer la longueur de l'objet pointé 

pvoid = pint; // Obtient uniquement l'adresse variable / objet et non la taille, mais ne peut pas pint = pvoid;

 

Si vous souhaitez affecter pvoid à d'autres types de pointeurs, vous devez forcer la conversion de type telle que:

pint = ( int *) pvoid; // Le type de conversion est d'obtenir la taille de la variable / objet pointé

 

Le pointeur vide ne peut pas être re-référencé (c'est-à-dire, prendre le sens du contenu)

* pvoid // error 

Si vous voulez re-référencer un pointeur, ou utiliser l' opérateur 
" -> " pour re-référencer une pièce, vous devez avoir une règle d'interprétation pour la mémoire pointée par le pointeur. 
Par exemple, int * p; 

Ensuite, lorsque vous copiez p plus tard, le compilateur traitera les quatre octets à partir de l'adresse pointée par p comme un complément entier. 

Étant donné que le pointeur void ne connaît que l'adresse de début de la variable / objet et ne connaît pas la taille de la


 variable / objet (quelques octets), il ne peut pas être correctement référencé. 
Dans la programmation actuelle, afin de répondre à la norme ANSI et d'améliorer la portabilité du programme, nous pouvons écrire du code qui réalise la même fonction comme ceci: void * pvoid; 

( char *) pvoid ++; // ANSI: correct; GNU: correct 

( char *) pvoid + = 1 ; // ANSI: faux; GNU: correct

 

Je suppose que tu aimes

Origine www.cnblogs.com/kunx/p/12753161.html
conseillé
Classement