char firstmatch(char *s1, char *s2) {
char *temp;
temp = s1;
do {
if (strchr(s2, *temp) != 0)
return temp;
temp++;
} while (*temp != 0);
return 0;
}
char *strchr(register const char *s, int c) {
do {
if (*s == c) {
return (char*)s;
}
} while (*s++);
return (0);
}
I am new to programming and I have been given this code which finds the first character in a string s1 that is also in string s2. The task is to understand the C code and convert into Assembly code. As of right now my focus is just to understand what the C code is doing and I am currently having difficulty with pointers. I can sort through the code on the firstmatch() function and make my way down but I am kind of confused with the char * strchr() function. I am unable to understand whats the point of int c in regards to a constant character pointer? I'd appreciate if somebody could help explain it.
4 Réponses :
L'argument int c pourrait aussi bien être char c . Le type de * temp est char .
La fonction strchr prend un pointeur dans une chaîne terminée par nul et un char et renvoie le pointeur vers l'occurrence suivante du char ou null s'il atteint le nul à la fin de la chaîne.
Le char * s1 représente une chaîne en C. Le 0 représente l'équivalent Acsii de '\ 0' qui est la terminaison d'une chaîne en C. Les caractères et les entiers sont interchangeables, mais vous devez connaître la valeur Ascii de chaque caractère. La lettre «A» équivaut à l'entier 65 par valeur Ascii. Cela devrait répondre à votre question sur int c. Cela ne fait aucune différence de comportement pour le code.
Maintenant, supposons que vous ayez la chaîne hello and meh, vous auriez:
First iteration: 'm' == 'h' -> false therefore proceed to next letter (*s++) Second iteration: 'e' == 'h' -> false therefore proceed to next letter (*s++) Third iteration: 'h' == 'h' -> true therefore return a char value that is not 0.
Donc vous appelez:
strchr('meh', 'h')
temp reçoit la valeur 'hello'.
Maintenant, vous appelez
firstmatch('hello', 'meh')
* temp dans ce cas, le scénario est équivalent à temp [0], qui est 'h'.
Dans le strchr, il parcourt chaque lettre de 'meh', en commençant par 'm'.
char * s1 = ['h', 'e','l','l','o','\0']
char * s2 = ['m', 'e', 'h','\0']
Ceci renvoie us à la fonction firstmatch dans la condition if. Puisque la condition if passe à la troisième itération, elle nous renvoie «h».
Supposons que la troisième itération échoue, elle incrémenterait sur la lettre suivante de s1, qui serait «e», et suivrait le même procédure décrite ci-dessus.
Enfin, le (* temp! = 0) signifie que si nous rencontrons le '\ 0' dans le s1 pour 'hello' que nous avons défini ci-dessus, alors il arrête la boucle entière et renvoie 0. Indique qu'il n'y a pas la même lettre.
Lisez l'arithmétique des pointeurs en C / C ++ si vous ne comprenez pas pourquoi * temp == temp [0]. De même * temp ++ == temp [n + 1] (n représentant le caractère courant).
Je suis toujours confus. En supposant que les 2 chaînes sont, str1 = "bonjour" stockées à l'adresse 100 et str2 = "meh" stockées à 104. En commençant par la fonction firstmatch: s1 doit être 100 et s2,104. * s1 = "bonjour" pointant vers le premier caractère "h" et * s2 = "meh" pointant vers "m". temp est identique à s1. Maintenant, la fonction strchr est appelée avec les arguments s2 et * temp. À la fonction strchr: enregistrez const * s = "m" mais le deuxième argument ne devrait-il pas être un argument de caractère contenant 'h' de la chaîne 1. Pourquoi est-ce que je compare à int c? Ou est-il assez intelligent pour changer la valeur de «h» en valeur ascii?
Oui, C est assez intelligent pour le changer automatiquement. Les valeurs int et char sont interchangeables. 'A' == 65 équivaut à 65 == 65. Il serait probablement préférable pour eux de comparer char à char pour plus de clarté, mais char à int fonctionne toujours aussi bien.
strchr () reçoit un pointeur vers (pensez à l'adresse mémoire de) le premier (ou le seul) caractère d'une séquence.
La fonction extrait un caractère de la mémoire en utilisant ce pointeur s et voit si sa valeur correspond à la valeur de c . S'il y a une correspondance, il renvoie le pointeur.
S'il n'y a pas de correspondance, il fait avancer le pointeur vers le caractère suivant de la séquence (c'est-à-dire qu'il incrémente l'adresse mémoire de 1) et se répète.
S'il n'y a pas de correspondance et que la valeur du caractère de la mémoire est 0, NULL est renvoyé.
Le pointeur étant sur un const char implique que la mémoire ne va pas être écrite, mais peut être lue. En effet, la fonction n'essaye jamais d'écrire en utilisant le pointeur.
Donc, vous lisez des caractères de mémoire et vous les comparez à un entier. Dans la plupart des expressions, les caractères se convertissent implicitement en entier signé (si une telle conversion est généralement possible sans perte de toute valeur de type char ) ou unsigned int (sinon). Consultez les promotions d'entiers à ce sujet. Si après cela, les deux côtés de l'opérateur == sont signés entre les entiers, tout est trivial, comparez-les simplement. Si l'un est unsigned int (le caractère * s promu) tandis que l'autre est signé int ( c ) , celui signé est converti en non signé (voir le même article lié pour la logique / règles), après quoi les deux côtés de == ont le même type ( unsigned int ) et sont comparables (c'est l'une des idées clés de C, la plupart des opérateurs binaires convertissent leurs entrées en un type commun et produisent le résultat de ce type commun).
En termes simples, en C, vous pouvez comparer différents types arithmétiques et le compilateur insérera les conversions nécessaires (selon les règles du langage). Cela dit, toutes les conversions ne conservent pas la valeur (par exemple, une conversion de signed int en unsigned int ne conserve pas les valeurs négatives, mais elles sont converties de manière bien définie) et cela peut être surprenant (par exemple, -1> 1u évalue à 1, ce qui semble absurde à quiconque connaît un peu les mathématiques), en particulier pour ceux qui sont nouveaux dans la langue.
La vraie question ici semble "Pourquoi c n'est-il pas défini comme char ?".
Si l'on inspecte les fonctions standard de la bibliothèque C, il trouvera que les valeurs de type char ne sont (presque?) Jamais passées ou renvoyées, bien que passer ou renvoyer des pointeurs vers char soit assez fréquent. Les caractères individuels sont généralement transmis au moyen du type int . La raison en est probablement que, comme mentionné ci-dessus, char serait converti en int ou unsigned int dans une expression de toute façon, donc quelques conversions supplémentaires (retour à char puis à nouveau à int ) peut être évité.
La fonction strchr () dans votre exemple de code est une implémentation incomplète de la fonction de bibliothèque C Standard qui localise la première occurrence d'un caractère dans une chaîne C, le cas échéant.
L'argument a le type int pour des raisons historiques: dans les premières versions des fonctions de langage, les arguments ne seraient tapés que si le type implicite int ne suffisait pas. les arguments de caractère ont été passés en tant que valeurs int , il était donc inutile de taper l'argument différemment.
Le mot clé register est obsolète: les premiers compilateurs C n'étaient pas aussi avancés comme ceux actuels et le programmeur pourrait aider les générateurs de code à déterminer les variables à stocker dans les registres du processeur en ornant leurs définitions du mot-clé register . Les compilateurs modernes sont plus efficaces et battent généralement les programmeurs à ce jeu, donc ce mot-clé est généralement ignoré de nos jours.
Notez cependant que cette implémentation se comporte différemment de la fonction Standard: la valeur de c code > doit être converti en char avant la comparaison. Comme indiqué par chux, toutes les fonctions de traitent les octets des chaînes C et des blocs de mémoire comme des caractères non signés à des fins de comparaison.
Voici une version plus lisible avec le comportement correct:
#include <string.h>
char *strchr(const char *str, int c) {
const unsigned char *s = (const unsigned char *)str;
do {
if (*s == (unsigned char)c) {
return (char *)s;
}
} while (*s++ != '\0');
return NULL;
}
UV pour une bonne mise en œuvre. Détail pédant: (uniquement nécessaire pour une portabilité élevée). * s == (char) c n'est potentiellement pas le bon type de signe pour la comparaison par "Pour toutes les fonctions de ce paragraphe, chaque caractère doit être interprété comme s'il avait le type unsigned char (et donc chaque représentation d'objet possible est valide et a une valeur différente). " §7.23.1 3. Considérons const unsigned char * us = s; ... if (* us == (caractère non signé) c) {... * us ++! = '\ 0' . Avec le complément de 2 commun, pas nécessaire cependant, seul un problème pour les autres très rares comme la comparaison signée ne distingue pas -0, +0.
@chux: vous avez raison, mais c'est vraiment extrême et très probablement sans intérêt pour tous les systèmes réels: je suis prêt à parier qu'un système avec signe / magnitude ou ses représentations complémentaires pour les entiers, en particulier char , doit avoir caractères non signés par défaut pour être conforme.
Nous convenons certainement de l'extrême rareté de cette préoccupation.