c99 goto initial initial

Lors du débogage d’un crash, j’ai rencontré ce problème dans un code:

int func() { char *p1 = malloc(...); if (p1 == NULL) goto err_exit; char *p2 = malloc(...); if (p2 == NULL) goto err_exit; ... err_exit: free(p2); free(p1); return -1; } 

Le problème se produit lorsque le premier malloc échoue. Parce que nous sautons sur l’initialisation de p2 , il contient des données aléatoires et l’appel à free(p2) peut se bloquer.

J’espérerais / espérer que cela serait traité de la même manière qu’en C ++ où le compilateur ne permet pas à un goto de sauter une initialisation.

Ma question: est-ce que sauter par-dessus une initialisation autorisée par le standard ou est-ce un bug dans l’implémentation de c99 par gcc?

Vous pouvez demander à gcc de vous avertir lorsque vous sautez une définition de variable en utilisant -Wjump-misses-init puis vous pouvez utiliser -Werror (ou, plus précisément, -Werror=jump-misses-init ) pour forcer les utilisateurs à avec ça. Cet avertissement est inclus dans -Wc++-compat afin que les développeurs de gcc sachent que le code se comporte différemment en C par rapport à C ++.

Vous pouvez également modifier légèrement le code:

 int func() { char *p1 = malloc(...); if (p1 == NULL) goto err_exit_1; char *p2 = malloc(...); if (p2 == NULL) goto err_exit_2; ... err_exit_2: free(p2); err_exit_1: free(p1); return -1; } 

… et continuez simplement à jumeler les étiquettes avec les variables initialisées. Vous aurez le même problème en appelant de nombreuses autres fonctions avec des variables unitarisées. Free se trouve être un problème plus évident.

Un saut comme celui-là est en effet autorisé par la norme, donc ce n’est pas un bug dans GCC. La norme répertorie cette situation à titre d’avertissement suggéré à l’annexe I.

La seule ressortingction imposée aux sauts dans C99 en ce qui concerne la scope est qu’il est illégal de sauter dans la scope d’une variable de type modifié de façon variable, comme un VLA.

 int main() { int n = 5; goto label; // <- ERROR: illegal jump int a[n]; label:; } 

En d'autres termes, il n'est pas correct de dire qu'un "saut est juste un saut en C". Les sauts sont quelque peu restreints quand il s'agit d'entrer dans la scope d'une variable, bien que cela ne soit pas aussi ssortingct qu'en C ++. La situation que vous décrivez ne fait pas partie des situations restreintes.

Ce n’est pas un bug dans gcc. Un saut est juste un saut en C. Aucune logique particulière n’est appliquée. Le problème est que vous n’initialisez pas vos pointeurs sur NULL premier. Si vous deviez le faire, alors votre appel gratuit serait free(NULL) ce qui ne ferait pas planter. Démarrer la fonction avec char *p1 = NULL, *p2 = NULL; et tout ira bien.

Hmm, ce n’est pas parce que la nouvelle norme autorise les déclarations de variable n’importe où que c’est toujours une bonne idée de l’utiliser. Dans ton cas, je referais ce que nous avions fait en C. classique.

 int func() { char *p1 = NULL; /* So we have a definite value */ char *p2 = NULL; p1 = malloc(...); if(!p1) goto err_exit; p2 = malloc(...); if(!p2) goto err_exit; ... err_exit: free(p2); free(p1); return -1; } 

si je comstack ce code avec l’indicateur -O2

 gcc -Wall -std=c99 -O2 jump.c 

j’ai l’avertissement:

 jump.c: In function 'func': jump.c:10: warning: 'p2' may be used uninitialised in this function 

et sans avertissement sans optimisation

Comme le dit AndreyT , C99 permet de sauter par-dessus l’initialisation. Vous pouvez corriger votre logique en utilisant des étiquettes distinctes pour les deux échecs:

 int func() { char *p1 = malloc(...); if (p1 == NULL) goto err_exit_p1; char *p2 = malloc(...); if (p2 == NULL) goto err_exit; ... err_exit: free(p2); err_exit_p1: free(p1); return -1; } 

Il s’agit d’un modèle standard – les “erreurs précoces” entraînent un saut vers une partie ultérieure du code de sortie d’erreur.

Utiliser gotos n’est pas une bonne idée, et vous venez d’en trouver une raison. Vous devez appeler une fonction de traitement des erreurs pour chaque erreur individuelle.