Pourquoi «a»! = «A» en C?

110
void main() {
    if("a" == "a")
      printf("Yes, equal");  
    else
      printf("No, not equal");
}

Pourquoi la sortie No, not equal?

Javed Akram
la source
100
void main??? Ew ...
Paul R
47
Les compilateurs C intégrés autorisent void main () car il se peut qu'il n'y ait pas de système d'exploitation auquel donner un code de retour.
Jeanne Pindar
26
Comment une question comme celle-ci peut-elle être si souvent votée? Ce n'est vraiment pas si intéressant ... Je veux dire, que les chaînes sont des tableaux et les tableaux sont des pointeurs, c'est vraiment un vieux chapeau en C, n'est-ce pas?
Felix Dombek
64
@Felix, c'est une question écrite de manière concise qui aborde un point commun de confusion pour les nouveaux arrivants dans la langue. La SO n'est pas réservée aux experts - elle s'adresse également aux débutants, et des questions ciblées comme celle-ci sont bonnes pour renvoyer les débutants à l'avenir.
bdonlan
37
@Felix: Vous vous trompez. les tableaux ne sont pas des pointeurs
John Dibling

Réponses:

209

Ce que vous comparez, ce sont les deux adresses mémoire pour les différentes chaînes, qui sont stockées à des emplacements différents. Cela ressemble essentiellement à ceci:

if(0x00403064 == 0x002D316A) // Two memory locations
{
    printf("Yes, equal");
}

Utilisez le code suivant pour comparer deux valeurs de chaîne:

#include <string.h>

...

if(strcmp("a", "a") == 0)
{
    // Equal
}

En outre, "a" == "a"peut effectivement retourner true, en fonction de votre compilateur, qui peut combiner des chaînes égales au moment de la compilation en une seule pour économiser de l'espace.

Lorsque vous comparez deux valeurs de caractères (qui ne sont pas des pointeurs), il s'agit d'une comparaison numérique. Par exemple:

'a' == 'a' // always true
Tim Cooper
la source
12
GCC a également les options -fmerge-constantset -fno-merge-constantspermet d'activer / désactiver la fusion de chaînes et de constantes à virgule flottante entre les unités de traduction, bien que sur certains GCC, il semble que la fusion constante soit toujours activée quelle que soit cette option.
Adam Rosenfield
2
Cela fonctionnerait si vous utilisez «a» au lieu de «a». Le premier est un caractère, qui est en fait une valeur numérique.
GolezTrol
@GolezTrol: en C, le littéral «a» est en fait de inttype. :-) De plus, les pointeurs ne doivent pas nécessairement être des valeurs numériques.
Bastien Léonard
intest numérique aussi, n'est-ce pas? Mais je pensais que les caractères étaient Byte. Int est de 4 octets. Les pointeurs eux-mêmes sont également des nombres entiers. Ils contiennent l'adresse d'un ensemble de données (des données qui ne doivent en effet pas être numériques).
GolezTrol
'a' == 'A' // not true... MySQL ne diffère pas.
Steven
52

Je suis un peu en retard à la fête, mais je vais quand même répondre; techniquement les mêmes bits, mais d'un peu point de vue différent (langage C ci-dessous):

En C, l'expression "a"désigne une chaîne littérale , qui est un tableau statique sans nom de const char, avec une longueur de deux - le tableau se compose de caractères 'a'et'\0' - le caractère nul de fin signale la fin de la chaîne.

Cependant, en C, de la même manière que vous ne pouvez pas passer des tableaux aux fonctions par valeur - ou leur attribuer des valeurs ( après l'initialisation ) - il n'y a pas d'opérateur surchargé ==pour les tableaux, il n'est donc pas possible de les comparer directement. Considérer

int a1[] = {1, 2, 3};
int a2[] = {3, 4, 5};
a1 == a2 // is this meaningful? Yes and no; it *does* compare the arrays for
         // "identity", but not for their values. In this case the result
         // is always false, because the arrays (a1 and a2) are distinct objects

Si le ==ne compare pas les tableaux, que fait-il alors? En C, dans presque tous les contextes - y compris celui-ci - les tableaux se désintègrent en pointeurs (qui pointent vers le premier élément du tableau) - et comparer des pointeurs pour l'égalité fait ce que vous attendez. Si efficacement, en faisant cela

"a" == "a"

vous comparez en fait les adresses des premiers caractères dans deux tableaux sans nom . Selon le standard C, la comparaison peut donner soit vrai soit faux (c'est-à-dire 1 ou 0) - "a"s peut en fait désigner le même tableau ou deux tableaux totalement indépendants. En termes techniques, la valeur résultante n'est pas spécifiée , ce qui signifie que la comparaison est autorisée (c'est-à-dire que ce n'est pas un comportement indéfini ou une erreur de syntaxe), mais l'une ou l'autre des valeurs est valide et l'implémentation (votre compilateur) n'est pas requise pour documenter ce qui va réellement se passer.

Comme d'autres l'ont souligné, pour comparer des "chaînes c" (c'est-à-dire des chaînes terminées par un caractère nul), vous utilisez la fonction de commodité strcmptrouvée dans le fichier d'en-tête standard string.h. La fonction a une valeur de retour de 0pour des chaînes égales; il est considéré comme une bonne pratique de comparer explicitement la valeur de retour au 0lieu d'utiliser l'opérateur `! ´, c'est-à-dire

strcmp(str1, str2) == 0 // instead of !strcmp(str1, str2)
eq-
la source
47

Selon C99 (section 6.4.5 / 6)

Littéraux de chaîne

Il n'est pas précisé si ces tableaux sont distincts à condition que leurs éléments aient les valeurs appropriées .

Donc, dans ce cas, il n'est pas précisé si les deux "a"sont distincts. Un compilateur optimisé pourrait en conserver un seul "a"à l'emplacement en lecture seule et les deux références pourraient y faire référence.

Découvrez la sortie sur gcc ici

Prasoon Saurav
la source
19

Parce qu'il s'agit de 2 const char*pointeurs séparés , pas de valeurs réelles. Vous dites quelque chose comme 0x019181217 == 0x0089178216qui bien sûr renvoie NON

Utiliser à la strcmp()place de==

Antwan van Houdt
la source
7
Les littéraux de chaîne ne sont pas des pointeurs, ce sont des tableaux. Cependant, ils se désintègrent en pointeurs de comparaison.
GManNickG
@Gman vrai, désolé de ne pas être vraiment clair à ce sujet, j'ai tendance à l'oublier :)
Antwan van Houdt
9

En termes simples, C n'a pas d'opérateur de comparaison de chaînes intégré. Il ne peut pas comparer les chaînes de cette façon.

Au lieu de cela, les chaînes sont comparées à l'aide de routines de bibliothèque standard telles que strcmp () ou en écrivant du code pour parcourir chaque caractère de la chaîne.

En C, une chaîne de texte entre guillemets renvoie un pointeur vers la chaîne. Votre exemple compare les pointeurs, et apparemment vos deux versions de la chaîne existent à des adresses différentes.

Mais il ne s'agit pas de comparer les chaînes elles-mêmes, comme vous semblez vous y attendre.

Jonathan Wood
la source
3

Des pointeurs.

Le premier "a"est un pointeur vers une chaîne ASCII terminée par un zéro.

Le second "a"est un pointeur vers une autre chaîne ASCII terminée par null.

Si vous utilisez un compilateur 32 bits, je m'attendrais "a"=="a"-4. Je viens de l'essayer avec tcc / Win32, et j'obtiens "a"=="a"-2. Tant pis...

Nico57
la source
6
Pourquoi vous attendez-vous à ce que les chaînes soient alignées sur une limite de 4 octets? Ce ne sont pas des entiers. 2 est ce à quoi je m'attendrais (si le compilateur ne les fusionne pas), car chaque chaîne mesure deux octets, y compris le terminateur nul.
Sergei Tachenov
Un certain degré d'alignement peut, par exemple, permettre strcmpd'exécuter plusieurs octets à la fois. Certains compilateurs le font, d'autres non, certains ne le font que pour des chaînes plus longues que le minimum ...
zwol
@Zack: comment connaîtraient-ils la longueur de la chaîne avant de les comparer?
Joachim Sauer
Je voulais dire, certains compilateurs alignent des chaînes plus longues que le minimum.
zwol
1

Vous comparez deux adresses mémoire, donc le résultat ne sera pas toujours vrai. Avez-vous essayé if('a' == 'a'){...}?

SK9
la source
1

cette question ouvre une très bonne piste d'explication pour tous les débutants ....
laissez-moi également y contribuer .....

comme tout le monde l'a expliqué ci-dessus, pourquoi vous obtenez une telle sortie.

maintenant si vous voulez votre prog. Pour imprimer "oui égal" alors

soit utiliser

if(strcmp("a", "a") == 0)
{

}

ou
n'utilisez pas "a" comme chaînes, utilisez-les comme caractères ....

if('a'=='a')  
{  
printf ("yes Equal");  
}  

en C les caractères sont un entier court de 1 octet .......

N-JOIE
la source
Les caractères n'occupent qu'un octet, mais les littéraux de caractère, tels que 'a', sont en fait des entiers.
Spidey
0

Certains compilateurs ont l'option 'merge strings' que vous pouvez utiliser pour forcer toutes les chaînes constantes à avoir la même adresse. Si vous utilisiez cela, le "a" == "a"serait true.

Daniel Mošmondor
la source
0

si la comparaison entre les caractères est toujours entre guillemets simples, par exemple

if('a' == 'a')

et C ne peut pas prendre en charge la comparaison de chaînes comme "abc" == "abc"

C'est fait avec strcmp("abc","abc")

Bhavin Patel
la source
-5

Ce gars n'utilise pas de variables. Au lieu de cela, il utilise temporairement des tableaux de texte: aet a. La raison pour laquelle

void main() 
{
    if("a" == "a")
      printf("Yes, equal");  
    else
      printf("No, not equal");
}

ne fonctionne pas bien sûr, c'est que vous ne comparez pas les variables.
Si vous créez des variables comme:

char * text = "a";
char * text2 = "a";

alors tu pourrais comparer textavec text2, et ça devrait être vrai

Vous ne devriez peut-être pas oublier d'utiliser {et }=)

void main() {
    if("a" == "a")
    {
      printf("Yes, equal");
    }
    else
    {
      printf("No, not equal");
    }
}
D. Ace
la source
1
" et il devrait être vrai " - Non. Il n'est pas spécifié si les littéraux de chaîne seront stockés dans le même emplacement mémoire. Lisez les autres réponses.
Spikatrix