Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

allocation dynamique

17 réponses
Avatar
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

10 réponses

1 2
Avatar
Richard Delorme
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
Avatar
Eric Levenez
Le 14/03/09 14:57, dans , « Benoit Izac »
a écrit :

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 : <http://www.levenez.com/lang/c/faq/>
Avatar
Xavier Roche
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.

<http://www.opengroup.org/onlinepubs/000095399/functions/vprintf.html>
<http://www.opengroup.org/onlinepubs/000095399/basedefs/stdarg.h.html>
Avatar
Benoit Izac
Bonjour,

le 14/03/2009 à 15:09, Richard Delorme a écrit dans le message
<49bbbb68$0$17776$ :

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
Avatar
Benoit Izac
Dans le message , le 14/03/2009 à 14:57, j'ai
é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
Avatar
espie
In article , Benoit Izac wrote:
Bonjour,

le 14/03/2009 à 15:09, Richard Delorme a écrit dans le message
<49bbbb68$0$17776$ :

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.
Avatar
Xavier Roche
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.
Avatar
Alexandre BACQUART
Benoit Izac wrote:
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 ?



Il y a FILENAME_MAX dans <stdio.h>. C'est la taille maximum garantie par
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
Avatar
Benoit Izac
Bonjour,

le 14/03/2009 à 19:03, Alexandre BACQUART a écrit dans le message
<49bbf156$0$3094$ :

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
Avatar
Eric Levenez
Le 14/03/09 19:03, dans <49bbf156$0$3094$, « Alexandre
BACQUART » a écrit :

Il y a FILENAME_MAX dans <stdio.h>. C'est la taille maximum garantie par
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 : <http://www.levenez.com/lang/c/faq/>
1 2