En C, le prototype de la fonction de valeur absolue (qui accepte un flottant) est
float fabsf( float );
Pourquoi ce prototype n'accepte-t-il pas une valeur constante, comme ceci:
float fabsf( float const );
fabsf ne changera pas la valeur de l'entrée, n'est-ce pas?
Si j'ai une fonction qui accepte une entrée et appelle fabsf, suis-je obligé d'éviter de spécifier l'entrée comme const?
Quelle est la façon appropriée de gérer l'exactitude de const dans cette situation?
Modifier
Comme l'a commenté M.M, sur les paramètres des prototypes , le const
est ignoré. La source éditée de la réponse originale (voir ci-dessous) montre ceci:
float correct(float const value);
float erroneous(float const value);
float changer(float value);
float correct(float value) {
return -value;
}
float erroneous(float value) {
value = -value;
return value;
}
float changer(float value) {
value = -value;
return value;
}
Il n'y a aucun message d'erreur.
Quoi qu'il en soit, je vais laisser l'original en place dans l'espoir que cela puisse aider.
Original
Le const
d'un paramètre rend ce paramètre en lecture seule à l'intérieur de la fonction.
Par exemple:
float correct(float const value) {
return -value;
}
float erroneous(float const value) {
value = -value;
return value;
}
float changer(float value) {
value = -value;
return value;
}
Cette source ne se compilera pas sans message d'erreur.
La fonction correct()
lira la valeur donnée, changera son signe et renverra la valeur négative.
La fonction erroneous()
semble faire la même chose, sauf qu'il y a une affectation au paramètre. Mais comme le paramètre est const
ce n'est pas autorisé.
Ensuite, la fonction changer()
fonctionnera comme les deux avant, mais elle ne donne aucune erreur.
Regardons le site de l'appel:
float f = 3.14159;
float g = correct(f); // or erroneous(f) or changer(f)
La variable f
donnée en argument sera copiée dans le paramètre value
. Il ne changera jamais même si changer()
sera appelé.
Vous voudrez peut-être regarder les paramètres comme une sorte de variables locales. En fait, ils sont principalement traités comme cela dans le code machine généré.
Alors, pourquoi voyez-vous const
parfois? Vous le voyez si un pointeur est défini comme paramètre.
Si vous ne voulez pas que la valeur pointée vers soit modifiée, vous devez ajouter const
; mais faites-le à la bonne position!
void effective(int const * pointer);
void futile(int * const pointer);
void possible_but_overly_restricted(int const * const pointer);
C utilise le passage par valeur. La valeur du paramètre d'une fonction est une copie de l'argument que vous donnez.
Il est correct de copier à la fois les flotteurs const et non const, et le résultat est un flotteur non const.
C'est similaire à l'affectation:
const float f = 5.5f;
float g = f; // OK
En fait, le langage spécifie que la valeur d'une expression ne peut jamais être const
, c'est-à-dire que lorsqu'une valeur est lue dans une variable, cette valeur n'est pas const
même si la variable l'était.
Comme le langage C utilise la sémantique de passage par valeur, l'argument any que vous lui passez, bien qu'il puisse être modifié en interne, n'affecte pas directement la valeur que vous transmettez.
Cela signifie que du point de vue de l'appelant, float fabsf( float );
et float fabsf( const float );
sont identiques. Il est donc inutile de faire le paramètre const
.
Où il fait est logique d'utiliser const
est si le paramètre que vous passez est un pointeur, par exemple:
void print_string(char *str)
Cette fonction, malgré ce que son nom l'indique, peut déréférencer le pointeur donné et modifier ce qu'il pointe, c'est-à-dire str[0] = 'x'
, pour entraîner un changement visible par la fonction appelante. Si cette fonction était définie comme ceci:
void print_string(const char *str)
L'appelant est assuré que la fonction ne peut effectuer aucune modification sur ce que str
pointe.
Pour ajouter une perspective d'avocat linguistique:
Pour que deux types de fonctions soient compatibles, les deux doivent spécifier des types de retour compatibles. De plus, les listes de types de paramètres, si les deux sont présentes, doivent convenir du nombre de paramètres et de l'utilisation du terminateur Ellipsis; les paramètres correspondants doivent avoir des types compatibles. [..] Dans la détermination de la compatibilité de type et d'un type composite, [..] chacun le paramètre déclaré avec le type qualifié est considéré comme ayant la version non qualifiée de son type déclaré.
N1570 6.7.6.3/15
Cela signifie que ces deux sont compatibles:
void foo(int const);
void foo(int);
Par conséquent, vous pouvez écrire le prototype avec ou sans const
(ce qui signifie sans plus de sens; moins à taper/lire) et pouvez ajouter const
dans la définition de la fonction si vous voulez éviter de modifier accidentellement le (copied - call by value!) paramètre dans le corps des fonctions.