#1 Le 23/08/2012, à 16:03
- ludooo923
[RESOLU][Programmation GCC] - Problème avec malloc
Bonjour,
J'ai un problème avec malloc, quelque soit la taille que je veux réserver, mon petit bout de code me retourne toujours la valeur 8.
Où est mon erreur, est-ce que je peut voir la taille du buffer avec cet méthode?
Code :
#include <stdio.h>
#include <stdlib.h>
int main (void)
{
char *buffer;
buffer = (char *) malloc (100);
printf("Taille du buffer : %ld\n",sizeof(buffer));
return 0;
}
Résultat :
Taille du buffer : 8
Peu importe le nombre entre parenthèses après malloc, le résultat est toujours le même.
Merci pour votre aide.
Dernière modification par ludooo923 (Le 23/08/2012, à 20:14)
Hors ligne
#2 Le 23/08/2012, à 16:18
- grim7reaper
Re : [RESOLU][Programmation GCC] - Problème avec malloc
Salut,
sizeof renvoie la taille de son opérande. Ici l’opérande est buffer, buffer est un pointeur donc il te renvoie 8 (tu dois être en 64 bits donc ). Tout est normal.
Et au cas où, je précise que tu ne peux pas récupérer (de manière portable en tout cas) la taille de ce que tu as alloué à partir du pointeur. Si tu veux garder la taille, il faut la stocker quelque part.
Dernière modification par grim7reaper (Le 23/08/2012, à 16:19)
Hors ligne
#3 Le 23/08/2012, à 16:22
- ludooo923
Re : [RESOLU][Programmation GCC] - Problème avec malloc
Merci pour l'info.
Je clôture.
Désolé, mais je reviens.
Comment faire pour connaître la taille de ce qui est enregistré dans le buffer afin de l'écrire dans un fichier avec fwrite.
fwrite(buffer,sizeof(char),sizeof(buffer),pFichier);
Dernière modification par ludooo923 (Le 23/08/2012, à 16:32)
Hors ligne
#4 Le 23/08/2012, à 16:40
- grim7reaper
Re : [RESOLU][Programmation GCC] - Problème avec malloc
D’où il vient le buffer ?
Si c’est toi qui a fait l’allocation dynamique, tu connais la taille.
Sinon, ça dépends comment tu récupères ce buffer.
Hors ligne
#5 Le 23/08/2012, à 16:51
- ludooo923
Re : [RESOLU][Programmation GCC] - Problème avec malloc
Dans l'idéal, ça serait un texte entré au clavier dont on ne connaît pas la taille. Celui-ci serait mis dans un buffer alloué dynamiquement, afin de l'écrire dans un fichier.
#include <stdio.h>
#include <stdlib.h>
int long_chaine (char *chaine)
{
int i;
for(i=0;i<=128;i++)
{
if (chaine[i] == '\n')
{
return i;
}
}
return 0;
}
int main ()
{
FILE * pFichier;
long longueur;
char * buffer;
char bufferE[128];
int i;
fgetc(stdin);
printf("Entrez le texte à écrire :");
fgets(bufferE,sizeof (bufferE),stdin);
longueur = long_chaine(bufferE);
buffer = (char*) malloc (sizeof(char)*longueur);
if (buffer == NULL) {fputs("Erreur mémoire\n",stderr);exit(1);}
i=0;
while(bufferE[i] != '\n')
{
buffer[i] = bufferE[i];
i++;
}
buffer[i+1] = '\n';
//
printf("buffer : %s\n",buffer);
//
pFichier = fopen("Key","w");
if (pFichier == NULL) {fputs ("Erreur ouverture fichier\n",stderr);exit(2);}
fwrite(buffer,sizeof(char),longueur,pFichier);
fclose(pFichier);
free (buffer);
}
Comme tu peut le voir c'est pas encore ça.
Merci
Hors ligne
#6 Le 23/08/2012, à 17:04
- pingouinux
Re : [RESOLU][Programmation GCC] - Problème avec malloc
Bonjour,
Quelques remarques :
Un chaîne se termine par \0, et non \n
Pour en calculer la longueur :
size_t strlen(const char *s);
Pour la copier :
char *strcpy(char *dest, const char *src); char *strncpy(char *dest, const char *src, size_t n);
Hors ligne
#7 Le 23/08/2012, à 17:13
- ludooo923
Re : [RESOLU][Programmation GCC] - Problème avec malloc
Merci pour le tuyau, en effet ça raccourci.
Sinon une idée pour récupérer de façon dynamique le texte entré au clavier?
Hors ligne
#8 Le 23/08/2012, à 18:36
- Tycho Brahe
Re : [RESOLU][Programmation GCC] - Problème avec malloc
Salut,
Voici une version plus propre et surtout plus safe de ce que tu as fais :
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
#define BUFF_SIZE 1024
#define OUT_FILENAME "Key"
int
main(void)
{
char buff[BUFF_SIZE];
size_t buff_sz, write_sz;
FILE *out;
bzero(buff, sizeof(buff));
fgets(buff, sizeof(buff), stdin);
out = fopen(OUT_FILENAME, "w");
if (out == NULL)
err(EXIT_FAILURE, OUT_FILENAME);
buff_sz = strlen(buff);
write_sz = fwrite(buff, sizeof(*buff), buff_sz, out);
if (write_sz != buff_sz)
warn(OUT_FILENAME);
fclose(out);
return EXIT_SUCCESS;
}
Sinon, qu'est-ce que tu entends exactement par "récupérer de façon dynamique le texte entré au clavier" ?
Loi de Newton :
Si tu restes à glander sous le pommier, tu pourrais bien prendre une pomme sur la gueule.
Hors ligne
#9 Le 23/08/2012, à 18:44
- ludooo923
Re : [RESOLU][Programmation GCC] - Problème avec malloc
Merci pour ton aide. Ce que j’entends, c'est que l'on ne connaît pas à l'avance la taille du texte entré, dans ton code tu donne une taille de 1024 à
la variable buff, or si le texte est plus grand tu ne récupère que les 1024 premiers caractères. Je voulais juste savoir s'il était possible d'alloué dynamiquement la taille de la variable buff.
Hors ligne
#10 Le 23/08/2012, à 19:28
- pingouinux
Re : [RESOLU][Programmation GCC] - Problème avec malloc
Je te propose ceci, qui est une adaptation du code de Tycho Brahe #8. Je lis l'entrée caractère par caractère, et réalloue le buffer quand il est plein.
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
#define BUFF_SIZE 1024
#define OUT_FILENAME "Key"
int
main(void)
{
//char buff[BUFF_SIZE];
size_t buff_sz, write_sz;
FILE *out;
// Ajouté
int p=0, l_buff=BUFF_SIZE;
char *buff=(char*)malloc(l_buff*sizeof(*buff));
char c;
// ######
//bzero(buff, sizeof(buff));
//fgets(buff, sizeof(buff), stdin);
// Ajouté
if (buff == NULL) { fprintf(stderr,"Erreur malloc\n"); exit(1); }
while( (c=fgetc(stdin)) != EOF ) {
if(p>=l_buff) {
l_buff+=BUFF_SIZE;
buff=(char*)realloc(buff,l_buff*sizeof(*buff));
if (buff == NULL) { fprintf(stderr,"Erreur realloc\n"); exit(1); }
}
buff[p++]=c;
}
buff[p]=0;
// ######
out = fopen(OUT_FILENAME, "w");
if (out == NULL)
err(EXIT_FAILURE, OUT_FILENAME);
buff_sz = strlen(buff);
write_sz = fwrite(buff, sizeof(*buff), buff_sz, out);
if (write_sz != buff_sz)
warn(OUT_FILENAME);
fclose(out);
// Ajouté
free(buff);
// ######
return EXIT_SUCCESS;
}
Corrigé suite aux remarques en #12 et #13
Dernière modification par pingouinux (Le 24/08/2012, à 08:30)
Hors ligne
#11 Le 23/08/2012, à 20:13
- ludooo923
Re : [RESOLU][Programmation GCC] - Problème avec malloc
Pas mal le coup du caractère par caractère et je réalloue ! En tout cas ça à l'air de fonctionner.
Merci
Hors ligne
#12 Le 23/08/2012, à 22:44
- Tycho Brahe
Re : [RESOLU][Programmation GCC] - Problème avec malloc
pingouinux : félicitations, tu viens exactement d'illustrer le pourquoi on préfère avoir des buffers de tailles fixe et éviter de faire joujou avec malloc et dérivés quand on le peut. Lance ton programme, appuie sur ctrl + D... et hop tu part direct en segfault ! De plus, valgrind me rapporte une fuite mémoire.
Bref, est-ce qu'avoir ce caractère "illimité" est vraiment indispensable ? En général non. Juste pour faire la remarque... une commande shell à un nombre d'arguments limités, vous en étiez-vous rendu compte ? Si votre réponse à cette question est non, alors vous devriez comprendre que ce modèle du buffer de taille fixe est viable et que, si ça permet d'éviter de graves erreurs dans les programmes, c'est une solution à privilégier
Loi de Newton :
Si tu restes à glander sous le pommier, tu pourrais bien prendre une pomme sur la gueule.
Hors ligne
#13 Le 24/08/2012, à 03:06
- grim7reaper
Re : [RESOLU][Programmation GCC] - Problème avec malloc
@Tycho Brahe : ton programme est pas mal mais il utilises des trucs pas standard (extension BSD telle que err et warn), voire carrément dépréciée (comme bzero)
4.3BSD. This function is deprecated (marked as LEGACY in POSIX.1-2001): use memset(3) in new programs. POSIX.1-2008 removes the specification of bzero().
Ça me fait un peu tiquer. Quitte à utiliser des extensions du C, alors autant faire à appel à getline (GNU C). D’autant plus que getline est passé dans POSIX.
Both getline() and getdelim() were originally GNU extensions. They were standardized in POSIX.1-2008.
@pingouinux : comme Tycho Brache te l’as signalé, ton programme a une fuite de mémoire (manque un free à la fin). J’ajouterais aussi que tu utilise mal realloc (mais c’est une erreur très courante).
Tu fais :
buff=(char*)realloc(buff,l_buff*sizeof(*buff));
Bien, et si maintenant realloc échoue :
The realloc() function returns a pointer to the newly allocated memory, which is suitably aligned for any kind of variable and may be different from ptr, or NULL if the request fails. If size was equal to 0, either NULL or a pointer suitable to be passed to free() is returned. If realloc() fails the original block is left untouched; it is not freed or moved.
(le gras est de moi).
Tu as aussi une fuite de mémoire : realloc renvoie NULL, donc buff contient maintenant NULL mais la mémoire n’est pas libéré et tu n’as plus l’adresse (écrasée par le retour de realloc).
@ludooo923 : voilà ma solution, avec ça tu peux lire une chaîne de taille arbitraire venant du clavier (Remarque : tu peux aussi lire un fichier texte avec le code actuel, il suffit de remplacer stdin par un FILE* qui va bien). Et la mémoire est libéré dans tout les cas (OK ou erreur). Ça pourrait sûrement encore être amélioré, en lisant par bloc ou par ligne (via getline éventuellement) au lieu de caractère par caractère (même si l’OS optimise un peu tout ça lui-même, ça ne fait pas tout).
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#define OUT_FILENAME "Key"
size_t read_text(FILE* fp, char** buff);
char* realloc_buffer(char** buff, size_t* size);
int main(void)
{
char* buff;
size_t buff_sz, write_sz;
FILE *out;
/* Read text. */
buff_sz = read_text(stdin, &buff);
if(buff_sz == 0)
goto failure;
/* Open output file. */
out = fopen(OUT_FILENAME, "w");
if(out == NULL)
{
perror("fopen()");
goto open_failure;
}
/* Write text. */
write_sz = fwrite(buff, sizeof(*buff), buff_sz, out);
if(write_sz != buff_sz)
{
perror("fwrite()");
goto write_failure;
}
/* Free memory. */
free(buff);
fclose(out);
return EXIT_SUCCESS;
/* Error handling. */
write_failure:
fclose(out);
open_failure:
free(buff);
failure:
return EXIT_FAILURE;
}
size_t read_text(FILE* fp, char** buff)
{
size_t size = BUFSIZ;
size_t i = 0;
int c;
/* Buffer allocation. */
*buff = malloc(size);
if(buff == NULL)
{
errno = ENOMEM;
perror("malloc()");
goto failure;
}
/* Read text. */
while((c = fgetc(fp)) != EOF)
{
if(i == size - 1)
if(realloc_buffer(buff, &size) == NULL)
{
errno = ENOMEM;
perror("realloc()");
goto mem_failure;
}
(*buff)[i] = (char)c;
++i;
}
/* Check: normal end or read error? */
if(feof(fp))
(*buff)[i] = '\0';
else
{
fputs("error fgetc failed", stderr);
goto mem_failure;
}
return i;
/* Error handling. */
mem_failure:
free(*buff);
*buff = NULL;
failure:
return 0;
}
char* realloc_buffer(char** buff, size_t* size)
{
static const size_t max_size = ~0u;
size_t new_size;
char* tmp;
/* Check for overflow. */
assert(max_size / 1.5 > *size);
/* Reallocation. */
new_size = (size_t)(*size * 1.5);
tmp = realloc(*buff, new_size);
/* Check reallocation. */
if(tmp != NULL)
{
*buff = tmp;
*size = new_size;
}
return tmp;
}
NB : les plus obsersvateurs remarqueront que j’utile BUFSIZ pour la taille de base du buffer et que je ne le définie pas. En fait, c’est une constante définie dans stdio.h
The value of this macro is an integer constant expression that is good to use for the size argument to setvbuf. This value is guaranteed to be at least 256.
The value of BUFSIZ is chosen on each system so as to make stream I/O efficient. So it is a good idea to use BUFSIZ as the size for the buffer when you call setvbuf.
Actually, you can get an even better value to use for the buffer size by means of the fstat system call: it is found in the st_blksize field of the file attributes. See Attribute Meanings.
Sometimes people also use BUFSIZ as the allocation size of buffers used for related purposes, such as strings used to receive a line of input with fgets (see Character Input). There is no particular reason to use BUFSIZ for this instead of any other integer, except that it might lead to doing I/O in chunks of an efficient size.
(le gras est de moi).
Bon ici c’est pas super utile car on lit caractère par caractère, mais dans l’optique de modifier le code pour lire bloc par bloc au moins la taille est déjà prête dans le code
Dernière modification par grim7reaper (Le 24/08/2012, à 03:09)
Hors ligne
#14 Le 24/08/2012, à 05:34
- pingouinux
Re : [RESOLU][Programmation GCC] - Problème avec malloc
@ Tycho Brahe et grim7reaper :
Merci pour vos remarques tout-à-fait justifiées. Je n'avais notamment pas testé un Ctrl+D direct.
Hors ligne
#15 Le 24/08/2012, à 10:11
- Tycho Brahe
Re : [RESOLU][Programmation GCC] - Problème avec malloc
@Tycho Brahe : ton programme est pas mal mais il utilises des trucs pas standard (extension BSD telle que err et warn), voire carrément dépréciée (comme bzero)
J'avais jamais remarqué que bzero était déprécié, j'avais bêtement cru qu'en compilant avec -Wall -Wextra gss m'aurai prévenu dans ce genre de cas... j'avais tort ^^ Pour err et warn, je les trouve vraiment extra, comme d'autre extentions bsd, et vu qu'elles sont très répandues (contrairement à strlcpy par exemple :x) et franchement utiles, je n'hésite pas à les utiliser.
Quitte à utiliser des extensions du C, alors autant faire à appel à getline (GNU C). D’autant plus que getline est passé dans POSIX.
En effet, et je ne la connaissais pas celle là ! J'ai lu le man… et franchement elle est awesome ! L'allocation d'un buffer plus grand en cas de dépassement (et surtotu sa propre aloc de buffer) c'est très appréciable !
Allez, du coup je reprend mon code en suivant tes conseils :
#define _POSIX_C_SOURCE 200809L
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
#define OUT_FILENAME "Key"
int
main(void)
{
char *buff = NULL;
ssize_t buff_sz;
size_t write_sz, n;
FILE *out;
buff_sz = getline(&buff, &n, stdin);
if (buff_sz == -1)
{
if (buff != NULL)
free(buff);
err(EXIT_FAILURE, "stdin");
}
out = fopen(OUT_FILENAME, "w");
if (out == NULL)
{
if (buff != NULL)
free(buff);
err(EXIT_FAILURE, OUT_FILENAME);
}
write_sz = fwrite(buff, sizeof(*buff), buff_sz, out);
n = buff_sz;
if (write_sz != n)
warn(OUT_FILENAME);
free(buff);
fclose(out);
return EXIT_SUCCESS;
}
Quand je compare avec le tiens, je me dis que c'est vraiment du bon ces extensions
Loi de Newton :
Si tu restes à glander sous le pommier, tu pourrais bien prendre une pomme sur la gueule.
Hors ligne
#16 Le 24/08/2012, à 11:13
- grim7reaper
Re : [RESOLU][Programmation GCC] - Problème avec malloc
Quand je compare avec le tiens, je me dis que c'est vraiment du bon ces extensions
Yep
Après perso je prefère donner la solution la plus standard possible, et si des extensions ou des bibliothèques facilitent le boulot je peux les mentionner. Après, chacun compose selon ses contraintes
Hors ligne