Pour m'entrainer je travaille sur les chaines de caractères sans utiliser la librairie <string.h>. J'ai essayé d'écrire un programme qui inverse une chaine de caractère, comme un miroir. Par exemple, si j'écris "lynx", il renvoi "xnyl". Pour ça j'ai créé deux fonctions en plus de main : la première me donne le nombre de caractère de la chaine, aucun souci. C'est au niveau de la seconde fonction que ça coince.
C'est elle qui est chargée de créer la chaine miroir et de la renvoyer à main. Je l'ai écrite pour qu'elle fonctionne ainsi : une variable i=0, et une seconde qui est égale à la longueur de la chaine originale (k). On fait donc chaine_miroir[i]=chaine_originale[k]. Donc le dernier terme de la chaine originale devient le premier de la chaine miroir. On fait i++ et k-- et après l'avant dernier terme de la chaine originale devient le second de la chaine_miroir et cela tant qu'on a pas fait tout les termes (j'ai opté pour une boucle While). Une fois cela fait, on renvoi chaine_miroir à main et on l'affiche.
C'est là qu'est le problème : au lieu de m'afficher la chaine inversée, il m'affiche "(null)".
J'ai fais des tests et on dirait que le souci est au niveau de l'étape chaine_miroir[i]=chaine_originale[k]. Pourriez-vous m'aider ?
Voici mon code :
#include <stdlib.h>
#include <stdio.h>
int len(char* chaine) //Fonction qui renvoi le nombre de caractère d'une chaine
{
unsigned int i,a = 0; // i --> variable pour itérer; a--> compte le nombre de caractère
for (i = 0; chaine[i]; i++) // Pour i=0 dans chaine avec i +=1
{
a++;
}
return a;
}
int miror(int nb_car, char* chaine) //création de la chaine miroir
{
char mir[100]; //variable qui contiendra la chaine miroir
unsigned int k = 0;
while (nb_car > -1) //nb_car(actere) > -1 signifie qu'il reste encore des termes, donc on continue
{
mir[k]=chaine[nb_car]; //inversement des termes
nb_car-- ; //On passe au terme précédent
k++; //On passe au terme suivant
}
return mir;
}
int main()
{
char mot[100]; //Ne fonctionne pas avec 'char*'
printf("Entrez un mot : ");
scanf ("%s", mot); // On récupère la chaine
printf("Nombre de caracteres : %i.\n", len(mot));
printf("Voici : %s \n", miror(len(mot), mot));
}
Tu veux faire retourner une chaîne de caractère à ta fonction miroir et son prototype retourne un int ? ça n'est pas la même chose !
Dans le code tu retournes bien l'adresse du tableau mir, mais le problème c'est que ce tableau est un tableau local à la fonction. Il n'existe que le temps de l'exécution de la fonction. Quand tu sors de la fonction il est détruit, il n'existe plus.
Les warning ne sont pas là pour faire joli, il faut les régler:
ab.c: In function 'miror':
ab.c:26:12: warning: return makes integer from pointer without a cast [-Wint-conversion]
return mir;
^~~
ab.c:26:12: warning: function returns address of local variable [-Wreturn-local-addr]
Une fonction en C ne peux pas retourner un tableau local....
Autre chose: tu parles de miroir[i] dans ta description, mais il ne se trouve pas dans ta fonction miroir. Et celle-ci va trop loin: elle devrait inverser les caractères jusqu'au milieu de la chaine, or toi tu les inverses tous, donc la chaine d'arrivée sera la même que la chaine de départ.
- Edité par edgarjacobs 23 février 2022 à 23:32:22
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Tu veux faire retourner une chaîne de caractère à ta fonction miroir et son prototype retourne un int ? ça n'est pas la même chose !
Dans le code tu retournes bien l'adresse du tableau mir, mais le problème c'est que ce tableau est un tableau local à la fonction. Il n'existe que le temps de l'exécution de la fonction. Quand tu sors de la fonction il est détruit, il n'existe plus.
Je ne suis pas sûr de comprendre, il faudrait donc que déclare ma fonction Miror telle que :
char* miror(int nb_car, char* chaine)
?
J'ai essayé et ça n'a rien changé.
Aussi, j'ai essayé de déclaré mir comme une variable globale :
et effectivement ça n'affiche plus "(null)", mais ça n'affiche toujours rien.
edgarjacobs a écrit:
Hello,
Les warning ne sont pas là pour faire joli, il faut les régler:
ab.c: In function 'miror':
ab.c:26:12: warning: return makes integer from pointer without a cast [-Wint-conversion]
return mir;
^~~
ab.c:26:12: warning: function returns address of local variable [-Wreturn-local-addr]
Une fonction en C ne peux pas retourner un tableau local....
Autre chose: tu parles de miroir[i] dans ta description, mais il ne se trouve pas dans ta fonction miroir. Et celle-ci va trop loin: elle devrait inverser les caractères jusqu'au milieu de la chaine, or toi tu les inverses tous, donc la chaine d'arrivée sera la même que la chaine de départ.
- Edité par edgarjacobs il y a environ 1 heure
J'ai essayé de changer mir en variable globale mais ça n'affiche toujours pas le bon résultat (voir message au-dessus).
Je ne comrpends pas la seconde partie de ton message : miroir[i] avec i++, on passe à la lettre suivante de la chaine et pour chaine_originale[k] avec k--, on passe à la lettre suivante à chaque fois, je ne vois pas comment les deux chaines peuvent finir identique.... ?
- Edité par Sakisaac Jonathan 24 février 2022 à 0:29:21
Je ne suis pas sûr de comprendre, il faudrait donc que déclare ma fonction Miror telle que :
char* miror(int nb_car, char* chaine)
Oui, mais je suis étonné que tu ne comprennes pas ? Une chaîne de caractère, c'est quand même stocké dans un tableau de char ! Alors que viennent faire des entiers (int) ici ?
Sakisaac Jonathan a écrit:
Aussi, j'ai essayé de déclaré mir comme une variable globale ...
et effectivement ça n'affiche plus "(null)", mais ça n'affiche toujours rien.
Alors, ça pourrait être une solution qui fonctionne, même si ce n'est pas la meilleure !
Ça ne fonctionne pas, car ton algo n'est pas correcte :
edgar t'as expliqué pourquoi !
Quand tu écris : chaine[nb_car] , nb_car et la taille de la chaîne, n'oublies pas que le dernier caractère (visible) de la chaîne ce situe à l'indice (taille_de_la_chaine -1). Exemple pour la chaîne abcd :
Je ne suis pas sûr de comprendre, il faudrait donc que déclare ma fonction Miror telle que :
char* miror(int nb_car, char* chaine)
Oui, mais je suis étonné que tu ne comprennes pas ? Une chaîne de caractère, c'est quand même stocké dans un tableau de char ! Alors que viennent faire des entiers (int) ici ?
Oui en effet c'est évident. En fait je pensais que toutes les fonctions devaient êtres déclarées en 'int' (comme main). Je me disais que c'était sûrement une convention.
rouIoude a écrit:
Alors, ça pourrait être une solution qui fonctionne, même si ce n'est pas la meilleure !
Ça ne fonctionne pas, car ton algo n'est pas correcte :
edgar t'as expliqué pourquoi !
Quand tu écris : chaine[nb_car] , nb_car et la taille de la chaîne, n'oublies pas que le dernier caractère (visible) de la chaîne ce situe à l'indice (taille_de_la_chaine -1). Exemple pour la chaîne abcd :
a b c d \0
0 1 2 3 4
- Edité par rouIoude il y a moins de 30s
Ah oui d'accord, merci beaucoup ! J'ai pris tout ça en compte et maintenant mon programme fonctionne !
Un grand merci à vous deux !
- Edité par Sakisaac Jonathan 24 février 2022 à 0:56:00
Essaies de mettre le caractère 'x' (ou un autre) dans toutes les cases de mir "avant" d'appeler la fonction miror et tu verras ce que ça donne.
Tu ne recopies pas le caractère de fin de chaîne '\0'. C'est une "chance" que mir soit initialisé à 0.
Tu pourrais passer les deux chaînes en paramètre à ta fonction. Tu n'a pas besoin de passer la longueur. Pourquoi ne pas l'obtenir en appelant la fonction len() dans la fonction miror.
- Edité par PierrotLeFou 24 février 2022 à 1:35:19
Le Tout est souvent plus grand que la somme de ses parties.
J'ai un autre souci : j'aimerai que le programme me signale que le mot est un palindrome lorsque s'en est un.
En gros : Si chaine_originale = chaine miroir --> afficher : "Ce mot est un palindrome."
J'ai déjà une fonction "miror" (fonctionnelle) qui me renvoi la chaine miroir et on recupère le mot (chaine_originale) dans la fonction main. Donc j'ai écris mon code ainsi dans la fonction main : Si chaine_originale == miroir(chaine_originale) --> afficher "Ce mot est un palindrome".
Le problème est que même si le mot est un palindrome (on entre "Anna" ou "Laval" par exemple), le programme me dit à chaque fois que ce n'en est pas un.
Voici le code :
#include <stdlib.h>
#include <stdio.h>
char mir[100]; //si l'on met mir dans la fonction "miror", alors le programme affichera 'null'
int len_chaine; // On y enregistrera la longueur de la chaine
unsigned int len(char* chaine) //Fonction qui renvoi le nombre de caractère d'une chaine
{
unsigned int i,a = 0;
for (i = 0; chaine[i]; i++)
{
a++;
}
len_chaine = a;
return len_chaine;
}
char* miror(char* chaine) //Fonction qui va créer la chaine miroir
{
unsigned int k = 0;
len_chaine--; // le dernier terme est '\0' il ne nous interesse pas
while (len_chaine > -1) //Si len_chaine est supérieur à -1 c'est qu'il reste encore des lettres dans le mot
{
mir[k]=chaine[len_chaine]; //On ecrit la derniere lettre du mot a la premiere place dans la nouvelle chaine
len_chaine-- ; //On passe à la lettre d'avant...
k++; //...qu'on ecrira juste après la précédente
}
return mir;
}
int main()
{
char mot[100]; //Ne fonctionne pas avec 'char*'
printf("Entrez un mot : ");
scanf ("%s", mot); // On récupère la chaine
printf("Nombre de caracteres : %x.\n", len(mot));
printf("Miroir du mot : %s \n", miror(mot));
if(mot == miror(mot)) //On regarde si c'est un palindrome
printf("%s est est un palindrome !\n", mot);
else
printf("%s n'est pas un palindrome.\n", mot);
}
PierrotLeFou:
Merci, lorsque j'aurai fini mon programme je prendrai en compte tes remarques pour l'améliorer. Je n'utilise pas len() car j'essai de me débrouiller sans <string.h>.
- Edité par Sakisaac Jonathan 24 février 2022 à 12:28:49
À partir d'un algorithme correct pour inverser une chaîne on pourrait facilement écrire celle qui teste si c'est un palindrome. Au lieu de faire "j'échange le premier et le dernier", je ferais "je compare le premier et le dernier"., etc
Le Tout est souvent plus grand que la somme de ses parties.
À partir d'un algorithme correct pour inverser une chaîne on pourrait facilement écrire celle qui teste si c'est un palindrome. Au lieu de faire "j'échange le premier et le dernier", je ferais "je compare le premier et le dernier"., etc
Je ne sais pas comment fonctionne la fonction strcmp donc j'ai opté pour cette solution et ça a fonctionné.
La fonction 'strcmp' appartient à la bibliothèque <string.h>, donc Sakisaac Johathan ne pouvait pas s'en servir (relire la toute première phrase de son message).
strcmp page 98 "Le langage C" Kernigham & Ritchie édition en français avril 1986
Tu lui propose d'acheter le livre ? Tu sais maintenant quand tu veux une info rapidement, tu peux utiliser le réseau internet ! Il y a même des moteurs de recherche dessus, Google par exemple !
strcmp page 98 "Le langage C" Kernigham & Ritchie édition en français avril 1986
Tu lui propose d'acheter le livre ? Tu sais maintenant quand tu veux une info rapidement, tu peux utiliser le réseau internet ! Il y a même des moteurs de recherche dessus, Google par exemple !
J'apprécie ton humour !
Je ne me séparerai jamais d'un livre qui fût une référence.
- Edité par joel76 25 février 2022 à 20:35:35
Le crayon la gomme et le papier sont les meilleurs outils du programmeur !
strcat(s,t) /* concatenate t to end of s */
char s[], t[]; /* s must be big enough */
{
int i, j;
i = j = 0;
while (s[i] != '\0') /* find end of s */
i++;
while ((s[i++] = t[j++]) != '\0') /* copy t */
;
}
Je ne sais pas si ce genre de code serait accepté maintenant, à l'époque c'était le must !
A noter pas de return s !
- Edité par joel76 25 février 2022 à 20:37:28
Le crayon la gomme et le papier sont les meilleurs outils du programmeur !
Je ne sais pas de quand date ton livre mais dès les premières versions d'UNIX (ici V7 de janvier 1979) le code classique d'un strcat est :
/*
* Concatenate s2 on the end of s1. S1's space must be large enough.
* Return s1.
*/
char *
strcat(s1, s2)
register char *s1, *s2;
{
register char *os1;
os1 = s1;
while (*s1++)
;
--s1;
while (*s1++ = *s2++)
;
return(os1);
}
De nos jours l'approche est différente. Par exemple dans la gnu libc, un strcat est implémenté via strcpy+strlen, et strcpy est implémenté avec memcpy. Au final toute la complexité des codes est condensée dans strlen et memcpy.
Une stratégie similaire est utilisée dans la Musl.
Est-ce que strlen() a un truc magique pour avancer à la fin de la chaîne de destination? Un bon compilateur optimiserait while(*dst) dst++; dans des registres. Je ne sais pas comment memcpy() est implémenté en général.
Le Tout est souvent plus grand que la somme de ses parties.
Alors @Joel76, je n'ai pas à ma disposition ton livre, mais dans le K&R 2ème édition original on trouve p45, juste avant ton exemple sans return :
K&R ont du rajouter cette ligne dans la seconde édition.
@Pierrot :
« Toute technologie suffisamment avancée est indiscernable de la magie. » Troisième loi de Clarke.
La gnu libc caste le pointeur vers char de la chaîne en un pointeur vers un unsigned long (4 octets en 32bits, 8 en 64bits sous linux) et détermine si cet unsigned long pointé possède un octet valant 0. Bon juste avant de faire cela, il y a une portion qui checke la présence d'un '\0' le temps d'arriver à une adresse correctement alignée (tu as la code en lien).
Ce n'est pas une «optimisation» du compilateur, mais une optimisation du codeur en considérant une plateforme.
La musl libc prend une approche similaire mais avec un code par défaut classique si on ne compile pas avec gcc :
memcpy suit la même procédure = on commence par copier des gros morceau tant qu'on peut puis à la fin on copie en octet ce qui reste. Le code de musl est relativement compréhensible et à nouveau possède un code par défaut classique si on ne compile pas avec gcc :
void *memcpy(void *restrict dest, const void *restrict src, size_t n)
{
unsigned char *d = dest;
const unsigned char *s = src;
#ifdef __GNUC__
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define LS >>
#define RS <<
#else
#define LS <<
#define RS >>
#endif
typedef uint32_t __attribute__((__may_alias__)) u32;
uint32_t w, x;
for (; (uintptr_t)s % 4 && n; n--) *d++ = *s++;
if ((uintptr_t)d % 4 == 0) {
for (; n>=16; s+=16, d+=16, n-=16) {
*(u32 *)(d+0) = *(u32 *)(s+0);
*(u32 *)(d+4) = *(u32 *)(s+4);
*(u32 *)(d+8) = *(u32 *)(s+8);
*(u32 *)(d+12) = *(u32 *)(s+12);
}
if (n&8) {
*(u32 *)(d+0) = *(u32 *)(s+0);
*(u32 *)(d+4) = *(u32 *)(s+4);
d += 8; s += 8;
}
if (n&4) {
*(u32 *)(d+0) = *(u32 *)(s+0);
d += 4; s += 4;
}
if (n&2) {
*d++ = *s++; *d++ = *s++;
}
if (n&1) {
*d = *s;
}
return dest;
}
if (n >= 32) switch ((uintptr_t)d % 4) {
case 1:
w = *(u32 *)s;
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
n -= 3;
for (; n>=17; s+=16, d+=16, n-=16) {
x = *(u32 *)(s+1);
*(u32 *)(d+0) = (w LS 24) | (x RS 8);
w = *(u32 *)(s+5);
*(u32 *)(d+4) = (x LS 24) | (w RS 8);
x = *(u32 *)(s+9);
*(u32 *)(d+8) = (w LS 24) | (x RS 8);
w = *(u32 *)(s+13);
*(u32 *)(d+12) = (x LS 24) | (w RS 8);
}
break;
case 2:
w = *(u32 *)s;
*d++ = *s++;
*d++ = *s++;
n -= 2;
for (; n>=18; s+=16, d+=16, n-=16) {
x = *(u32 *)(s+2);
*(u32 *)(d+0) = (w LS 16) | (x RS 16);
w = *(u32 *)(s+6);
*(u32 *)(d+4) = (x LS 16) | (w RS 16);
x = *(u32 *)(s+10);
*(u32 *)(d+8) = (w LS 16) | (x RS 16);
w = *(u32 *)(s+14);
*(u32 *)(d+12) = (x LS 16) | (w RS 16);
}
break;
case 3:
w = *(u32 *)s;
*d++ = *s++;
n -= 1;
for (; n>=19; s+=16, d+=16, n-=16) {
x = *(u32 *)(s+3);
*(u32 *)(d+0) = (w LS 8) | (x RS 24);
w = *(u32 *)(s+7);
*(u32 *)(d+4) = (x LS 8) | (w RS 24);
x = *(u32 *)(s+11);
*(u32 *)(d+8) = (w LS 8) | (x RS 24);
w = *(u32 *)(s+15);
*(u32 *)(d+12) = (x LS 8) | (w RS 24);
}
break;
}
if (n&16) {
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
}
if (n&8) {
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
}
if (n&4) {
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
}
if (n&2) {
*d++ = *s++; *d++ = *s++;
}
if (n&1) {
*d = *s;
}
return dest;
#endif
for (; n; n--) *d++ = *s++;
return dest;
}
Mon K & R (en anglais) date de 1978, et page 44 on y trouve bien une version de strcat() qui est byte pour byte identique à celle donnée par @joel76. Je pense que c'est la première version du K & R, et (je viens de le remarquer) je l'ai payée 1750 FB (43.38 €). J'ai sans doute dû l'acheter en 1981.
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Mon édition date plus de 88/89, post ANSI C. Le code y est bien identique, il n'y a qu'une ligne spécifiant que ce n'est pas la version de la libc, que l'exemple donné ne renvoyait pas de pointeur sur la chaîne concaténée.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Le Tout est souvent plus grand que la somme de ses parties.
Le Tout est souvent plus grand que la somme de ses parties.
Le Tout est souvent plus grand que la somme de ses parties.
Le crayon la gomme et le papier sont les meilleurs outils du programmeur !
Le crayon la gomme et le papier sont les meilleurs outils du programmeur !
Le crayon la gomme et le papier sont les meilleurs outils du programmeur !
Le Tout est souvent plus grand que la somme de ses parties.
Le crayon la gomme et le papier sont les meilleurs outils du programmeur !
Le Tout est souvent plus grand que la somme de ses parties.
Le crayon la gomme et le papier sont les meilleurs outils du programmeur !
Le crayon la gomme et le papier sont les meilleurs outils du programmeur !
Le Tout est souvent plus grand que la somme de ses parties.
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent