allocation dynamique

Le
Benoit Izac
Bonjour,

Soit le bout de code suivant :
--
#include <stdio.h>

#define SIZE 256

int main(void)
{
char *dirname = "/sys/class/power_supply";
char *type = "BAT";
int n = 1;
char *basename = "status";
char filename[SIZE];

if (snprintf(filename, SIZE, "%s/%s%d/%s", dirname, type, n,
basename)) {
/* on utilise filename */
} else {
/* on gere l'erreur */
}

return 0;
}
--

Je souhaiterai me passer de SIZE et donc allouer dynamiquement filename.

J'ai pensé à faire ceci :
--
char *filename;
/* size_of(n) renvoie le nombre de caractère(s) nécessaire(s) pour
représenter n en décimal */
size_t size = strlen(dirname) + 1 + strlen(type) + 1 + size_of(n) + 1 +
strlen(basename) + 1;
filename = malloc(size);
if (filename) {
if (snprintf(filename, size, "%s/%s%d/%s", dirname, type, n,
basename)) {
/* */
}
free(filename);
}
--

Est-ce la seule manière de procéder ?

NB: j'ai bien pensé à regarder comment asprintf (GNU_SOURCE) faisait
mais j'y comprend rien. ;)
--
Benoit Izac
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 2
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Richard Delorme
Le #18900061
Benoit Izac a écrit :

J'ai pensé à faire ceci :
-----------------------------------------------------------------------
char *filename;
/* size_of(n) renvoie le nombre de caractère(s) nécessaire(s) pour
représenter n en décimal */
size_t size = strlen(dirname) + 1 + strlen(type) + 1 + size_of(n) + 1 +
strlen(basename) + 1;
filename = malloc(size);
if (filename) {
if (snprintf(filename, size, "%s/%s%d/%s", dirname, type, n,
basename)) {
/* ... */
}
free(filename);
}



Selon la norme du C99, on doit pouvoir faire :

size_t size = snprintf(NULL, 0, "%s/%s%d/%s", dirname, type, n,

basename) + 1;
filename = malloc(size);
if (filename) {
snprintf(filename, size, "%s/%s%d/%s", dirname, type, n,

basename);
/* ... */

}


Est-ce la seule manière de procéder ?


Non.

--
Richard
Eric Levenez
Le #18900051
Le 14/03/09 14:57, dans
if (snprintf(filename, SIZE, "%s/%s%d/%s", dirname, type, n,
basename)) {
Je souhaiterai me passer de SIZE et donc allouer dynamiquement filename.



Le plus simple est de demander à snprintf la taille requise avant de faire
un malloc et de refaire le snprintf dans le buffer alloué :

s = snprintf(NULL, 0, "%s/%s%d/%s", dirname, type, n, basename);
p = malloc(s + 1);
snprintf(p, s + 1, "%s/%s%d/%s", dirname, type, n, basename);

Il faut tester les codes de retour, bien sûr; et il faut une bibliothèque C
conforme.

--
Éric Lévénez
FAQ de fclc :
Xavier Roche
Le #18900231
Eric Levenez a écrit :
Le plus simple est de demander à snprintf la taille requise avant de faire
un malloc et de refaire le snprintf dans le buffer alloué :



Il y a aussi une extension GNU (non POSIX) qui alloue ce qu'il faut
(asprintf)

Celle-ci peut être facilement réécrite avec des fonctions POSIX en
utilisant vsnprintf() et une va_list de la même manière, pour calculer
la taille voulue du buffer.

Benoit Izac
Le #18900291
Bonjour,

le 14/03/2009 à 15:09, Richard Delorme a écrit dans le message

size_t size = snprintf(NULL, 0, "%s/%s%d/%s", dirname, type, n,
basename) + 1;



Merci à tous les deux. Ça m'apprendra à ne pas lire la documentation
dans son intégralité. De plus je viens de réaliser pourquoi gcc me
renvoyait un « warning: implicit declaration of function snprintf »
quand j'utilisais -ansi.

D'où ma question, comment faisait-on avant C99 (avec sprintf() donc) ?

--
Benoit Izac
Benoit Izac
Le #18900541
Dans le message écrit :

if (snprintf(filename, SIZE, "%s/%s%d/%s", dirname, type, n,
basename)) {



Je viens de m'apercevoir que mon truc est complètement faux. Ça devrait
être :

if (snprintf(filename, SIZE, "%s/%s%d/%s", dirname, type, n,
basename) < SIZE)
/* ok */

--
Benoit Izac
espie
Le #18900531
In article
Bonjour,

le 14/03/2009 à 15:09, Richard Delorme a écrit dans le message

size_t size = snprintf(NULL, 0, "%s/%s%d/%s", dirname, type, n,
basename) + 1;



Merci à tous les deux. Ça m'apprendra à ne pas lire la documentation
dans son intégralité. De plus je viens de réaliser pourquoi gcc me
renvoyait un « warning: implicit declaration of function snprintf »
quand j'utilisais -ansi.

D'où ma question, comment faisait-on avant C99 (avec sprintf() donc) ?



Ben on reimplementait snprintf a la main. Il y en a des paquets
d'implementation dans la nature.

Il y a plusieurs choses dans la bibliotheque standard:
- des trucs indispensables, impossibles a implementer de facon portable (comme
malloc ou fputc).
- des fonctions qui sont surtout la pour etre pratiques, et qu'on peut tres
bien reimplementer soi-meme si on veut (mais souvent pas aussi efficacement
que la bibliotheque du systeme, qui peut se permettre certains raccourcis pas
du tout portables). snprintf en fait partie.
Xavier Roche
Le #18900521
Benoit Izac a écrit :
D'où ma question, comment faisait-on avant C99 (avec sprintf() donc) ?



Comme on pouvait.

(1) on allouait un buffer "assez grand"
(ce qui a fait les beaux jours des vulnérabilités par dépassement de
tampon sur sprintf(), strcpy(), etc. -- on en voit toujours de nos
jours, à ce propos :p)

(2) on allouait "proprement" en calculant la taille des arguments de
type "chaine"

La solution (1) était très souvent la plus courante.
Alexandre BACQUART
Le #18901871
Benoit Izac wrote:
Bonjour,

Soit le bout de code suivant :
-----------------------------------------------------------------------
#include
#define SIZE 256

int main(void)
{
char *dirname = "/sys/class/power_supply";
char *type = "BAT";
int n = 1;
char *basename = "status";
char filename[SIZE];

if (snprintf(filename, SIZE, "%s/%s%d/%s", dirname, type, n,
basename)) {
/* on utilise filename */
} else {
/* on gere l'erreur */
}

return 0;
}
-----------------------------------------------------------------------

Je souhaiterai me passer de SIZE et donc allouer dynamiquement filename.


>
J'ai pensé à faire ceci :
-----------------------------------------------------------------------
char *filename;
/* size_of(n) renvoie le nombre de caractère(s) nécessaire(s) pour
représenter n en décimal */
size_t size = strlen(dirname) + 1 + strlen(type) + 1 + size_of(n) + 1 +
strlen(basename) + 1;
filename = malloc(size);
if (filename) {
if (snprintf(filename, size, "%s/%s%d/%s", dirname, type, n,
basename)) {
/* ... */
}
free(filename);
}
-----------------------------------------------------------------------

Est-ce la seule manière de procéder ?



Il y a FILENAME_MAX dans l'implémentation pour les noms de fichiers (la norme va à peine plus
loin : s'il n'y a pratiquement aucune limite, c'est la taille
*recommandée* à allouer pour contenir les noms de fichiers).

Cela me paraît donc très adapté pour allouer des chaînes dans lesquelles
on va construire un chemin complet vers un fichier. Par contre, préférer
un 'malloc' à une variable automatique (j'ai vu des FILENAME_MAX allant
jusqu'à 4096).

Ensuite, si ton souhait est de stocker ces chemins de manière compacte,
le mieux est de copier cette chaîne (que tu vas libérer de toutes
façons) dans une autre, mais cette fois tu connais la taille minimum à
allouer : strlen(filename)+1 (ou mieux dans ton cas, le retour de ton
snprintf(...)+1).

NB: j'ai bien pensé à regarder comment asprintf (GNU_SOURCE) faisait
mais j'y comprend rien. ;)



Pas standard.


--
Alex
Benoit Izac
Le #18902271
Bonjour,

le 14/03/2009 à 19:03, Alexandre BACQUART a écrit dans le message

Ensuite, si ton souhait est de stocker ces chemins de manière
compacte, le mieux est de copier cette chaîne (que tu vas libérer de
toutes façons) dans une autre, mais cette fois tu connais la taille
minimum à allouer : strlen(filename)+1 (ou mieux dans ton cas, le
retour de ton snprintf(...)+1).



J'ai juste donné l'exemple que j'avais en tête. Le but était d'avoir une
réponse générique qui me permette de connaître la taille exacte
à allouer pour stocker cette chaîne. snprintf(NULL, 0, ...) remplit
parfaitement ce rôle. Par contre, je ne comprends pas l'intérêt de la
copie dans une autre chaîne.

--
Benoit Izac
Eric Levenez
Le #18902261
Le 14/03/09 19:03, dans BACQUART »
Il y a FILENAME_MAX dans l'implémentation pour les noms de fichiers (la norme va à peine plus
loin : s'il n'y a pratiquement aucune limite, c'est la taille
*recommandée* à allouer pour contenir les noms de fichiers).



Attention, FILENAME_MAX est la taille maximum d'un nom de fichier, alors que
là on veut allouer le chemin complet vers ce fichier. Là la norme C ne dit
rien vue que la notion de répertoire y est absente. Il y a bien PATH_MAX qui
est Posix mais il est parfois faux. Le mieux est donc de ne pas supposer la
taille maximale.

--
Éric Lévénez
FAQ de fclc :
Publicité
Poster une réponse
Anonyme