Petit programme en C, avez-vous des remarques etc. ?
Aucune réponse
Francois Lafont
Bonjour à tous,
Autant le dire tout de suite, je suis vraiment totalement
inexpérimenté en langage C (pour ne pas dire plus). Ceci,
étant dit, j'ai codé un petit programme et j'aimerais bien
avoir, si c'est possible, vos suggestions et critiques. Je
mets le programme en fin de message.
C'est un programme qui se lance en ligne de commandes de cette
manière :
Le programme va alors lancer une requête http à l'adresse
http://localhost/cgi/machin.pl en utilisant la lib cURL
(ici, il s'agit d'un script Perl en localhost mais ça pourrait
être autre chose). La valeur 10 correspond à un timeout en
secondes (au delà le programme abandonne la requête), et le
reste des arguments xxx, yyy, zzz (il doit y en avoir au moins
1, mais ce n'est pas forcément 3 arguments, le nombre d'arguments
dépend de la page qui est appelée) est passé en variable POST
à la page http qui est appelée. Les variables POST seront
définies sous cette forme :
token1=xxx
token2=yyy
token3=zzz
Ensuite, les pages qui seront appelées avec ce programme
devront fournir une sortie de la forme :
- une première avec juste le nombre 0, 1, 2 ou 3;
- et des lignes suivantes avec ce qu'on veut, peu importe.
Par exemple :
2
CRITICAL blabla blabla.
Blibli blibli.
est une sortie correcte pour la page web. Ensuite, le
programme, qui aura mis cette sortie dans un buffer,
affichera uniquement les lignes autres que la ligne 1
et devra retourner comme exit code la valeur indiquée
dans la ligne 1 de la sortie de la page web. Si je
reprends l'exemple précédent, le programme devra donc
dans ce cas là afficher en sortie :
CRITICAL blabla blabla.
Blibli blibli.
tout en retournant la valeur 2 comme exit code.
Enfin, si la sortie de la page web appelée est trop
longue, alors le programme devra tronquer cette
sortie suivant une taille limite qui est indiquée
en tant que macro dans le programme.
Voilà pour ce que le programme est censé faire.
Pour l'écrire, je suis parti d'exemples que l'on
trouve ici ou là sur le web à propos de la lib cURL.
Par exemple, un point en particulier sur lequel
j'aimerais bien des avis (même si je suis vraiment
preneur de toute remarque à propos du code), c'est
l'usage du "cast". J'ai souvent entendu dire que
c'était à éviter et qu'on pouvait en général s'en
passer. Or dans le code, le cast est pas mal utilisé.
Peut-être que je m'y suis mal pris... Je n'ai pas vu
comment faire autrement par exemple au niveau d'une
fonction qui attend en argument des pointeurs de type
void*. En effet, dans le code on indique que l'on
souhaite écrire la sortie de la requête cURL dans un
buffer en indiquant le nom d'une fonction write_data
qui doit avoir cette signature :
où :
- la variable buffer un pointeur vers un buffer qui
contient un bout de la sortie de la requête cURL,
- size*nmemb correspond à la taille de ce buffer,
- userp est un pointeur vers le buffer dans lequel
on souhaite écrire la sortie de cURL (c'est ce
buffer là que l'on récupère dans la fonction main).
Dans la fonction main, userp correspond à la variable
wr_buf qui est un tableau de "char" mais il doit être
casté en void* pour être passé à la fonction write_data,
sachant que, dans le corps de la fonction write_data, je
m'empresse de faire le cast « inverse » (ie je caste
userp en un pointeur de type char*). Voilà par exemple
un point sur lequel je m'interroge. Mais encore une
fois, toutes vos remarques (sur la forme comme sur le
fond) m'intéressent.
Au départ, je pensais que ce programme allait réellement
me servir mais finalement il est probable que je ne l'utilise
pas. Ceci étant, en écrivant ce programme, je me suis pris
au jeu en quelques sortes et j'ai tenté de l'achever tant
bien que mal.
Merci d'avance pour toutes vos remarques et critiques.
François Lafont
if (argc < 4) {
printf("Sorry, bad syntax. You must apply at least 3 arguments.\n");
printf("%s <timeout> <url> <args>...\n", argv[0]);
return UNKNOWN;
}
if (!isPositiveInteger(argv[1])) {
printf("Sorry, bad syntax. The first argument must be a positive");
printf(" integer (it's a timeout in seconds).\n");
return UNKNOWN;
}
int timeout = atoi(argv[1]);
char *url = argv[2];
// First step, init curl.
CURL *curl;
curl = curl_easy_init();
if (!curl) {
printf("Sorry, couldn't init curl.\n");
return UNKNOWN;
}
// Construction of the post variable, a string with this form:
// token1=<urlencoded data1>&token2=<urlencoded data2>&...
char post[MAX_POST_LENGTH] = { 0 };
int token_num = 1;
char *urlencoded_str = NULL;
int i = 0;
for (i = 3; i < argc; i++) {
if (token_num > 999) {
printf
("Sorry, the limit number (999) of POST variables is exceeded.\n");
curl_easy_cleanup(curl);
return UNKNOWN;
}
// 10 is the max length of the string "token<num>=&".
// The maximum is reached with "token999=&".
int temp_size = 10 + strlen(urlencoded_str) + 1;
char temp[temp_size];
//memset(temp, 0, temp_size*sizeof(char));
sprintf(temp, "token%d=%s&", token_num, urlencoded_str);
if (strlen(post) + strlen(temp) + 1 < MAX_POST_LENGTH) {
strcat(post, temp);
}
else {
printf("Sorry, the max POST size is exceeded.\n");
curl_easy_cleanup(curl);
return UNKNOWN;
}
curl_free(urlencoded_str);
token_num++;
}
// Remove the last character "&".
post[strlen(post) - 1] = 0;
//printf("C: POST [%s]\n", post);
// Tell curl that we'll receive data to the function write_data
// which will write the data in wr_buf.
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) wr_buf);
// Allow curl to perform the action.
CURLcode ret;
ret = curl_easy_perform(curl);
if (ret) {
curl_easy_cleanup(curl);
printf("Sorry, exit value of curl is %d.", ret);
switch (ret) {
case CURLE_COULDNT_RESOLVE_HOST:
printf(" Could not resolve the host address.\n");
break;
case CURLE_OPERATION_TIMEDOUT:
printf(" Operation timeout.\n");
break;
int return_value;
if (!strncmp(wr_buf, "0\n", 2)) {
return_value = OK;
}
else if (!strncmp(wr_buf, "1\n", 2)) {
return_value = WARNING;
}
else if (!strncmp(wr_buf, "2\n", 2)) {
return_value = CRITICAL;
}
else if (!strncmp(wr_buf, "3\n", 2)) {
return_value = UNKNOWN;
}
else {
printf("Unexpected output of the plugin, return value not");
printf(" displayed or not in {0, 1, 2, 3}.\n");
return UNKNOWN;
}
printf("%s", wr_buf + 2);
return return_value;
}
// Write data callback function (called within the context
// of curl_easy_perform).
size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp) {
// It is assumed that the type of userp is char*.
char *wr_buf = (char *) userp;
static int wr_index = 0;
int segsize = size * nmemb;
// Check to see if this data exceeds the size of our buffer. If so,
// it's possible to return O to indicate a problem to curl.
// But here, we just stop the function without error (ie, we return
// segsize) and our buffer will be troncated.
if (wr_index + segsize > MAX_BUF) {
if (MAX_BUF - wr_index > 0) {
memcpy((void *) &wr_buf[wr_index], buffer,
(size_t) (MAX_BUF - wr_index));
}
wr_index = MAX_BUF + 1; // wr_buf will be not written anymore.
return segsize;
}
// Copy the data from the curl buffer into our buffer.
memcpy((void *) &wr_buf[wr_index], buffer, (size_t) segsize);
// Update the write index.
wr_index += segsize;
// Return the number of bytes received, indicating to curl that
// all is okay.
return segsize;
}
int isPositiveInteger(const char s[]) {
if (s == NULL || *s == '\0') {
return 0;
}
int i;
for (i = 0; i < strlen(s); i++) {
// ASCII value of 0 -> 48, of 1 -> 49, ..., of 9 -> 57.
if (s[i] < 48 || s[i] > 57) {
return 0;
}
}
Met des warnings, tu apprendras des choses. Les bibliotheques toujours en fin de ligne. Meme si ca marche a peu pres avec les gcc recents, entre autre parce que certaines bibliotheques sont des shared object et pas des vraies bibliotheques, c'est pas une raison de pas faire les trucs proprement.
In article <53c323d1$0$2175$426a74cc@news.free.fr>,
Francois Lafont <francois.lafont@nospam.invalid> wrote:
Met des warnings, tu apprendras des choses.
Les bibliotheques toujours en fin de ligne. Meme si ca marche a peu pres
avec les gcc recents, entre autre parce que certaines bibliotheques sont
des shared object et pas des vraies bibliotheques, c'est pas une raison de
pas faire les trucs proprement.
Met des warnings, tu apprendras des choses. Les bibliotheques toujours en fin de ligne. Meme si ca marche a peu pres avec les gcc recents, entre autre parce que certaines bibliotheques sont des shared object et pas des vraies bibliotheques, c'est pas une raison de pas faire les trucs proprement.
Ok, merci. Petite question : à quoi sert « -W » par rapport à « -Wall » ? Mettre uniquement « -Wall » n'est-il pas équivalent ?
Sinon effectivement, j'avais ce warning :
curl-launcher.c:206:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
C'est la ligne « for » à la fin qui était incriminée :
int i; for (i = 0; i < strlen(s); i++) { ...
Si j'ai bien compris, strlen(s) est de type size_t ce qui serait plus ou moins équivalent à "unsigned int" et du coup le i serait implicitement converti en "unsigned int". D'où le warning car potentiellement la valeur de i pourrait être interprétée de manière « inattendue » par le compilateur. J'ai remplacé le « int i; » par un « size_t i; » ce qui a entraîné la disparition du warning. J'ai à peu près bon ?
Les bibliotheques toujours en fin de ligne.
Ok. Je viens de regarder la page man de gcc et effectivement l'endroit où se trouve l'option -l n'est pas anodin.
Meme si ca marche a peu pres avec les gcc recents, entre autre parce que certaines bibliotheques sont des shared object et pas des vraies bibliotheques, c'est pas une raison de pas faire les trucs proprement.
Ok, merci.
Petite question : à quoi sert « -W » par rapport à « -Wall » ? Mettre
uniquement « -Wall » n'est-il pas équivalent ?
Sinon effectivement, j'avais ce warning :
curl-launcher.c:206:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
C'est la ligne « for » à la fin qui était incriminée :
int i;
for (i = 0; i < strlen(s); i++) { ...
Si j'ai bien compris, strlen(s) est de type size_t ce qui serait
plus ou moins équivalent à "unsigned int" et du coup le i serait
implicitement converti en "unsigned int". D'où le warning car
potentiellement la valeur de i pourrait être interprétée de manière
« inattendue » par le compilateur. J'ai remplacé le « int i; » par
un « size_t i; » ce qui a entraîné la disparition du warning.
J'ai à peu près bon ?
Les bibliotheques toujours en fin de ligne.
Ok. Je viens de regarder la page man de gcc et effectivement l'endroit
où se trouve l'option -l n'est pas anodin.
Meme si ca marche a peu pres
avec les gcc recents, entre autre parce que certaines bibliotheques sont
des shared object et pas des vraies bibliotheques, c'est pas une raison de
pas faire les trucs proprement.
Ok, merci. Petite question : à quoi sert « -W » par rapport à « -Wall » ? Mettre uniquement « -Wall » n'est-il pas équivalent ?
Sinon effectivement, j'avais ce warning :
curl-launcher.c:206:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
C'est la ligne « for » à la fin qui était incriminée :
int i; for (i = 0; i < strlen(s); i++) { ...
Si j'ai bien compris, strlen(s) est de type size_t ce qui serait plus ou moins équivalent à "unsigned int" et du coup le i serait implicitement converti en "unsigned int". D'où le warning car potentiellement la valeur de i pourrait être interprétée de manière « inattendue » par le compilateur. J'ai remplacé le « int i; » par un « size_t i; » ce qui a entraîné la disparition du warning. J'ai à peu près bon ?
Les bibliotheques toujours en fin de ligne.
Ok. Je viens de regarder la page man de gcc et effectivement l'endroit où se trouve l'option -l n'est pas anodin.
Meme si ca marche a peu pres avec les gcc recents, entre autre parce que certaines bibliotheques sont des shared object et pas des vraies bibliotheques, c'est pas une raison de pas faire les trucs proprement.
Oui tout à fait. Merci pour tes remarques.
-- François Lafont
Lucas Levrel
Le 14 juillet 2014, Francois Lafont a écrit :
Par exemple, un point en particulier sur lequel j'aimerais bien des avis (même si je suis vraiment preneur de toute remarque à propos du code), c'est l'usage du "cast". J'ai souvent entendu dire que c'était à éviter et qu'on pouvait en général s'en passer.
Note le « en général » !
Peut-être que je m'y suis mal pris... Je n'ai pas vu comment faire autrement par exemple au niveau d'une fonction qui attend en argument des pointeurs de type void*.
Il me semble que le cast depuis ou vers void* peut être implicite. Donc tu pourrais te passer de l'écrire dans le code, mais ce serait considéré comme malpropre je crois, car ça cacherait un peu ce qui se passe.
Dans la fonction main, userp correspond à la variable wr_buf qui est un tableau de "char" mais il doit être casté en void* pour être passé à la fonction write_data, sachant que, dans le corps de la fonction write_data, je m'empresse de faire le cast « inverse » (ie je caste userp en un pointeur de type char*). Voilà par exemple un point sur lequel je m'interroge.
Et si tu voulais récupérer des flottants, tu ferais des casts depuis/vers float*. Des entiers ? int*. Etc. L'intérêt du typage void* c'est que l'utilisateur de la bibliothèque peut passer ce qu'il veut. (Et comme les fonctions C ne sont pas polymorphes, on ne peut pas vraiment faire autrement.)
-- LL Ἕν οἶδα ὅτι οὐδὲν οἶδα (Σωκράτης)
Le 14 juillet 2014, Francois Lafont a écrit :
Par exemple, un point en particulier sur lequel
j'aimerais bien des avis (même si je suis vraiment
preneur de toute remarque à propos du code), c'est
l'usage du "cast". J'ai souvent entendu dire que
c'était à éviter et qu'on pouvait en général s'en
passer.
Note le « en général » !
Peut-être que je m'y suis mal pris... Je n'ai pas vu
comment faire autrement par exemple au niveau d'une
fonction qui attend en argument des pointeurs de type
void*.
Il me semble que le cast depuis ou vers void* peut être implicite. Donc tu
pourrais te passer de l'écrire dans le code, mais ce serait considéré
comme malpropre je crois, car ça cacherait un peu ce qui se passe.
Dans la fonction main, userp correspond à la variable
wr_buf qui est un tableau de "char" mais il doit être
casté en void* pour être passé à la fonction write_data,
sachant que, dans le corps de la fonction write_data, je
m'empresse de faire le cast « inverse » (ie je caste
userp en un pointeur de type char*). Voilà par exemple
un point sur lequel je m'interroge.
Et si tu voulais récupérer des flottants, tu ferais des casts depuis/vers
float*. Des entiers ? int*. Etc. L'intérêt du typage void* c'est que
l'utilisateur de la bibliothèque peut passer ce qu'il veut. (Et comme les
fonctions C ne sont pas polymorphes, on ne peut pas vraiment faire
autrement.)
Par exemple, un point en particulier sur lequel j'aimerais bien des avis (même si je suis vraiment preneur de toute remarque à propos du code), c'est l'usage du "cast". J'ai souvent entendu dire que c'était à éviter et qu'on pouvait en général s'en passer.
Note le « en général » !
Peut-être que je m'y suis mal pris... Je n'ai pas vu comment faire autrement par exemple au niveau d'une fonction qui attend en argument des pointeurs de type void*.
Il me semble que le cast depuis ou vers void* peut être implicite. Donc tu pourrais te passer de l'écrire dans le code, mais ce serait considéré comme malpropre je crois, car ça cacherait un peu ce qui se passe.
Dans la fonction main, userp correspond à la variable wr_buf qui est un tableau de "char" mais il doit être casté en void* pour être passé à la fonction write_data, sachant que, dans le corps de la fonction write_data, je m'empresse de faire le cast « inverse » (ie je caste userp en un pointeur de type char*). Voilà par exemple un point sur lequel je m'interroge.
Et si tu voulais récupérer des flottants, tu ferais des casts depuis/vers float*. Des entiers ? int*. Etc. L'intérêt du typage void* c'est que l'utilisateur de la bibliothèque peut passer ce qu'il veut. (Et comme les fonctions C ne sont pas polymorphes, on ne peut pas vraiment faire autrement.)
-- LL Ἕν οἶδα ὅτι οὐδὲν οἶδα (Σωκράτης)
Francois Lafont
Le 14/07/2014 18:49, Lucas Levrel a écrit :
Par exemple, un point en particulier sur lequel j'aimerais bien des avis (même si je suis vraiment preneur de toute remarque à propos du code), c'est l'usage du "cast". J'ai souvent entendu dire que c'était à éviter et qu'on pouvait en général s'en passer.
Note le « en général » !
Oui. Il doit y avoir des exceptions et avec les void* on doit être en plein dedans.
Peut-être que je m'y suis mal pris... Je n'ai pas vu comment faire autrement par exemple au niveau d'une fonction qui attend en argument des pointeurs de type void*.
Il me semble que le cast depuis ou vers void* peut être implicite. Donc tu pourrais te passer de l'écrire dans le code, mais ce serait considéré comme malpropre je crois, car ça cacherait un peu ce qui se passe.
où le dernier argument est censé être un void*, si j'enlève le cast, j'ai effectivement une compilation qui passe. J'ai tout de même un petit warning (pas super clair pour moi d'ailleurs) :
warning: call to ‘_curl_easy_setopt_err_cb_data’ declared with attribute warning: curl_easy_setopt expects a private data pointer as argument for this option [enabled by default]
Dans la fonction main, userp correspond à la variable wr_buf qui est un tableau de "char" mais il doit être casté en void* pour être passé à la fonction write_data, sachant que, dans le corps de la fonction write_data, je m'empresse de faire le cast « inverse » (ie je caste userp en un pointeur de type char*). Voilà par exemple un point sur lequel je m'interroge.
Et si tu voulais récupérer des flottants, tu ferais des casts depuis/vers float*. Des entiers ? int*. Etc.
Ben oui, dans ma logique c'est comme ça que je voyais les choses, d'où mes interrogations justement.
L'intérêt du typage void* c'est que l'utilisateur de la bibliothèque peut passer ce qu'il veut. (Et comme les fonctions C ne sont pas polymorphes, on ne peut pas vraiment faire autrement.)
Ok, mais dans le corps de la fonction qui attend un void*, si je suis amené à faire des trucs du genre pointeur[i] alors dans ce cas, pour que ça fonctionne, il faut forcément que je convertisse pointeur en autre chose que void*.
Du coup, si j'ai bien compris :
1. Lors de l'appel de la fonction, je convertis explicitement le pointeur passé en argument effectif en un void*.
2. Dans le corps de la fonction, je peux toujours convertir sans risque le pointeur en un char* afin de pouvoir faire des trucs comme p[i], p++ etc. à condition tout de même que, le pointeur en question pointe bien vers un tableau (pas forcément un tableau de char) et que je dispose bien, dans le corps de cette fonction, de la taille du tableau en question via un sizeof (donc la taille exprimée en unité de chars).
Donc dans mon programme, la macro MAX_BUF doit correspondre à la taille du buffer en unité de chars. Auquel cas, je devrais l'appeler plutôt MAX_BUF_IN_BYTES.
Est-ce correct ?
-- François Lafont
Le 14/07/2014 18:49, Lucas Levrel a écrit :
Par exemple, un point en particulier sur lequel
j'aimerais bien des avis (même si je suis vraiment
preneur de toute remarque à propos du code), c'est
l'usage du "cast". J'ai souvent entendu dire que
c'était à éviter et qu'on pouvait en général s'en
passer.
Note le « en général » !
Oui. Il doit y avoir des exceptions et avec les void*
on doit être en plein dedans.
Peut-être que je m'y suis mal pris... Je n'ai pas vu
comment faire autrement par exemple au niveau d'une
fonction qui attend en argument des pointeurs de type
void*.
Il me semble que le cast depuis ou vers void* peut être implicite. Donc tu pourrais te passer de l'écrire dans le code, mais ce serait considéré comme malpropre je crois, car ça cacherait un peu ce qui se passe.
où le dernier argument est censé être un void*, si j'enlève le
cast, j'ai effectivement une compilation qui passe. J'ai tout
de même un petit warning (pas super clair pour moi d'ailleurs) :
warning: call to ‘_curl_easy_setopt_err_cb_data’ declared with
attribute warning: curl_easy_setopt expects a private data pointer
as argument for this option [enabled by default]
Dans la fonction main, userp correspond à la variable
wr_buf qui est un tableau de "char" mais il doit être
casté en void* pour être passé à la fonction write_data,
sachant que, dans le corps de la fonction write_data, je
m'empresse de faire le cast « inverse » (ie je caste
userp en un pointeur de type char*). Voilà par exemple
un point sur lequel je m'interroge.
Et si tu voulais récupérer des flottants, tu ferais des casts depuis/vers float*. Des entiers ? int*. Etc.
Ben oui, dans ma logique c'est comme ça que je voyais les
choses, d'où mes interrogations justement.
L'intérêt du typage void* c'est que l'utilisateur de la bibliothèque peut passer ce qu'il veut. (Et comme les fonctions C ne sont pas polymorphes, on ne peut pas vraiment faire autrement.)
Ok, mais dans le corps de la fonction qui attend un void*,
si je suis amené à faire des trucs du genre pointeur[i] alors
dans ce cas, pour que ça fonctionne, il faut forcément que je
convertisse pointeur en autre chose que void*.
Du coup, si j'ai bien compris :
1. Lors de l'appel de la fonction, je convertis explicitement
le pointeur passé en argument effectif en un void*.
2. Dans le corps de la fonction, je peux toujours convertir
sans risque le pointeur en un char* afin de pouvoir faire
des trucs comme p[i], p++ etc. à condition tout de même
que, le pointeur en question pointe bien vers un tableau
(pas forcément un tableau de char) et que je dispose bien,
dans le corps de cette fonction, de la taille du tableau
en question via un sizeof (donc la taille exprimée en unité
de chars).
Donc dans mon programme, la macro MAX_BUF doit correspondre à
la taille du buffer en unité de chars. Auquel cas, je devrais
l'appeler plutôt MAX_BUF_IN_BYTES.
Par exemple, un point en particulier sur lequel j'aimerais bien des avis (même si je suis vraiment preneur de toute remarque à propos du code), c'est l'usage du "cast". J'ai souvent entendu dire que c'était à éviter et qu'on pouvait en général s'en passer.
Note le « en général » !
Oui. Il doit y avoir des exceptions et avec les void* on doit être en plein dedans.
Peut-être que je m'y suis mal pris... Je n'ai pas vu comment faire autrement par exemple au niveau d'une fonction qui attend en argument des pointeurs de type void*.
Il me semble que le cast depuis ou vers void* peut être implicite. Donc tu pourrais te passer de l'écrire dans le code, mais ce serait considéré comme malpropre je crois, car ça cacherait un peu ce qui se passe.
où le dernier argument est censé être un void*, si j'enlève le cast, j'ai effectivement une compilation qui passe. J'ai tout de même un petit warning (pas super clair pour moi d'ailleurs) :
warning: call to ‘_curl_easy_setopt_err_cb_data’ declared with attribute warning: curl_easy_setopt expects a private data pointer as argument for this option [enabled by default]
Dans la fonction main, userp correspond à la variable wr_buf qui est un tableau de "char" mais il doit être casté en void* pour être passé à la fonction write_data, sachant que, dans le corps de la fonction write_data, je m'empresse de faire le cast « inverse » (ie je caste userp en un pointeur de type char*). Voilà par exemple un point sur lequel je m'interroge.
Et si tu voulais récupérer des flottants, tu ferais des casts depuis/vers float*. Des entiers ? int*. Etc.
Ben oui, dans ma logique c'est comme ça que je voyais les choses, d'où mes interrogations justement.
L'intérêt du typage void* c'est que l'utilisateur de la bibliothèque peut passer ce qu'il veut. (Et comme les fonctions C ne sont pas polymorphes, on ne peut pas vraiment faire autrement.)
Ok, mais dans le corps de la fonction qui attend un void*, si je suis amené à faire des trucs du genre pointeur[i] alors dans ce cas, pour que ça fonctionne, il faut forcément que je convertisse pointeur en autre chose que void*.
Du coup, si j'ai bien compris :
1. Lors de l'appel de la fonction, je convertis explicitement le pointeur passé en argument effectif en un void*.
2. Dans le corps de la fonction, je peux toujours convertir sans risque le pointeur en un char* afin de pouvoir faire des trucs comme p[i], p++ etc. à condition tout de même que, le pointeur en question pointe bien vers un tableau (pas forcément un tableau de char) et que je dispose bien, dans le corps de cette fonction, de la taille du tableau en question via un sizeof (donc la taille exprimée en unité de chars).
Donc dans mon programme, la macro MAX_BUF doit correspondre à la taille du buffer en unité de chars. Auquel cas, je devrais l'appeler plutôt MAX_BUF_IN_BYTES.
Peut-être que je m'y suis mal pris... Je n'ai pas vu comment faire autrement par exemple au niveau d'une fonction qui attend en argument des pointeurs de type void*.
Non! les cast entre void* et d'autres choses servent juste a permettre de compiler le meme code avec un compilo C++, ce qui est une vaste connerie, parce que tant qu'a faire, autant faire du C++ directement.
Ca ne rend pas le code specialement plus propre, et surtout c'est dangereux, car on peut faire des erreurs et se planter d'un niveau de pointeur.
Je rappelle qu'un cast dit globalement au compilo "ne regarde pas, je sais ce que je fais", et donc qu'il vaut mieux s'en passer quand c'est possible.
En particulier, en C, les void* sont convertibles en autres pointeurs vers donnees sans cast, et sans warning. C'est necessaire pour que malloc marche, par exemple.
In article <alpine.LNX.2.10.1407141839300.1664@localhost>,
Lucas Levrel <lucas.levrel@u-pec.fr> wrote:
Peut-être que je m'y suis mal pris... Je n'ai pas vu
comment faire autrement par exemple au niveau d'une
fonction qui attend en argument des pointeurs de type
void*.
Non! les cast entre void* et d'autres choses servent juste a permettre
de compiler le meme code avec un compilo C++, ce qui est une vaste connerie,
parce que tant qu'a faire, autant faire du C++ directement.
Ca ne rend pas le code specialement plus propre, et surtout c'est dangereux,
car on peut faire des erreurs et se planter d'un niveau de pointeur.
Je rappelle qu'un cast dit globalement au compilo "ne regarde pas, je sais
ce que je fais", et donc qu'il vaut mieux s'en passer quand c'est possible.
En particulier, en C, les void* sont convertibles en autres pointeurs vers
donnees sans cast, et sans warning. C'est necessaire pour que malloc marche,
par exemple.
Peut-être que je m'y suis mal pris... Je n'ai pas vu comment faire autrement par exemple au niveau d'une fonction qui attend en argument des pointeurs de type void*.
Non! les cast entre void* et d'autres choses servent juste a permettre de compiler le meme code avec un compilo C++, ce qui est une vaste connerie, parce que tant qu'a faire, autant faire du C++ directement.
Ca ne rend pas le code specialement plus propre, et surtout c'est dangereux, car on peut faire des erreurs et se planter d'un niveau de pointeur.
Je rappelle qu'un cast dit globalement au compilo "ne regarde pas, je sais ce que je fais", et donc qu'il vaut mieux s'en passer quand c'est possible.
En particulier, en C, les void* sont convertibles en autres pointeurs vers donnees sans cast, et sans warning. C'est necessaire pour que malloc marche, par exemple.
Francois Lafont
Le 14/07/2014 20:31, Marc Espie a écrit :
Non! les cast entre void* et d'autres choses servent juste a permettre de compiler le meme code avec un compilo C++, ce qui est une vaste connerie, parce que tant qu'a faire, autant faire du C++ directement.
Ca ne rend pas le code specialement plus propre, et surtout c'est dangereux, car on peut faire des erreurs et se planter d'un niveau de pointeur.
Je rappelle qu'un cast dit globalement au compilo "ne regarde pas, je sais ce que je fais", et donc qu'il vaut mieux s'en passer quand c'est possible.
En particulier, en C, les void* sont convertibles en autres pointeurs vers donnees sans cast, et sans warning. C'est necessaire pour que malloc marche, par exemple.
Ok, si une fonction attend un void* en argument, on peut lui fournir un truc* sans faire de cast, et ça passe sans problème. Mais si la fonction en question se sert de ce pointeur pour ensuite écrire des données dans un tableau (dont l'adresse de début est donnée par le pointeur), je suis bien obligé d'écrire le cast ci-dessous dans le corps de la fonction :
void f(void* p) { char *t = (char*) p; // Et on utilise t pour écrire dans le tableau // via une boucle. // etc... }
du code donné dans le premier message, si je retire le cast au niveau du dernier paramètre, j'ai le droit à un warning comme j'ai indiqué dans mon message précédent. Dans cet exemple précis, la conversion en void* ne semble pas automatique.
-- François Lafont
Le 14/07/2014 20:31, Marc Espie a écrit :
Non! les cast entre void* et d'autres choses servent juste a permettre
de compiler le meme code avec un compilo C++, ce qui est une vaste connerie,
parce que tant qu'a faire, autant faire du C++ directement.
Ca ne rend pas le code specialement plus propre, et surtout c'est dangereux,
car on peut faire des erreurs et se planter d'un niveau de pointeur.
Je rappelle qu'un cast dit globalement au compilo "ne regarde pas, je sais
ce que je fais", et donc qu'il vaut mieux s'en passer quand c'est possible.
En particulier, en C, les void* sont convertibles en autres pointeurs vers
donnees sans cast, et sans warning. C'est necessaire pour que malloc marche,
par exemple.
Ok, si une fonction attend un void* en argument, on
peut lui fournir un truc* sans faire de cast, et ça
passe sans problème. Mais si la fonction en question
se sert de ce pointeur pour ensuite écrire des données
dans un tableau (dont l'adresse de début est donnée
par le pointeur), je suis bien obligé d'écrire le
cast ci-dessous dans le corps de la fonction :
void f(void* p) {
char *t = (char*) p;
// Et on utilise t pour écrire dans le tableau
// via une boucle.
// etc...
}
du code donné dans le premier message, si je retire le
cast au niveau du dernier paramètre, j'ai le droit à un
warning comme j'ai indiqué dans mon message précédent.
Dans cet exemple précis, la conversion en void* ne semble
pas automatique.
Non! les cast entre void* et d'autres choses servent juste a permettre de compiler le meme code avec un compilo C++, ce qui est une vaste connerie, parce que tant qu'a faire, autant faire du C++ directement.
Ca ne rend pas le code specialement plus propre, et surtout c'est dangereux, car on peut faire des erreurs et se planter d'un niveau de pointeur.
Je rappelle qu'un cast dit globalement au compilo "ne regarde pas, je sais ce que je fais", et donc qu'il vaut mieux s'en passer quand c'est possible.
En particulier, en C, les void* sont convertibles en autres pointeurs vers donnees sans cast, et sans warning. C'est necessaire pour que malloc marche, par exemple.
Ok, si une fonction attend un void* en argument, on peut lui fournir un truc* sans faire de cast, et ça passe sans problème. Mais si la fonction en question se sert de ce pointeur pour ensuite écrire des données dans un tableau (dont l'adresse de début est donnée par le pointeur), je suis bien obligé d'écrire le cast ci-dessous dans le corps de la fonction :
void f(void* p) { char *t = (char*) p; // Et on utilise t pour écrire dans le tableau // via une boucle. // etc... }
du code donné dans le premier message, si je retire le cast au niveau du dernier paramètre, j'ai le droit à un warning comme j'ai indiqué dans mon message précédent. Dans cet exemple précis, la conversion en void* ne semble pas automatique.
Non! les cast entre void* et d'autres choses servent juste a permettre de compiler le meme code avec un compilo C++, ce qui est une vaste connerie, parce que tant qu'a faire, autant faire du C++ directement.
Ca ne rend pas le code specialement plus propre, et surtout c'est dangereux, car on peut faire des erreurs et se planter d'un niveau de pointeur.
Je rappelle qu'un cast dit globalement au compilo "ne regarde pas, je sais ce que je fais", et donc qu'il vaut mieux s'en passer quand c'est possible.
En particulier, en C, les void* sont convertibles en autres pointeurs vers donnees sans cast, et sans warning. C'est necessaire pour que malloc marche, par exemple.
(ah, et en style C usuel, tu vas plus normalement ecrire "char *t" que "char* t"... qui est la aussi plus communement associe a C++.
Tout betement pour eviter l'erreur classique du char *p, r; ^ tiens, ca c'est pas un pointeur.
Le seul cas courant ou tu auras besoin d'un cast, c'est pour ecrire une fonction similaire a strchr, ou tu es amene a convertir de const char* vers char*.
In article <53c476f0$0$2217$426a34cc@news.free.fr>,
Francois Lafont <francois.lafont@nospam.invalid> wrote:
Non! les cast entre void* et d'autres choses servent juste a permettre
de compiler le meme code avec un compilo C++, ce qui est une vaste connerie,
parce que tant qu'a faire, autant faire du C++ directement.
Ca ne rend pas le code specialement plus propre, et surtout c'est dangereux,
car on peut faire des erreurs et se planter d'un niveau de pointeur.
Je rappelle qu'un cast dit globalement au compilo "ne regarde pas, je sais
ce que je fais", et donc qu'il vaut mieux s'en passer quand c'est possible.
En particulier, en C, les void* sont convertibles en autres pointeurs vers
donnees sans cast, et sans warning. C'est necessaire pour que malloc marche,
par exemple.
(ah, et en style C usuel, tu vas plus normalement ecrire "char *t" que
"char* t"... qui est la aussi plus communement associe a C++.
Tout betement pour eviter l'erreur classique du
char *p, r;
^ tiens, ca c'est pas un pointeur.
Le seul cas courant ou tu auras besoin d'un cast, c'est pour ecrire une
fonction similaire a strchr, ou tu es amene a convertir de const char*
vers char*.
Non! les cast entre void* et d'autres choses servent juste a permettre de compiler le meme code avec un compilo C++, ce qui est une vaste connerie, parce que tant qu'a faire, autant faire du C++ directement.
Ca ne rend pas le code specialement plus propre, et surtout c'est dangereux, car on peut faire des erreurs et se planter d'un niveau de pointeur.
Je rappelle qu'un cast dit globalement au compilo "ne regarde pas, je sais ce que je fais", et donc qu'il vaut mieux s'en passer quand c'est possible.
En particulier, en C, les void* sont convertibles en autres pointeurs vers donnees sans cast, et sans warning. C'est necessaire pour que malloc marche, par exemple.
(ah, et en style C usuel, tu vas plus normalement ecrire "char *t" que "char* t"... qui est la aussi plus communement associe a C++.
Tout betement pour eviter l'erreur classique du char *p, r; ^ tiens, ca c'est pas un pointeur.
Le seul cas courant ou tu auras besoin d'un cast, c'est pour ecrire une fonction similaire a strchr, ou tu es amene a convertir de const char* vers char*.
espie
Ou, si tu preferes, ne confond pas transtypage implicite et cast explicite.
Tu as parfaitement le droit de convertir des valeurs d'un type a un autre sans mettre de cast en C, et habituellement sans que le compilo moufte, sauf cas particulier.
Ainsi,
void *p;
...
int *k = p;
ne va pas balancer de warning du tout. C'est totalement legal en C.
Tu reserves les cast aux cas ou ca chouine un peu.
Si ca passe sans le moindre avertissement du compilo, il ne faut surtout pas mettre de cast, car les cast desactivent *completement* les avertissements.
Genre:
int i;
void *p = (void *)i; // va passer, alors qu'on voulait &i
Exemple plus drole
void print(void *t, int n) { int i; int **p = t;
for (i = 0 ; i < n; i++) printf("%d ", p[i]); }
On peut etre tente de virer p, mais ca ne sera pas plus lisible, pas plus efficace (tout compilo decent saura faire son boulot) et ca ouvre la porte a de grosses erreurs.
void print(void *t, int n) { int i;
for (i = 0 ; i < n; i++) printf("%d ", ((int **)t)[i]); }
Ou, si tu preferes, ne confond pas transtypage implicite et cast explicite.
Tu as parfaitement le droit de convertir des valeurs d'un type a un autre
sans mettre de cast en C, et habituellement sans que le compilo moufte,
sauf cas particulier.
Ainsi,
void *p;
...
int *k = p;
ne va pas balancer de warning du tout. C'est totalement legal en C.
Tu reserves les cast aux cas ou ca chouine un peu.
Si ca passe sans le moindre avertissement du compilo, il ne faut surtout
pas mettre de cast, car les cast desactivent *completement* les
avertissements.
Genre:
int i;
void *p = (void *)i; // va passer, alors qu'on voulait &i
Exemple plus drole
void
print(void *t, int n)
{
int i;
int **p = t;
for (i = 0 ; i < n; i++)
printf("%d ", p[i]);
}
On peut etre tente de virer p, mais ca ne sera pas plus lisible, pas plus
efficace (tout compilo decent saura faire son boulot) et ca ouvre la porte
a de grosses erreurs.
void
print(void *t, int n)
{
int i;
for (i = 0 ; i < n; i++)
printf("%d ", ((int **)t)[i]);
}
Ou, si tu preferes, ne confond pas transtypage implicite et cast explicite.
Tu as parfaitement le droit de convertir des valeurs d'un type a un autre sans mettre de cast en C, et habituellement sans que le compilo moufte, sauf cas particulier.
Ainsi,
void *p;
...
int *k = p;
ne va pas balancer de warning du tout. C'est totalement legal en C.
Tu reserves les cast aux cas ou ca chouine un peu.
Si ca passe sans le moindre avertissement du compilo, il ne faut surtout pas mettre de cast, car les cast desactivent *completement* les avertissements.
Genre:
int i;
void *p = (void *)i; // va passer, alors qu'on voulait &i
Exemple plus drole
void print(void *t, int n) { int i; int **p = t;
for (i = 0 ; i < n; i++) printf("%d ", p[i]); }
On peut etre tente de virer p, mais ca ne sera pas plus lisible, pas plus efficace (tout compilo decent saura faire son boulot) et ca ouvre la porte a de grosses erreurs.
void print(void *t, int n) { int i;
for (i = 0 ; i < n; i++) printf("%d ", ((int **)t)[i]); }
Le Tue, 15 Jul 2014 07:17:37 +0000 (UTC), Marc Espie écrivait :
In article <53c476f0$0$2217$, Francois Lafont wrote:
Le 14/07/2014 20:31, Marc Espie a écrit :
Non! les cast entre void* et d'autres choses servent juste a permettre de compiler le meme code avec un compilo C++, ce qui est une vaste connerie, parce que tant qu'a faire, autant faire du C++ directement.
Ca ne rend pas le code specialement plus propre, et surtout c'est dangereux, car on peut faire des erreurs et se planter d'un niveau de pointeur.
Je rappelle qu'un cast dit globalement au compilo "ne regarde pas, je sais ce que je fais", et donc qu'il vaut mieux s'en passer quand c'est possible.
En particulier, en C, les void* sont convertibles en autres pointeurs vers donnees sans cast, et sans warning. C'est necessaire pour que malloc marche, par exemple.
Ok, si une fonction attend un void* en argument, on peut lui fournir un truc* sans faire de cast, et ça passe sans problème. Mais si la fonction en question se sert de ce pointeur pour ensuite écrire des données dans un tableau (dont l'adresse de début est donnée par le pointeur), je suis bien obligé d'écrire le cast ci-dessous dans le corps de la fonction :
void f(void* p) { char *t = (char*) p; // Et on utilise t pour écrire dans le tableau // via une boucle. // etc... }
Non ?
Non.
void f(void *p) { char *t = p; // Et on utilise t pour écrire dans le tableau // via une boucle. // etc... }
ca marchera.
(ah, et en style C usuel, tu vas plus normalement ecrire "char *t" que "char* t"... qui est la aussi plus communement associe a C++.
Tout betement pour eviter l'erreur classique du char *p, r; ^ tiens, ca c'est pas un pointeur.
Certes. Mais comme on peut écrire du Fortran dans n'importe quel langage, on préférera :
char *p, *q, *r; char s, t, u;
Ça évite ce genre d'erreur d'inattention.
Le seul cas courant ou tu auras besoin d'un cast, c'est pour ecrire une fonction similaire a strchr, ou tu es amene a convertir de const char* vers char*.
Et que le const char * est piégeux...
JKB
-- Si votre demande me parvient sur carte perforée, je titiouaillerai très volontiers une réponse... => http://grincheux.de-charybde-en-scylla.fr => http://loubardes.de-charybde-en-scylla.fr
Le Tue, 15 Jul 2014 07:17:37 +0000 (UTC),
Marc Espie <espie@lain.home> écrivait :
In article <53c476f0$0$2217$426a34cc@news.free.fr>,
Francois Lafont <francois.lafont@nospam.invalid> wrote:
Le 14/07/2014 20:31, Marc Espie a écrit :
Non! les cast entre void* et d'autres choses servent juste a permettre
de compiler le meme code avec un compilo C++, ce qui est une vaste connerie,
parce que tant qu'a faire, autant faire du C++ directement.
Ca ne rend pas le code specialement plus propre, et surtout c'est dangereux,
car on peut faire des erreurs et se planter d'un niveau de pointeur.
Je rappelle qu'un cast dit globalement au compilo "ne regarde pas, je sais
ce que je fais", et donc qu'il vaut mieux s'en passer quand c'est possible.
En particulier, en C, les void* sont convertibles en autres pointeurs vers
donnees sans cast, et sans warning. C'est necessaire pour que malloc marche,
par exemple.
Ok, si une fonction attend un void* en argument, on
peut lui fournir un truc* sans faire de cast, et ça
passe sans problème. Mais si la fonction en question
se sert de ce pointeur pour ensuite écrire des données
dans un tableau (dont l'adresse de début est donnée
par le pointeur), je suis bien obligé d'écrire le
cast ci-dessous dans le corps de la fonction :
void f(void* p) {
char *t = (char*) p;
// Et on utilise t pour écrire dans le tableau
// via une boucle.
// etc...
}
Non ?
Non.
void f(void *p) {
char *t = p;
// Et on utilise t pour écrire dans le tableau
// via une boucle.
// etc...
}
ca marchera.
(ah, et en style C usuel, tu vas plus normalement ecrire "char *t" que
"char* t"... qui est la aussi plus communement associe a C++.
Tout betement pour eviter l'erreur classique du
char *p, r;
^ tiens, ca c'est pas un pointeur.
Certes. Mais comme on peut écrire du Fortran dans n'importe quel
langage, on préférera :
char *p, *q, *r;
char s, t, u;
Ça évite ce genre d'erreur d'inattention.
Le seul cas courant ou tu auras besoin d'un cast, c'est pour ecrire une
fonction similaire a strchr, ou tu es amene a convertir de const char*
vers char*.
Et que le const char * est piégeux...
JKB
--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
=> http://loubardes.de-charybde-en-scylla.fr
Le Tue, 15 Jul 2014 07:17:37 +0000 (UTC), Marc Espie écrivait :
In article <53c476f0$0$2217$, Francois Lafont wrote:
Le 14/07/2014 20:31, Marc Espie a écrit :
Non! les cast entre void* et d'autres choses servent juste a permettre de compiler le meme code avec un compilo C++, ce qui est une vaste connerie, parce que tant qu'a faire, autant faire du C++ directement.
Ca ne rend pas le code specialement plus propre, et surtout c'est dangereux, car on peut faire des erreurs et se planter d'un niveau de pointeur.
Je rappelle qu'un cast dit globalement au compilo "ne regarde pas, je sais ce que je fais", et donc qu'il vaut mieux s'en passer quand c'est possible.
En particulier, en C, les void* sont convertibles en autres pointeurs vers donnees sans cast, et sans warning. C'est necessaire pour que malloc marche, par exemple.
Ok, si une fonction attend un void* en argument, on peut lui fournir un truc* sans faire de cast, et ça passe sans problème. Mais si la fonction en question se sert de ce pointeur pour ensuite écrire des données dans un tableau (dont l'adresse de début est donnée par le pointeur), je suis bien obligé d'écrire le cast ci-dessous dans le corps de la fonction :
void f(void* p) { char *t = (char*) p; // Et on utilise t pour écrire dans le tableau // via une boucle. // etc... }
Non ?
Non.
void f(void *p) { char *t = p; // Et on utilise t pour écrire dans le tableau // via une boucle. // etc... }
ca marchera.
(ah, et en style C usuel, tu vas plus normalement ecrire "char *t" que "char* t"... qui est la aussi plus communement associe a C++.
Tout betement pour eviter l'erreur classique du char *p, r; ^ tiens, ca c'est pas un pointeur.
Certes. Mais comme on peut écrire du Fortran dans n'importe quel langage, on préférera :
char *p, *q, *r; char s, t, u;
Ça évite ce genre d'erreur d'inattention.
Le seul cas courant ou tu auras besoin d'un cast, c'est pour ecrire une fonction similaire a strchr, ou tu es amene a convertir de const char* vers char*.
Et que le const char * est piégeux...
JKB
-- Si votre demande me parvient sur carte perforée, je titiouaillerai très volontiers une réponse... => http://grincheux.de-charybde-en-scylla.fr => http://loubardes.de-charybde-en-scylla.fr