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

Petit programme en C, avez-vous des remarques etc. ?

Aucune réponse
Avatar
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 :

./mon-programme 10 localhost/cgi/machin.pl xxx yyy zzz

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 :

size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp);

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

-------------------------------------------------------
/* With Debian Wheezy:
*
* sudo apt-get install libcurl4-openssl-dev
* gcc -lcurl -std=c99 -o curl-launcher.exe curl-launcher.c
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <curl/curl.h>

#define MAX_BUF 65536
#define MAX_POST_LENGTH 4096
#define OK 0
#define WARNING 1
#define CRITICAL 2
#define UNKNOWN 3

size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp);
int isPositiveInteger(const char s[]);

int main(int argc, char *argv[]) {

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;
}

//printf("C: token%d: [%s]\n", token_num, argv[i]);

urlencoded_str = curl_easy_escape(curl, argv[i], 0);

// 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);

char wr_buf[MAX_BUF + 1] = { 0 };

curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, 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;

default:
printf("\n");
break;

}

return UNKNOWN;
}

curl_easy_cleanup(curl);

/*
printf("----------------------------------\n");
printf("%s", wr_buf);
printf("----------------------------------\n");
*/

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;
}
}

return 1;
}
-------------------------------------------------------

8 réponses

3 4 5 6 7
Avatar
Francois Lafont
Bonjour à tous,

Suite à la discussion intitulée "Faire du C99 en 2014",
j'ai tenté de réécrire (en partie) mon petit programme en
me passant des tableaux à taille dynamique afin de le
rendre (à peu près) conforme C90. Le code est ici :

https://github.com/flaf/miscellaneous/blob/master/puppet/production/modules/apache_poller/files/sp_check_ansi.c

Je le compile sans erreur avec la commande ci-dessous,
sachant que je suis sous Debian Wheezy avec gcc version
4.7.2 :

gcc -stdÈ9 -pedantic -Wextra -Wall -O2
-o sp_check_ansi sp_check_ansi.c -lcurl

En revanche, j'ai de nombreux warnings, tous du même type,
à savoir (un exemple parmi d'autres) :

sp_check_ansi.c:212:3: warning: ISO C90 forbids mixed declarations and code [-pedantic]

Je comprends bien ce warning (je suis censé déclarer mes
variables au début du corps des fonctions) mais pour le coup
je n'ai pas très envie de me plier à cette règle.

1. Bon, du coup, le source ne sera pas conforme C90 à 100%,
certes... C'est grave ? Outre le fait qu'au niveau style
ça ne me plaît pas trop de mettre toutes les déclarations
au début de chaque fonction, parfois je suis obligé de
déclarer une variable après un peu de code : par exemple
dans la fonction "main" je veux déclarer une variable avec
"const", variable qui correspond à un argument fourni au
programme via la ligne de commandes, or il faut bien avant
que je teste si cet argument a bien été fourni par l'utilisateur.

2. Y a-t-il un moyen d'empêcher *uniquement* ce warning là
« forbids mixed declarations and code » avec une option de
gcc ou je ne sais quoi (mis à part un « | grep -v ... ») ?
Je n'ai pas trouvé. Après quelques recherches, j'ai cru que :

gcc -stdÈ9 -pedantic -Wextra -Wall -O2
-Wno-declaration-after-statement
-o sp_check_ansi sp_check_ansi.c -lcurl

allait faire ce que je souhaite mais ce n'est pas le cas.

--
François Lafont
Avatar
Xavier Roche
On 07/25/2014 05:09 AM, Francois Lafont wrote:
2. Y a-t-il un moyen d'empêcher *uniquement* ce warning là
« forbids mixed declarations and code » avec une option de
gcc ou je ne sais quoi



(1) Utiliser -Wno-pedantic (ou -Wno-declaration-after-statement selon
les versions)

(2) Utiliser -stdÉ9 :p
Avatar
Samuel DEVULDER
Le 25/07/2014 05:09, Francois Lafont a écrit :

1. Bon, du coup, le source ne sera pas conforme C90 à 100%,
certes... C'est grave ? Outre le fait qu'au niveau style
ça ne me plaît pas trop de mettre toutes les déclarations
au début de chaque fonction, parfois je suis obligé de
déclarer une variable après un peu de code : par exemple
dans la fonction "main" je veux déclarer une variable avec
"const", variable qui correspond à un argument fourni au
programme via la ligne de commandes, or il faut bien avant
que je teste si cet argument a bien été fourni par l'utilisateur.



Tu peux ouvrir un bloc pour y déclarer les variables utilisées
localement. Il faut juste aimer les {} supplémentaires.

a+

sam.
Avatar
Francois Lafont
Le 25/07/2014 07:22, Xavier Roche a écrit :

2. Y a-t-il un moyen d'empêcher *uniquement* ce warning là
« forbids mixed declarations and code » avec une option de
gcc ou je ne sais quoi



(1) Utiliser -Wno-pedantic (ou -Wno-declaration-after-statement selon les versions)



1. Avec :

gcc -stdÈ9 -pedantic -Wextra -Wall -Wno-pedantic -O2 -o prog prog.c

J'ai toujours le warning en question avec en plus ce message :

cc1: warning: unrecognized command line option "-Wno-pedantic" [enabled by default]

2. Avec :

gcc -stdÈ9 -Wextra -Wall -Wno-pedantic -O2 -o prog prog.c

Je n'ai plus le warning et je n'ai plus non plus de message m'indiquant
que l'option "-Wno-pedantic" est inconnue (curieux ?). En revanche,
le warning n'est plus le seul qui est passé sous silence. Par exemple,
avec la commande ci-dessus, si je déclare un tableau à taille dynamique,
je n'ai aucun warning qui me le signale.

3. Avec :

gcc -stdÈ9 -pedantic -Wextra -Wall -Wno-declaration-after-statement -O2 -o prog prog.c

J'ai toujours le warning « forbids mixed declarations and code ».
Et si je reprends la même commande sans le -pedantic, alors
je n'ai plus le warning mais le warning sur les tableaux à taille
dynamique disparaît aussi.

Bref, en fait, il semble que ce warning provienne de l'option
-pedantic. D'ailleurs le warning est de la forme :

warning: ISO C90 forbids mixed declarations and code [-pedantic]
^^^^^^^^^^^
Du coup, d'après mes tentatives empiriques, c'est uniquement en
virant l'option -pedantic que j'arrive à faire disparaître ce
warning. Seulement, l'inconvénient est que ça fait disparaître
aussi d'autres warnings (comme dans le cas de l'utilisation des
tableaux dynamiques).

Il n'y a pas moyen de passer sous silence *uniquement* ce warning
et de garder les autres (ceux que j'obtiens avec le jeu d'options
« -stdÈ9 -pedantic -Wextra -Wall ») ?

--
François Lafont
Avatar
Francois Lafont
Le 25/07/2014 08:02, Samuel DEVULDER a écrit :

1. Bon, du coup, le source ne sera pas conforme C90 à 100%,
certes... C'est grave ? Outre le fait qu'au niveau style
ça ne me plaît pas trop de mettre toutes les déclarations
au début de chaque fonction, parfois je suis obligé de
déclarer une variable après un peu de code : par exemple
dans la fonction "main" je veux déclarer une variable avec
"const", variable qui correspond à un argument fourni au
programme via la ligne de commandes, or il faut bien avant
que je teste si cet argument a bien été fourni par l'utilisateur.



Tu peux ouvrir un bloc pour y déclarer les variables utilisées localement. Il faut juste aimer les {} supplémentaires.



Ah, je ne savais que c'était possible de faire ça. Effectivement,
ça marche mais comme tu dis il faut aimer les {...}. Ça
alourdit énormément.


--
François Lafont
Avatar
Antoine Leca
Rappel : une déclaration qui survient après une instruction est une
violation de la syntaxe admissible par la norme C89, et un compilateur
conforme à cette norme doit le signaler au programmeur.

Le 25/07/2014 11:45, Francois Lafont écrivit :
gcc -stdÈ9 -pedantic [...]
J'ai toujours le warning « forbids mixed declarations and code ».



C'est le contraire qui serait surprenant...

Le mélange de déclarations et d'instructions est un ajout de C99. Donc
ce ne sera pas correctement pour un compilateur qui vise C89.
Et avec gcc -stdÈ9 -pedantic, tu places GCC dans le mode de maximale
conformité à la norme C89... (c'est le sens réel de -pedantic)

[...] le warning sur les tableaux à taille dynamique disparaît aussi.



20 secondes de Google suggèrent -Wvla

Du coup, d'après mes tentatives empiriques, c'est uniquement en
virant l'option -pedantic que j'arrive à faire disparaître ce
warning.



Ce qui peut avoir deux interprétations distinctes :

1) [les développeurs de] GCC considèrent que mélanger déclarations et
instructions dans du code C89 est une chose normale et naturelle, et
qu'il n'est pas nécessaire de signaler l'éventuel problème au
programmeur, à moins d'y être obligé pour être conforme (= pédant).

2) GCC a un bogue à ce niveau dans le système de warnings, qui met sous
silence un problème pourtant clair de portabilité. Direction Gnats ?


Seulement, l'inconvénient



Euh, désolé mais non, pas d'accord : soit tu programmes en conformité
avec C99 et tu t'autorises de mélanger déclarations et instructions,
mais tu te restreint aux compilateurs qui supportent cette norme.
Soit tu veux utiliser plus de compilateurs, et il faut déplacer les
déclarations avant la première instruction (ou rajouter des { } ).

Dans le premier cas, tu utiliseras -stdÉ9
Dans le second, tu _veux_ voir l'avertissement.


Il n'y a pas moyen de passer sous silence *uniquement* ce warning
et de garder les autres (ceux que j'obtiens avec le jeu d'options
« -stdÈ9 -pedantic -Wextra -Wall ») ?



L'idée derrière -pedantic est de ne PAS utiliser cette option dans les
conditions normales, car les avertissements qu'elle engendre sont
supposés être inutiles dans les conditions normales de programmation.
(Cela ne veut pas dire que l'objectif soit atteint :^) .)


Antoine
Avatar
espie
Les warnings de gcc sont de toutes facons une sorte de monstre elabore
au cours des ages... arriver a en sortir quelque chose de coherent releve
de la gageure.
Avatar
Francois Lafont
Le 25/07/2014 15:21, Antoine Leca a écrit :

Rappel : une déclaration qui survient après une instruction est une
violation de la syntaxe admissible par la norme C89, et un compilateur
conforme à cette norme doit le signaler au programmeur.



Oui, je suis d'accord avec ça. Et l'objet de ma question
était donc de savoir s'il était techniquement possible de
dire à gcc de faire une *exception* à ce principe là sur
un warning précis. (Et au passage, à part avec un grep, a
priori je ne vois pas comment c'est possible).

Mais par ailleurs, je veux bien admettre qu'effectivement ma
demande n'est sans doute pas cohérente, voire pas légitime.

Le 25/07/2014 11:45, Francois Lafont écrivit :
gcc -stdÈ9 -pedantic [...]
J'ai toujours le warning « forbids mixed declarations and code ».



C'est le contraire qui serait surprenant...



Ben, j'espérais qu'avec un truc comme -Wno-xxx, on puisse ajouter
une exception. Et en fait, ça marche parfois.

Voici un copier-coller de ma console, avec des compilations
successives d'un *même* programme :

# À la base, on a du warning "vla" et du
# "forbids mixed declarations and code"
~$ gcc -stdÈ9 -pedantic -o test test.c
test.c: In function ‘main’:
test.c:6:3: warning: ISO C90 forbids variable length array ‘tab’ [-Wvla]
test.c:18:3: warning: ISO C90 forbids mixed declarations and code [-pedantic]

# Pas le moindre warning ici. Que dalle, Nada. ;)
~$ gcc -stdÈ9 -Wall -Wextra -o test.exe test.c

# Ok, on ajoute individuellement le warning "declaration-after-statement"
~$ gcc -stdÈ9 -Wall -Wextra -Wdeclaration-after-statement -o test.exe test.c
test.c: In function ‘main’:
test.c:18:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]

# Puis on ajoute le warning "vla". On retrouve bien tous nos warnings.
~$ gcc -stdÈ9 -Wall -Wextra -Wdeclaration-after-statement -Wvla -o test.exe test.c
test.c: In function ‘main’:
test.c:6:3: warning: variable length array ‘tab’ is used [-Wvla]
test.c:18:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]

# Retour sur -pedantic mais on veut faire taire le warning "vla". Ça marche !
~$ gcc -stdÈ9 -pedantic -Wno-vla -o test.exe test.c
test.c: In function ‘main’:
test.c:18:3: warning: ISO C90 forbids mixed declarations and code [-pedantic]

# Par contre, impossible de faire taire "declaration-after-statement".
~$ gcc -stdÈ9 -pedantic -Wno-vla -Wno-declaration-after-statement -o test.exe test.c
test.c: In function ‘main’:
test.c:18:3: warning: ISO C90 forbids mixed declarations and code [-pedantic]

Va comprendre la logique là-dedans...
Comme tu peux voir ci-dessus, même si ma demande n'est
moralement pas légitime, j'avais des raisons d'espérer
qu'elle soit techniquement possible.

[...] le warning sur les tableaux à taille dynamique disparaît aussi.



20 secondes de Google suggèrent -Wvla



Ah mais je le savais en plus, et sans la moindre recherche Google
car l'option est indiquée dans le warning lui-même. Par contre, je
pensais, à tort, que le fait d'avoir spécifié « -Wall -Wextra »
incluait automatiquement le warning -Wvla. En fait, non et on peut
même lire dans la page man de gcc à propos de l'option -Wall :

Note that some warning flags are not implied by -Wall.
[...] Some of them are enabled by -Wextra *but* many of
them must be enabled individually.

Donc force est de constater qu'avec un :

gcc -std‰ -Wall -Wextra ...

le warning sur les tableaux à taille dynamique n'est pas activé
(il fait partie de la liste de ceux qui doivent être "activés
individuellement" ou via -pedantic). Idem pour le warning sur
les déclarations dans le code.

Du coup, d'après mes tentatives empiriques, c'est uniquement en
virant l'option -pedantic que j'arrive à faire disparaître ce
warning.



Ce qui peut avoir deux interprétations distinctes :

1) [les développeurs de] GCC considèrent que mélanger déclarations et
instructions dans du code C89 est une chose normale et naturelle, et
qu'il n'est pas nécessaire de signaler l'éventuel problème au
programmeur, à moins d'y être obligé pour être conforme (= pédant).



Tu peux donc ajouter les tableaux à taille dynamique dans la liste
des fonctionnalités que les développeurs de gcc trouvent « normales
et naturelles [...] » dans du code C89.

2) GCC a un bogue à ce niveau dans le système de warnings, qui met sous
silence un problème pourtant clair de portabilité. Direction Gnats ?



Perso, je ne suis pas apte à juger, je m'en tiens à la version 1. ;)

Seulement, l'inconvénient



Euh, désolé mais non, pas d'accord : soit tu programmes en conformité
avec C99 et tu t'autorises de mélanger déclarations et instructions,
mais tu te restreint aux compilateurs qui supportent cette norme.
Soit tu veux utiliser plus de compilateurs, et il faut déplacer les
déclarations avant la première instruction (ou rajouter des { } ).

Dans le premier cas, tu utiliseras -stdÉ9
Dans le second, tu _veux_ voir l'avertissement.



Ok. Je m'incline. ;)

Il n'y a pas moyen de passer sous silence *uniquement* ce warning
et de garder les autres (ceux que j'obtiens avec le jeu d'options
« -stdÈ9 -pedantic -Wextra -Wall ») ?



L'idée derrière -pedantic est de ne PAS utiliser cette option dans les
conditions normales, car les avertissements qu'elle engendre sont
supposés être inutiles dans les conditions normales de programmation.
(Cela ne veut pas dire que l'objectif soit atteint :^) .)



Merci pour toutes ces explications.

--
François Lafont
3 4 5 6 7