Super long! Analyse et réponses de 16 questions classiques en langage C (collection)

Source: https://segmentfault.com/a/1190000038292644

 

1. Utilisez la directive de prétraitement #define pour déclarer une constante indiquant le nombre de secondes dans une année (ignorez le problème des années bissextiles)

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365) UL

Je veux voir quelques choses ici:

  • #define les connaissances de base de la grammaire (par exemple: ne peut pas se terminer par un point-virgule, utilisation de parenthèses, etc.)

  • Sachant que le préprocesseur calculera la valeur d'une expression constante pour vous, il est donc plus clair et sans frais d'écrire directement comment vous calculez le nombre de secondes dans une année au lieu de calculer la valeur réelle.

  • Sachez que cette expression débordera de l'entier d'une machine 16 bits, de sorte que le symbole entier long L est utilisé pour indiquer au compilateur que la constante est un entier long.

  • Si vous utilisez UL (pour un entier long non signé) dans vos expressions, vous avez un bon point de départ. N'oubliez pas que la première impression est très importante.

 

2. Ecrivez une macro "standard" MIN. Cette macro entre deux paramètres et renvoie le plus petit.

#define MIN (A, B) ((A) <= (B) (A): (B))

Ce test est conçu aux fins suivantes:

  • Identifie les connaissances de base de l'application #define dans les macros. Ceci est très important, car jusqu'à ce que l'opérateur en ligne devienne une partie du standard C, les macros sont le seul moyen de générer facilement du code embarqué. Pour les systèmes embarqués, afin d'obtenir les performances requises, le code embarqué est souvent nécessaire Méthodes.

  • Connaissance des opérateurs triples conditionnels. La raison pour laquelle cet opérateur existe en langage C est qu'il permet au compilateur de générer du code plus optimisé que if-then-else. Il est important de comprendre cet usage.

  • Savoir mettre soigneusement les paramètres entre parenthèses dans les macros

  • J'utilise également cette question pour commencer à discuter des effets secondaires des macros, par exemple: que se passe-t-il lorsque vous écrivez le code suivant? le moins = MIN (* p ++, b);

 

3. À quoi sert le drapeau du préprocesseur #error?

Si vous ne connaissez pas la réponse, veuillez consulter les références

Cette question est très utile pour distinguer un mec normal d'un nerd. Seuls les nerds peuvent lire l'annexe des manuels de langage C pour trouver les réponses à des questions comme celle-ci. Bien sûr, si vous ne cherchez pas un nerd, le candidat espère mieux ne pas connaître la réponse.

 

4. Les boucles infinies sont souvent utilisées dans les systèmes embarqués Comment écrire des boucles infinies en C?

Il existe plusieurs solutions à ce problème. Mon plan préféré est:

 

Certains programmeurs préfèrent le schéma suivant:

 

Cette implémentation m'a embarrassé car cette grammaire n'exprime pas exactement ce qui se passe. Si un candidat donne ceci comme un plan, j'utiliserai ceci comme une occasion pour explorer la raison de cela. Si leur réponse fondamentale est: «On m'a appris à faire cela, mais je n'ai jamais pensé à pourquoi.» Cela me laisserait une mauvaise impression.

La troisième solution consiste à utiliser goto

 

Si le candidat donne le schéma ci-dessus, cela signifie soit qu'il est un programmeur en langage assembleur (ce qui peut être une bonne chose), soit qu'il est un programmeur BASIC / FORTRAN qui souhaite saisir un nouveau champ.

 

 

5. Utilisez la variable a pour donner la définition suivante

a) Un entier

b) Un pointeur vers un entier

c) Un pointeur vers un pointeur, qui pointe vers un pointeur vers un entier (Un pointeur vers un pointeur vers un entier)

d) Un tableau de 10 entiers (Un tableau de 10 entiers)

e) Un tableau de 10 pointeurs vers des entiers (Un tableau de 10 pointeurs vers des entiers)

f) Un pointeur vers un tableau de 10 entiers

g) Pointeur vers une fonction qui prend un entier comme argument et renvoie un entier. La fonction a un paramètre entier et renvoie un entier

h) Un tableau de dix pointeurs qui pointent vers une fonction qui a un paramètre entier et renvoie un entier (Un tableau de dix pointeurs vers des fonctions qui prennent un argument entier et retournent un entier)

la réponse est:

a) int a; // Un nombre entier

b) int * a; // Un pointeur vers un entier

c) int ** a; // Un pointeur vers un pointeur vers un entier

d) int a [10]; // Un tableau de 10 entiers

e) int * a [10]; // Un tableau de 10 pointeurs vers des entiers

f) int (* a) [10]; // Un pointeur vers un tableau de 10 entiers

g) int (* a) (int); // Un pointeur vers une fonction a qui prend un argument entier et renvoie un entier

h) int (* a [10]) (int); // Un tableau de 10 pointeurs vers des fonctions qui prennent un argument entier et retournent un entier

Les gens affirment souvent qu'il y a ici quelques questions auxquelles on ne peut répondre qu'en retournant le livre, et je suis d'accord avec cette affirmation. Quand j'ai écrit cet article, j'ai vérifié le livre afin de déterminer l'exactitude de la grammaire.

Mais lorsque j'ai été interviewé, je m'attendais à ce qu'on me pose cette question (ou une question similaire). Parce que pendant le temps que j'ai été interviewé, j'étais sûr de connaître la réponse à cette question. Si le candidat ne connaît pas toutes les réponses (ou au moins la plupart des réponses), c'est qu'il ne s'est pas préparé à l'entrevue.Si l'intervieweur n'est pas préparé pour l'entrevue, pourquoi peut-il se préparer?

 

6. À quoi sert le mot-clé static?

Peu de gens peuvent répondre complètement à cette simple question. En langage C, le mot-clé static a trois fonctions évidentes:

  • Dans le corps de la fonction, une variable déclarée statique conserve sa valeur pendant l'appel de la fonction.

  • Dans le module (mais en dehors de la fonction), une variable déclarée statique est accessible par les fonctions utilisées dans le module, mais pas par d'autres fonctions extérieures au module. C'est une variable globale locale.

  • Dans un module, une fonction déclarée statique ne peut être appelée que par d'autres fonctions de ce module. Autrement dit, cette fonction est limitée à la portée locale du module où elle est déclarée.

La plupart des candidats peuvent répondre correctement à la première partie, une partie peut répondre correctement à la deuxième partie et très peu de personnes peuvent comprendre la troisième partie. Il s'agit d'une grave lacune d'un candidat, car il ne comprend évidemment pas les avantages et l'importance des données localisées et des plages de codes.

 

sept. Que signifie le mot-clé const?

Tant que j'entends l'interviewé dire: "const signifie constant", je sais que j'ai affaire à un amateur. L'année dernière, Dan Saks a résumé toutes les utilisations de const dans son article, de sorte que chaque lecteur d'ESP (Traducteur: Programmation de systèmes embarqués) devrait être très familier avec ce que const peut et ne peut pas faire. Si vous n'avez jamais lu cet article, comme tant que vous pouvez dire que const signifie "lecture seule". Bien que cette réponse ne soit pas une réponse complète, je l'accepte comme une réponse correcte. (Si vous souhaitez obtenir une réponse plus détaillée, lisez attentivement l’article de Saks.) Si le candidat peut répondre correctement à cette question, je lui poserai une question supplémentaire: que signifient les déclarations suivantes?

 

Les deux premiers ont le même effet, a est un nombre entier constant. Le troisième signifie que a est un pointeur vers un entier constant (c'est-à-dire qu'un entier n'est pas modifiable, mais un pointeur le peut). La quatrième signification a est un pointeur constant vers un entier (c'est-à-dire que l'entier pointé par le pointeur peut être modifié, mais le pointeur ne l'est pas). Le dernier signifie que a est un pointeur constant vers un entier constant (c'est-à-dire que l'entier pointé par le pointeur n'est pas modifiable et que le pointeur est également non modifiable).

Si le candidat peut répondre correctement à ces questions, il m'a laissé une bonne impression. Au fait, vous pourriez vous demander, même si vous n'utilisez pas le mot-clé const, vous pouvez toujours écrire un programme qui fonctionne correctement, alors pourquoi devrais-je autant valoriser le mot-clé const? J'ai aussi les raisons suivantes:

  • La fonction du mot-clé const est de transmettre des informations très utiles aux personnes qui liront votre code. En fait, déclarer un paramètre comme une constante revient à indiquer à l'utilisateur le but du paramètre. Si vous avez passé beaucoup de temps à nettoyer les déchets laissés par d'autres personnes, vous apprendrez rapidement à remercier ces informations supplémentaires. (Bien sûr, les programmeurs qui savent utiliser const laisseront rarement les déchets à d'autres pour les nettoyer.)

  • En donnant à l'optimiseur des informations supplémentaires, l'utilisation du mot-clé const peut produire un code plus compact.

  • Une utilisation raisonnable du mot-clé const peut amener le compilateur à protéger naturellement les paramètres qui ne veulent pas être modifiés et à les empêcher d'être modifiés par inadvertance. En bref, cela peut réduire l'apparition de bogues.

Il existe une bonne méthode de mémoire, telle que: const char * a en tournant dans le sens des aiguilles d'une montre, vous pouvez voir que const est le plus proche de a, c'est-à-dire que const modifie la constante a.

Si le mot-clé const n'implique pas de pointeurs, nous le comprenons bien, voici le cas des pointeurs:

 

Si vous pouvez distinguer les quatre situations ci-dessus, félicitations, vous avez franchi une étape gratifiante. Je ne sais pas, c'est bon, on peut se référer à la méthode sur Item21 de "Effective c ++", si const est situé à gauche de l'astérisque, alors const est utilisé pour modifier la variable pointée par le pointeur, c'est-à-dire, le pointeur pointe vers une constante, si const est situé sur l'astérisque Sur le côté droit, const est de modifier le pointeur lui-même, c'est-à-dire que le pointeur lui-même est une constante. Par conséquent, [1] et [2] sont identiques, le contenu pointé par le pointeur est constant (const n'a pas de rapport avec la position du déclarateur de variable). Dans ce cas, il n'est pas permis de modifier le contenu, tel que * a = 3; [3] signifie que le pointeur lui-même est constant et que le contenu pointé par le pointeur n'est pas constant. Dans ce cas, le pointeur lui-même ne peut pas être modifié. Par exemple, a ++ est faux; [4] signifie que le pointeur lui-même et le contenu pointé sont tous deux constants.

 

 

8. Quelle est la signification du mot-clé volatile et donnez trois exemples différents.

Une variable définie comme volatile signifie que la variable peut être modifiée de manière inattendue, de sorte que le compilateur n'assumera pas la valeur de cette variable. Pour être précis, l'optimiseur doit soigneusement relire la valeur de cette variable à chaque fois qu'il utilise cette variable, au lieu d'utiliser la sauvegarde stockée dans le registre. Voici quelques exemples de variables volatiles: 1). Registres matériels de périphériques parallèles (tels que les registres d'état)

2). Variables non automatiques auxquelles accéder dans un sous-programme de service d'interruption (variables non automatiques)

3). Variables partagées par plusieurs tâches dans les applications multithread

Les personnes qui ne peuvent pas répondre à cette question ne seront pas embauchées. Je pense que c'est le problème le plus fondamental qui distingue les programmeurs C des programmeurs système embarqués. Les programmeurs système embarqués traitent souvent du matériel, des interruptions, du RTOS, etc., qui nécessitent tous des variables volatiles. Ne pas comprendre le contenu volatil entraînera un désastre.

En supposant que la personne interrogée a correctement répondu à cette question (enfin, je me demande si c'est le cas), je vais creuser un peu plus pour voir si ce gars comprend l'importance de volatile.

1) Un paramètre peut-il être constant ou volatil? Expliquer pourquoi.

2) Un pointeur peut-il être volatil? Expliquer pourquoi.

3). Quel est le problème avec la fonction suivante:

 

Jupe d'apprentissage C / C ++ [7, 12, 2, 84, 705], que vous soyez novice ou avancé, que vous souhaitiez changer de carrière ou vous lancer dans une carrière, vous pouvez venir à comprendre et apprendre ensemble! Il y a des outils de développement dans la jupe, beaucoup de produits secs et des informations techniques à partager!

 

Voici la réponse:

1) Oui. Un exemple est le registre d'état en lecture seule. Il est volatil car il peut être modifié de manière inattendue. Il est constant car le programme ne doit pas essayer de le modifier.

2) Oui. Bien que ce ne soit pas très courant. Un exemple est quand un sous-programme de service intermédiaire modifie un pointeur vers un tampon.

3) Il y a une farce dans ce code. Le but de ce code est de renvoyer le carré de la valeur pointée par le pointeur ptr, mais comme ptr pointe vers un paramètre volatile, le compilateur générera un code similaire à ce qui suit:

 

Comme la valeur de * ptr peut être modifiée de manière inattendue, a et b peuvent être différents. Par conséquent, ce code peut ne pas renvoyer la valeur carrée que vous attendiez! Le code correct est le suivant:

 

 

9. Les systèmes intégrés exigent toujours que les utilisateurs effectuent des opérations sur les bits sur des variables ou des registres. Étant donné une variable entière a, écrivez deux morceaux de code, le premier définit le bit 3 de a et le second efface le bit 3 de a. Dans les deux opérations ci-dessus, gardez les autres bits inchangés.

Il y a trois réponses de base à cette question

1) Je ne sais pas par où commencer. La personne interrogée n'a jamais effectué de travail sur le système embarqué.

2) Utilisez des champs de bits. Les champs de bits sont des choses qui sont jetées dans le coin du langage C. Cela garantit que votre code n'est pas portable entre différents compilateurs, et il garantit également que votre code n'est pas réutilisable. J'ai malheureusement vu récemment un pilote écrit par Infineon pour sa puce de communication plus complexe.Il utilise des champs de bits et est donc totalement inutile pour moi, car mon compilateur utilise d'autres méthodes pour implémenter des champs de bits. Moralement: ne laissez jamais un gars non intégré se tenir sur le côté du matériel réel.

3) Utilisez les #defines et les masques de bits pour fonctionner. Il s'agit d'une méthode hautement portable et doit être utilisée. La meilleure solution est la suivante:

 

 

Certaines personnes aiment définir un masque pour définir et effacer des valeurs et définir des constantes descriptives, ce qui est également acceptable. Je veux voir quelques points importants: expliquer les constantes, les opérations | = et & = ~.

 

10. Les systèmes intégrés nécessitent souvent que les programmeurs accèdent à un emplacement mémoire spécifique. Dans un projet, il est nécessaire de définir la valeur d'une variable entière avec une adresse absolue de 0x67a9 à 0xaa66. Le compilateur est un pur compilateur ANSI. Écrivez du code pour accomplir cette tâche.

Cette question teste si vous savez qu'il est légal de transtyper un entier en pointeur afin d'accéder à une adresse absolue. La manière de résoudre ce problème varie selon les styles personnels. Le code similaire typique est le suivant:

 

Une méthode plus obscure est: * (int * const) (0x67a9) = 0xaa55;

Même si votre goût est plus proche de la deuxième option, je vous suggère d'utiliser la première option lors de l'entretien.

 

11. Les interruptions sont une partie importante des systèmes embarqués, ce qui a conduit de nombreux développeurs de compilateurs à fournir une extension-let standard C supporte les interruptions. Le fait spécifique représenté est qu'un nouveau mot-clé __interrupt a été produit. Le code suivant utilise le mot clé __interrupt pour définir une routine de service d'interruption (ISR). Veuillez commenter ce code.

 

Il y a tellement d'erreurs dans cette fonction que les gens ne savent pas par où commencer:

1). ISR ne peut pas renvoyer de valeur. Si vous ne comprenez pas cela, vous ne serez pas embauché.

2). ISR ne peut pas transmettre de paramètres. Si vous ne voyez pas cela, vos chances d'être embauché sont égales au premier élément.

3) Dans de nombreux processeurs / compilateurs, la virgule flottante n'est généralement pas réentrante. Certains processeurs / compilateurs doivent pousser les registres à l'avant de la pile, et certains processeurs / compilateurs n'autorisent tout simplement pas les opérations en virgule flottante dans ISR. De plus, les ISR doivent être courts et efficaces et il n'est pas judicieux d'effectuer des opérations en virgule flottante dans ISR.

Dans le même esprit que le troisième point, printf () a souvent des problèmes de réentrance et de performances. Si vous perdez les troisième et quatrième points, je ne vous embarrasserai pas trop. Inutile de dire que si vous pouvez obtenir les deux derniers points, vos perspectives d'emploi s'améliorent de plus en plus.

 

 

12. Quelle est la sortie du code suivant et pourquoi?

 

Cette question teste si vous comprenez le principe de la conversion automatique des nombres entiers dans le langage C. J'ai constaté que certains développeurs connaissent très peu ces choses. Quoi qu'il en soit, la réponse à cette question sur les nombres entiers non signés est que le résultat est "> 6". La raison en est que lorsqu'il existe des types signés et non signés dans l'expression, tous les opérandes sont automatiquement convertis en types non signés.

Par conséquent, -20 devient un entier positif très grand, de sorte que le résultat calculé par l'expression est supérieur à 6. Ceci est très important pour les systèmes embarqués où des types de données non signés doivent être fréquemment utilisés. Si vous ne répondez pas correctement à cette question, vous êtes sur le point de ne pas obtenir le poste.

 

13. Évaluez l'extrait de code suivant:

 

Pour un processeur dont le type int n'est pas 16 bits, le code ci-dessus est incorrect. Doit être écrit comme suit: unsigned int compzero = ~ 0;

Cette question révèle vraiment si le candidat comprend l'importance de la longueur des mots du processeur. D'après mon expérience, les bons programmeurs embarqués comprennent très précisément les détails du matériel et ses limites, mais les programmes PC traitent souvent le matériel comme un problème inévitable.

À ce stade, le candidat est soit complètement abattu, soit confiant qu'il va gagner. S'il est évident que le candidat n'est pas très bon, alors le test se termine ici. Mais s'il est évident que le candidat se porte bien, je poserai les questions supplémentaires suivantes. Ces questions sont plus difficiles. Je pense que seuls les très bons candidats peuvent réussir. En posant ces questions, j'espère voir plus de moyens du candidat pour faire face au problème, plutôt que la réponse. Quoi qu'il en soit, vous devriez le traiter comme ce divertissement ...

 

14. Bien qu'ils ne soient pas aussi courants que les ordinateurs non intégrés, les systèmes embarqués ont toujours un processus d'allocation dynamique de mémoire à partir du tas. Alors, quels sont les problèmes possibles d'allocation de mémoire dynamique dans les systèmes embarqués?

Ici, j'espère que les candidats pourront mentionner la fragmentation de la mémoire, les problèmes de garbage collection, le temps de maintien variable, etc. Ce sujet a été largement discuté dans les magazines ESP (principalement PJ Plauger, dont l'explication dépasse de loin toute explication que je peux citer ici), alors revenez sur ces magazines! Après avoir laissé le candidat dans un faux sentiment de sécurité, je suis venu avec un si petit programme: Quelle est la sortie de l'extrait de code suivant et pourquoi?

 

C'est une question intéressante. Récemment, un de mes collègues a passé par inadvertance une valeur de 0 à la fonction malloc et a obtenu un pointeur valide.Je n'ai pensé qu'à ce problème. C'est le code ci-dessus, la sortie du code est "Got a valid pointer". J'utilise ceci pour commencer à discuter d'un tel problème, pour voir si la personne interrogée pense que la routine de la bibliothèque est correcte. Obtenir la bonne réponse est important, mais la méthode de résolution du problème et la justification de votre décision sont plus importantes.

 

15. Typedef est fréquemment utilisé dans le langage C pour déclarer un synonyme d'un type de données existant. Vous pouvez également utiliser le préprocesseur pour faire des choses similaires. Par exemple, considérons l'exemple suivant:

 

L'intention des deux cas ci-dessus est de définir dPS et tPS comme un pointeur vers la structure s. Quelle méthode est la meilleure? (Le cas échéant) Pourquoi? C'est une question très délicate, et quiconque répond correctement à cette question (pour une bonne raison) doit être félicité. La réponse est: typedef est meilleur. Prenons l'exemple suivant:

 

La première extension est: struct s * p1, p2; Le code ci-dessus définit p1 comme un pointeur vers une structure et p2 comme une structure réelle, ce qui peut ne pas être ce que vous voulez. Le deuxième exemple définit correctement les deux pointeurs p3 et p4.

 

16. Le langage C accepte certaines structures choquantes. La structure suivante est-elle légale et que fait-elle?

 

Cette question servira de fin heureuse à ce quiz. Croyez-le ou non, l'exemple ci-dessus est complètement grammatical. La question est de savoir comment le compilateur le gère-t-il? Les auteurs de compilateurs de bas niveau discuteront réellement de ce problème. Selon le principe de meilleure gestion, le compilateur doit être capable de gérer tous les usages légaux possibles. Par conséquent, le code ci-dessus est traité comme suit: c = a ++ + b; par conséquent, a = 6, b = 7, c = 12 après que ce code soit maintenu. Si vous connaissez la réponse ou devinez la bonne réponse, faites-le bien. Si vous ne connaissez pas la réponse, je ne considère pas cela comme une question. J'ai trouvé que le plus grand avantage de cette question est: C'est un bon sujet sur le style d'écriture de code, la lisibilité du code et la modification du code.

 

Si vous souhaitez également apprendre la programmation, recevez des conseils complets et systématiques. Voici une base d'apprentissage recommandée pour vous. Que ce soit Xiaobai ou une personne avancée, ils peuvent grandir ici. Après avoir rejoint le groupe, vous pouvez contacter l'administrateur pour recevoir le package d'apprentissage novice, cliquez sur moi pour entrer dans la base d'apprentissage

Je suppose que tu aimes

Origine blog.csdn.net/miaozenn/article/details/112298502
conseillé
Classement