Pages : 1
#1 Le 31/10/2013, à 16:18
- kboo
C/C++ décalage de pointeur
Bonjour,
Je cherche à l'aide de ce code à décalé le pointeur du tableau toto de 4 octets, afin d'afficher " toto" à la place de "toto"
( je sais que je peux faire strcpy(toto, " toto"); mais c'est la seule manière que j'ai trouvé pour isoler et exposer mon problème:
#include <iostream>
#include <cstring>
int main(void)
{
std::cout << "Ok" << std::endl;
char toto[10];
&toto +=4; // horreur qui ne marche pas.. j'essaye de faire quelque chose de semblable....
strcpy(toto, "toto");
std::cout << toto << std::endl;
return 0;
}
merci bien!
Hors ligne
#2 Le 01/11/2013, à 00:37
- jacobus77
Re : C/C++ décalage de pointeur
J'ai pas bien compris ce que tu veux faire mais bon si ça peux t'aider.
#include <iostream>
#include <cstring>
int main()
{
std::cout << "Ok" << std::endl;
char toto[10] = "toto";
std::cout << toto << std::endl;
int i, len;
for (i = 0, len = strlen(toto); i < len; i++) {
toto[i + len] = toto[i];
toto[i] = ' ';
}
toto[i + len] = '\0';
std::cout << toto << std::endl;
return 0;
}
Hors ligne
#3 Le 03/11/2013, à 13:39
- darunia_goron
Re : C/C++ décalage de pointeur
jacobus77 a proposé une bonne solution à la main. En utilisant les bibliothèques, tu peux faire quelque chose du genre :
#include <iostream>
#include <cstring>
int main(void)
{
std::cout << "Ok" << std::endl;
char toto[10];
memset (toto, ' ', sizeof (toto)); /* Tu dois premièrement initialiser ton tableau. (Ici avec des espaces). */
strcpy (toto + 4, "toto"); /* On écrit le texte "toto" à partir de la quatrième case du tableau. */
/* Tu peux également passer par un pointeur intermédiaire, genre : char *pt_tmp = toto + 4 */
std::cout << toto << std::endl;
return 0;
}
Ton erreur était que toto est déjà un pointeur. &toto était donc un non sens.
P.S. : je n'ai pas testé le code.
Hors ligne
#4 Le 03/11/2013, à 14:13
- Zakhar
Re : C/C++ décalage de pointeur
Je pense que tu as à la base un problème de compréhension de ce qu'est un pointeur, du moins comment C le gère...
J'ai aussi un peu galéré pour comprendre ça avec la doc... il y a 20 ans... mais quand tu as le "déclic" après tout devient clair.
Donc pour expliquer, lorsque tu définis :
char toto[10];
Tu as défini un emplacement mémoire qui va pouvoir contenir 10 caractères.
Utilisé sans indice, toto désigne le début de ta zone mémoire, est c'est donc bien un pointeur sur caractère.
Seulement, comme c'est une zone de stockage, le "pointeur" toto est une CONSTANTE.
Normal, c'est l'endroit où la zone de 10 est stockée, c'est donc un endroit fixe, pas un truc que tu peux modifier.
Ca n'a donc aucun sens de d'essayer de changer toto comme tu le fais en faisant :
toto += 4;
Par contre, tu peux très bien faire :
char *s;
s= toto + 4;
En effet, s est ici un pointeur (un vrai, pas une constante, avec une zone mémoire réservée pour stocker la valeur du pointeur).
Et tu peux initialiser s à toto + 4, et te servir ensuite de s.
Note que si c'est plus clair pour toi, tu peux aussi faire :
s = &toto[4];
C'est pareil, que tu prennes toto +4 ou l'adresse du 4ème caractère de toto, les deux donnent le même résultat.
Mais là tu as alors un problème d'algorithme. Même si tu copies ta chaîne de caractère vers s qui pointe dans la 4ème case, qui te dit qu'il y a des blancs au début de toto[] ?
Et comme tu ne les as pas mis, le C ne les a pas inventés pour toi !
Voici un exemple correct, et sans "magic numbers", en "plain C"
Ca illustre quelques bonne pratiques :
- jamais de "nombre en dur" (magic numbers) dans tes programmes (0 et 1 sont tolérés), mais toujours définir ce que représente ton nombre.
- ne pas oublier que C ne vérifie pas les "débordements"... si tu ne lui précises pas, d'où l'usage de strncpy qui s'assure que la chaîne copiée rentrera bien dans le buffer et ne va pas provoquer un gros crash !
- quand les choses sont des constantes (comme "toto" dans str ci-dessous), bien le préciser, ainsi le compilateur va pouvoir faire de saines optimisations.
- ici on lui dit aussi que str est local à ce fichier (static) et ne sera pas utilisé par un autre programme qu'on linkerait avec notre source. Ça permet encore d'autres optimisation !
- on commente toujours en anglais (pas besoin de faire du Shakespeare !), et on met des noms de variables en anglais. Ça aide quand tu veux partager les choses puisque c'est la langue internationale !
#include <stdio.h>
#include <string.h>
#define BUFSIZE 256
#define OFFSET 4
static const char str[]="toto";
int main()
{
char buf[BUFSIZE];
memset(buf, ' ', BUFSIZE); /* Now buf has spaces */
strncpy(buf + OFFSET, str, BUFSIZE - OFFSET - 1); /* Moves str at buf + OFFSET, not overflowing our buffer, and accounting for the terminating \0 */
printf("%s\n", buf);
return 0;
}
Tu compiles avec
gcc test.c -o test
Tu exécutes :
./test
Résultat :
$ ./test
toto
Dernière modification par Zakhar (Le 03/11/2013, à 15:32)
"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)
Hors ligne
#5 Le 03/11/2013, à 14:37
- darunia_goron
Re : C/C++ décalage de pointeur
@Zakhar : lorsque tu utilises strncpy, tu dois t'assurer que la chaîne se termine bien par '\0'. Dans ton exemple, si on change BUFSIZE à une valeur inférieure ou égale à 8, toto ne se terminera pas par '\0' et le printf de la ligne suivante fera un dépassement de tampon.
La fonction strncpy() est identique [à strcpy()], sauf que seuls les n premiers octets de src sont copiés. Ainsi, s'il n'y a pas d'octet nul dans les n premiers octets de src, la chaîne résultante ne disposera pas d'octet nul final.
Dernière modification par darunia_goron (Le 03/11/2013, à 14:43)
Hors ligne
#6 Le 03/11/2013, à 15:31
- Zakhar
Re : C/C++ décalage de pointeur
Très juste !
Il faut donc faire :
strncpy(buf + OFFSET, str, BUFSIZE - OFFSET - 1);
Bug corrigé et commenté !
Il faut noter aussi que ce n'est pas totalement "idiot proof" puisque si celui qui programme met des valeurs à BUFFSIZE et OFFSET de sorte que la soustraction soit négative... le résultat sera... crash !
Donc si on veut aussi se protéger d'un programmeur "idiot", il faut protéger ce cas par un test conditionnel, et ça donne alors :
#include <stdio.h>
#include <string.h>
#ifndef BUFSIZE
#define BUFSIZE 256
#endif
#ifndef OFFSET
#define OFFSET 4
#endif
static const char str[]="toto";
int main()
{
char buf[BUFSIZE];
memset(buf, ' ', BUFSIZE - 1); /* Now buf has spaces */
buf[BUFSIZE - 1]= '\0';
#if BUFSIZE > OFFSET -1 /* This protect from an impossible copy */
strncpy(buf + OFFSET, str, BUFSIZE - OFFSET - 1); /* Moves str at buf + OFFSET, not overflowing our buffer, and accounting for the terminating \0 */
printf("%s\n", buf);
return 0;
#else
printf("The programmer is very stupid and set a buffer smaller than the offset.\n");
printf("The result would be only spaces... useless!\n");
return 1;
#endif
}
Avec la compilation conditionnelle, le programme supporte maintenant qu'on lui passe les valeurs de BUFSIZE et OFFSET à la compilation.
Pour vérifier que le programme nous insulte correctement, on peut faire par exemple:
gcc test.c -o test -DBUFSIZE=10 -DOFFSET=20
$ ./test
The programmer is very stupid and set a buffer smaller than the offset.
The result would be only spaces... useless!
(Alternativement, on pourrait laisser sans le \0 et utiliser snprintf, mais ce serait un "piège" lors de la maintenance car on peut s'attendre à ce que la chaîne soit terminée par un 0)
Dernière modification par Zakhar (Le 03/11/2013, à 15:52)
"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)
Hors ligne
#7 Le 03/11/2013, à 16:02
- darunia_goron
Re : C/C++ décalage de pointeur
Pas exactement. La fonction strncpy ne fera pas de dépassement de tampon. C'est printf qui est le coupable :
#include <stdio.h>
#include <string.h>
#define BUFSIZE 256
#define OFFSET 252
static const char str[]="toto";
int main()
{
char foo[] = "Un depassement de tampon sauvage apparait !";
char buf[BUFSIZE];
memset(buf, ' ', BUFSIZE); /* Now buf has spaces */
strncpy(buf + OFFSET, str, BUFSIZE - OFFSET - 1); /* Moves str at buf + OFFSET, not overflowing our buffer, and accounting for the terminating \0 */
printf("%s\n", buf);
return 0;
}
Dans le code précédent,
strncpy(buf + OFFSET, str, BUFSIZE - OFFSET);
était correct.
Le problème est que le tampon est initialisé avec des espaces. C'est donc à nous de remplir le dernier octet.
strncpy(buf + OFFSET, str, BUFSIZE - OFFSET);
buf[BUFSIZE - 1] = '\0' /* Garantee that buf is null terminated. */
Dernière modification par darunia_goron (Le 03/11/2013, à 16:26)
Hors ligne
#8 Le 03/11/2013, à 16:17
- darunia_goron
Re : C/C++ décalage de pointeur
Je n'avais pas vu ton EDIT lorsque j'ai rédigé mon précédent message.
Pour la vérification des macros, je préfère utiliser la directive #error :
#ifndef BUFSIZE
# define BUFSIZE 256
#endif
#ifndef OFFSET
# define OFFSET 4
#endif
#if OFFSET > BUFSIZE - 1
# error Problem Exists Between Chair and Keyboard !
#endif
Il y a deux avantages à cela :
le programmeur est informé de l'erreur à la compilation et non à l'exécution.
le code est plus lisible (typiquement, ce bout de code irait dans un .h). À mon goût, ce genre de test n'ont pas leur place dans une fonction.
Hors ligne
#9 Le 04/11/2013, à 00:32
- Zakhar
Re : C/C++ décalage de pointeur
Tu as tout à fait raison, c'est bien mieux à la compilation.
Il y a juste un tout petit ennui à ça, c'est que #error et son copain #warning sont des extensions GCC.
Donc potentiellement, si tu veux du code ANSI (genre gcc avec options -ansi -pedantic) ton code ne compilera pas ou donnera des warnings qui peuvent faire "peur" à un éventuel mainteneur.
Je suis d'ailleurs en train d'enlever les #warning informatifs (du genre : attention, je fais des optmisations Little Endian!) de mon parser JSON exprès pour ne pas violer l'ansi.
Après, on peut aussi se fiche de l'ansi... après tout on est sur un forum linux, et la plupart utilisent GCC ou CLANG qui a de toute façon repris les mêmes options et extensions (à peu près !)
Mais en fait tout ça nous éloigne du sujet original qui était la compréhension des pointeurs en C... qui est en soi un vaste sujet.
Dernière modification par Zakhar (Le 04/11/2013, à 00:36)
"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)
Hors ligne
#10 Le 04/11/2013, à 07:06
- grim7reaper
Re : C/C++ décalage de pointeur
Il y a juste un tout petit ennui à ça, c'est que #error et son copain #warning sont des extensions GCC.
Non.
Du moins pas pour #error, #error est tout ce qu’il y a de plus standard.
Par contre ouais, #warning est une extension.
Hors ligne
#11 Le 04/11/2013, à 22:57
- Zakhar
Re : C/C++ décalage de pointeur
Au temps pour moi... comme quoi l'ANSI n'est pas très logique.
"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)
Hors ligne
#12 Le 04/11/2013, à 23:19
- darunia_goron
Re : C/C++ décalage de pointeur
Je ne savais pas que #warning n'était pas AINSI. J'aurais appris quelque chose. Personnellement, je suis disciple du « pas de warning, que des erreurs » (genre compiler avec -Werror). J'ai vu trop de laxisme sur les warnings et quand on a à faire à un projet avec des centaines de warnings, il devient laborieux de trier ce qui est pertinent de ce qui ne l'est pas.
Hors ligne
Pages : 1