ProgrammationDéveloppeur C embarqué

Parlez-moi du mécanisme de fonctionnement de l'opérateur return dans le langage C. Quels sont les détails de sa syntaxe et de sa sémantique, comment retourner correctement des valeurs d'une fonction, quelles sont les différences entre return sans valeur et avec une expression, et quels pièges sont associés aux structures, pointeurs et variables locales retournés ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Historique de la question

L'opérateur return est apparu dans C pour terminer explicitement l'exécution d'une fonction et transmettre le résultat au code appelant. Dans les premiers langages de programmation, il n'était pas toujours possible de retourner des valeurs, et le mécanisme return a permis d'indiquer clairement le résultat des calculs. Cela a amélioré l'expressivité et la sécurité des programmes.

Problème

La tâche principale : terminer correctement la fonction et, si nécessaire, retourner une valeur correspondant à un type spécifique. Les erreurs se produisent souvent en raison d'un retour de valeur de type incorrect, de pointeurs vers des variables non existantes ou locales, ou d'une ignorance de la valeur retournée par la partie appelante.

Solution

  • return; s'applique uniquement aux fonctions de type void (ne retournent rien).
  • return expression; est utilisé pour les fonctions avec un type non-void et termine la fonction en retournant la valeur spécifiée.
  • Le type de valeur retournée doit correspondre exactement au prototype de la fonction déclaré.
  • Lors du retour de structures, une copie de la structure est retournée. Lors du retour d'un pointeur, seule l'adresse est copiée.
  • Il est dangereux de retourner des pointeurs vers des variables locales (elles sont détruites lors de la sortie de la fonction).

Exemple de code :

#include <stdio.h> struct Point { int x, y; }; struct Point make_point(int x, int y) { // retour d'une structure (copie) struct Point p = {x, y}; return p; } int* dangerous() { int num = 42; return &num; // dangereux : retour d'une adresse de variable locale ! } void do_nothing() { return; // correct pour les fonctions de type void } int main() { struct Point p = make_point(3, 4); printf("%d %d\n", p.x, p.y); int* ptr = dangerous(); // UB : ptr pointe vers la zone écrasée }

Caractéristiques clés :

  • return termine immédiatement l'exécution de la fonction
  • le type de valeur retournée doit correspondre à celui déclaré
  • lors du retour de structures/objets, une copie est effectuée, et non un retour de référence

Questions pièges.

Peut-on utiliser return dans les fonctions sans valeur (void) ?

Réponse : Oui, on peut écrire "return;" pour les fonctions void, mais on ne peut pas spécifier une expression (return x;) pour une fonction void.

Que se passe-t-il lors du retour d'un tableau d'une fonction ?

Réponse : En C, il est impossible de retourner un tableau directement. On ne peut retourner qu'un pointeur (par exemple, vers un tableau statique), mais il est préférable de retourner un pointeur et sa taille ou d'utiliser un tableau alloué dynamiquement.

int* make_arr() { static int arr[5] = {1,2,3,4,5}; return arr; // le tableau statique vit après la sortie de la fonction }

Pourquoi est-il dangereux de retourner un pointeur vers une variable locale ?

Réponse : Après la sortie de la fonction, la mémoire de la variable locale est libérée (zone de pile). L'utilisation du pointeur retourné conduit à un comportement indéfini.

Erreurs typiques et anti-patrons

  • Retourner un pointeur vers une variable située sur la pile
  • Non-concordance entre le type de l'expression retournée et le type de la fonction
  • Oublier la voie de return dans des fonctions déclarées retournant une valeur

Exemple de la vie réelle

Cas négatif

La fonction retourne un pointeur vers une variable locale, l'appelant reçoit "des déchets", un comportement imprévisible et des bugs aléatoires.

Avantages :

  • Mise en œuvre rapide

Inconvénients :

  • Plantages aléatoires, les données sont corrompues lors de tout changement de la pile après la sortie de la fonction

Cas positif

Utilisation d'une structure retournée (copiée par valeur) ou retour d'un pointeur vers une mémoire statique/dynamique :

Avantages :

  • Comportement prévisible
  • Pas de pointeurs "pendants"

Inconvénients :

  • Parfois coûteux (copie de grandes structures), ou nécessité de se souvenir de libérer explicitement la mémoire