OVH Cloud OVH Cloud

allocation dynamique bis

17 réponses
Avatar
Benoit Izac
Bonjour,

J'ai compliqué un peu la chose en faisant de l'allocation dynamique pour
une entrée dont je ne connais pas la taille par avance et que je ne peux
pas calculer.

Ci-dessous le code, tous commentaires bien venus.
-------------------------------------------------------------------------
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum bat_states { UNKNOW, CHARGED, CHARGING, DISCHARGING };
int get_battery_state(int);

#define MALLOC_SIZE 16

int
get_battery_state(int n)
{
int ret = -1;
char *basedir = "/sys/class/power_supply";
char *type = "BAT";
char *basename = "status";
char *fn;
size_t fn_size;
FILE *fp;
int s,
i;
char *nlp; /* newline position */
char *buf; /* buffer for fgets */
size_t buf_size;
char *line; /* line read */
size_t line_size;
char *r; /* return of fgets */

/*
* generate filename
*/
s = snprintf(NULL, 0, "%s/%s%d/%s", basedir, type, n, basename) + 1;
if (s == -1) {
fprintf(stderr, "snprintf(NULL, 0, ...) failed\n");
return -1;
}
fn_size = s;
fn = malloc(fn_size);
if (!fn) {
perror("fn = malloc()");
return -1;
}
s = snprintf(fn, fn_size, "%s/%s%d/%s", basedir, type, n, basename);
if ((s == -1) || ((size_t) s > (fn_size - 1))) {
fprintf(stderr, "snprintf(fn, %d, ...) return %d\n",
(int) fn_size, s);
if (fn)
free(fn);
return -1;
}

/*
* open filename
*/
fp = fopen(fn, "r");
if (!fp) {
perror("fopen() failed");
free(fn);
return -1;
}
free(fn);


/*
* allocate space for buf
*/
buf = malloc(MALLOC_SIZE);
if (!buf) {
perror("buf = malloc()");
fclose(fp);
return -1;
}

/*
* initialisation
*/
line = NULL;
line_size = 0;
i = 0;
if (MALLOC_SIZE < 2) {
fprintf(stderr, "MALLOC_SIZE must be at least 2\n");
fclose(fp);
free(buf);
return -1;
}

/*
* read data
*/
while ((r = fgets(buf, MALLOC_SIZE, fp))) {
nlp = strchr(buf, '\n');
if (nlp)
*nlp = '\0';
/*
* add one byte for the first allocation to include '\0'
*/
line_size += (i == 0 ? 1 : 0);
buf_size = (nlp ? strlen(buf) : (MALLOC_SIZE - 1));
line_size += buf_size;
line = realloc(line, line_size);
if (!line) {
perror("realloc(line)");
fclose(fp);
free(buf);
return -1;
}
if (i == 0) /* line MUST have a '\0' */
*line = '\0';
i++;
strncat(line, buf, buf_size);
if (nlp)
break;
}
free(buf);

if (!r) { /* fgets return NULL */
if (feof(fp))
fprintf(stderr, "fgets receive EOF\n");
else if (ferror(fp))
perror("fgets()");
else
fprintf(stderr, "you should never see this message!");
if (line)
free(line);
fclose(fp);
return -1;
}
fclose(fp);

/*
* compare
*/
if (strcmp(line, "Full") == 0)
ret = CHARGED;
else if (strcmp(line, "Discharging") == 0)
ret = DISCHARGING;
else if (strcmp(line, "Charging") == 0)
ret = CHARGING;
else {
fprintf(stderr, "unknow battery state: %s\n", line);
ret = UNKNOW;
}
free(line);

return ret;
}
-------------------------------------------------------------------------

Petite série de questions au passage :
- comment faites vous pour être sûr que les allocations soient bien
libérées ?
- peut-on remplacer la série if, else if, etc. de strcmp() par un
switch ?
- ce code me parrait bien compliqué pour faire peu de chose, peut-on
le simplifier (tout en gardant la gestion des erreurs) ?

J'ai bien essayé de faire un point de sortie unique de la fonction mais
le code se retrouve inexorablement collé à droite de l'écran (et
j'utilise des indentations de quatre espaces).

--
Benoit Izac

7 réponses

1 2
Avatar
Benoit Izac
Bonjour,

le 15/03/2009 à 12:26, -ed- a écrit dans
le message
:

    - comment faites vous pour être sûr que les allocations soient bien
    libérées ?



J'utilise un 'traqueur de mémoire personnel'

http://mapage.noos.fr/emdel/clib.htm
module SYSALLOC

mais c'est surtout en respectant les principes de la programmation
structurée (un seul return par fonction, par exemple), que le code est
'naturellement correct'...



Je l'ai bien compris, disons que j'ai juste du mal à le mettre en place.

    - peut-on remplacer la série if, else if, etc. de strcmp() par un
    switch ?



Non. L'usage général est if-else if. switch-case est limité aux
entiers.



Je sais bien mais je me demandais s'il n'y avait pas une méthode pour
faire renvoyer un entier en fonction de ce qui est contenu dans
« line ». Je trouve un switch plus lisible que des if-else.

    - ce code me parrait bien compliqué pour faire peu de chose, peut-on
    le simplifier (tout en gardant la gestion des erreurs) ?



On peut surtout mieux le structurer. Il faut aussi séparer le lecture
des traitements...



Je ne comprends pas ces deux réponses, pourrais-tu donner un exemple ?

J'ai bien essayé de faire un point de sortie unique de la fonction mais
le code se retrouve inexorablement collé à droite de l'écran (et
j'utilise des indentations de quatre espaces).



Utilise 3 espaces. Si ça déborde encore, c'est qu'il n'y a pas assez
de découpage en fonctions...



J'aime bien mes quatre espaces.

--
Benoit Izac
Avatar
Antoine Leca
Le 15/03/2009 12:13Z, Xavier Roche écrivit :
Xavier Roche a écrit :
En effet, les chaînes sont en général constantes (*)



J'ai oublié le (*): de mémoire, le standard impose que les chaînes
constantes soient.. constantes.



Je ne sais pas comment interpréter cela.

Cependant, à propos de la norme, celle-ci explicite que les littéraux
chaînes peuvent être (au choix de l'implémentation) traités comme const
(=readonly) ou traités comme modifiables.

Pour celui qui écrit des programmes portables, cela signifie donc qu'il
faut considérer les littéraux chaînes comme const, donc interdit d'y écrire.
Pour celui qui fait de l'adaptation, celui signifie que si le programme
plante quand il écrit dans une chaîne (ou si on est averti de cette
éventualité), on peut chercher si le compilateur possède une option qui
autorise ce genre de manipulation (avec en contre-partie un moindre
niveau d'optimisation); sinon il faut réécrire, en général cela
représente une copie de chaîne supplémentaire.


Antoine
Avatar
espie
In article <gpiui1$378$,
Antoine Leca wrote:
Pour celui qui fait de l'adaptation, celui signifie que si le programme
plante quand il écrit dans une chaîne (ou si on est averti de cette
éventualité), on peut chercher si le compilateur possède une option qui
autorise ce genre de manipulation (avec en contre-partie un moindre
niveau d'optimisation); sinon il faut réécrire, en général cela
représente une copie de chaîne supplémentaire.



De memoire, gcc propose un -fwrite-strings jusqu'a 2.95, et cette option a
ensuite disparu...

L'immense majorite des programmes qui avaient besoin de cette option ont
ete modifies pour ne plus en avoir besoin. Ca n'est generalement pas trop
complique, sauf tentative d'IOCCC manifeste.
Avatar
Xavier Roche
Antoine Leca a écrit :
Je ne sais pas comment interpréter cela.
Cependant, à propos de la norme, celle-ci explicite que les littéraux
chaînes peuvent être (au choix de l'implémentation) traités comme const
(=readonly) ou traités comme modifiables.



Ah, damned - ca serait en C++ que la restriction aurait été introduite ?
Avatar
Benoit Izac
Bonjour,

le 15/03/2009 à 12:37, Xavier Roche a écrit dans le message
<gpip9j$nne$ :

Cela pourrait être encapsulé de manière plus zen:
[...]



J'ai testé mais ça coredump sévère :

% cat test.c
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>

static char *my_sprintf( char *format, ...);

int main(void)
{
const char *a ="a";
char *const s = my_sprintf("%s", a);
printf("%sn", s);
return 0;
}

static char* my_sprintf(char *format, ...) {
va_list args;
int len;
char *str;
va_start(args, format);
len = vsnprintf(NULL, 0, format, args);
if (len < 0) {
return NULL;
}
str = malloc(len + 1);
if (str == NULL) {
return NULL;
}
if (vsnprintf(str, len + 1, format, args) > len) {
free(str);
return NULL;
}
va_end(args);
return str;

}
% gcc -W -Wall -Wextra -O2 -pedantic test.c
% ./a.out
zsh: segmentation fault (core dumped) ./a.out

--
Benoit Izac
Avatar
Xavier Roche
Benoit Izac a écrit :
J'ai testé mais ça coredump sévère :



Hum, je ne vois pas d'erreur évidente dans le code. Ah, si: vsnprintf()
peut laisser "args" dans un état totalement incohérent.

<http://www.opengroup.org/onlinepubs/000095399/functions/vprintf.html>
"(..) As these functions invoke the va_arg macro, the value of ap after
the return is unspecified."

Je ne vois pas d'autre coquille pour le reste.

char *str;
va_start(args, format);
len = vsnprintf(NULL, 0, format, args);
+ va_end(args);
if (len < 0) {
return NULL;
}
@@ -25,7 +26,9 @@
if (str == NULL) {
return NULL;
}
+ va_start(args, format);
if (vsnprintf(str, len + 1, format, args) > len) {
+ va_end(args);
free(str);
return NULL;
}
Avatar
Benoit Izac
Bonjour,

le 15/03/2009 à 21:26, Xavier Roche a écrit dans le message
<gpjoa9$nne$ :

Hum, je ne vois pas d'erreur évidente dans le code. Ah, si:
vsnprintf() peut laisser "args" dans un état totalement incohérent.

<http://www.opengroup.org/onlinepubs/000095399/functions/vprintf.html>
"(..) As these functions invoke the va_arg macro, the value of ap
after the return is unspecified."



Bravo ! Tout s'explique, ça marche au poil maintenant.

--
Benoit Izac
1 2