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

Futures page man str[n]cat

89 réponses
Avatar
Marc Boyer
Voici donc ce qui devrait arriver dans les pages de man.
Par rapport à ce que nous avions proposé, les parties "WARNING"
et "BUG" ont été oublié, pour ne reprendre que la description
de ce qu'elles font, pas les mises en garde.




STRCAT(3) Linux Programmer's Manual STRCAT(3)

NAME
strcat, strncat - concatenate two strings

SYNOPSIS
#include <string.h>

char *strcat(char *dest, const char *src);

char *strncat(char *dest, const char *src, size_t n);

DESCRIPTION
The strcat() function appends the src string to the dest
string, overwriting the null character (`\0') at the end of
dest, and then adds a terminating null character. The
strings may not overlap, and the dest string must have
enough space for the result.

The strncat() function is similar, except that

* it will use at most n characters from src; and

* src does not need to be null terminated if it contains n
or more characters.

As with strcat(), the resulting string in dest is always
null terminated.

If src contains n or more characters, strcat() writes n+1
characters to dest (n from src plus the terminating null
character). Therefore, the size of dest must be at least
strlen(dest)+n+1.

A simple implementation of strncat() might be:

char*
strncat(char *dest, const char *src, size_t n)
{
size_t dest_len = strlen(dest);
int i;

for(i = 0 ; i < n && src[i] != '\0' ; i++)
dest[dest_len + i] = src[i];
dest[dest_len + i] = '\0';
return dest;
}

RETURN VALUE
The strcat() and strncat() functions return a pointer to the
resulting string dest.

CONFORMING TO
SVr4, 4.3BSD, C89, C99.

SEE ALSO
bcopy(3), memccpy(3), memcpy(3), strcpy(3), strncpy(3),
wcscat(3), wcsncat(3)




--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)

10 réponses

Avatar
espie
In article ,
Gabriel Dos Reis wrote:
Je ne pense pas "donner l'habitude de voir des warnings et de trouver
ca normal".


De toutes facons, gcc est assez minable sur pas mal de warnings et de
non warnings.

Je dis pas que c'est facile, comme boulot, mais les warnings du style
`la comparaison en question est toujours valide vu le type de donnees
considere', c'est beaucoup plus du bruit qu'autre chose sur du code
vaguement portable... et ca a tendance a cacher d'autres vrais problemes.

A cote de ca, ca s'est un peu ameliore cote suivi de la vie des variables,
et il ne sort presque plus de `variable not initialized' qui ne correspondent
a rien...

Avatar
Thierry B.
--{ Gabriel Dos Reis a plopé ceci: }--

| >| void foo(int x, int y){
| >| (void) y; // unused parameter, parce que blabla...
|
| Je trouve ça plutôt bien. Parfois, il m'arrive d'écrire une
| fonction pour laquelle je sais que je vais avoir besoin un
| jour ou l'autre de paramètres supplémentaires.
|
| (void) y; /* unused parameter, parce que blabla... */

Je ne sais pas si elle est orthodoxe : elle ne passe pas le
"code review" chez moi, et un etudiant qui me rend un projet avec ce
machin la dedans aura probablement une note proche de F.

Oui, m'sieur.


void foo(int x, int y)
{

if (0 != y)
{
abort_retry_cancel("param y (%d) must be 0n", y);
}

/* et maintenant du code qui fait des choses */
}

C'est mieux, là ?

Maintenant, si je travaille dans un projet ou le manager a decide
cela, je l'ecrirai mais je lui dirai ce que j'en pense.


Ah, oui, le manager...

--
Why drink and drive when you can smoke and fly?

Avatar
Gabriel Dos Reis
"Thierry B." writes:

| void foo(int x, int y)
| {
|
| if (0 != y)
| {
| abort_retry_cancel("param y (%d) must be 0n", y);
| }
|
| /* et maintenant du code qui fait des choses */
| }
|
| C'est mieux, là ?

Pourquoi insister a aller au dela du code simple et correct ?
Si "y" ne contribue pas a la semantique de la fonction, pourquoi
insister a l'utiliser ?

-- Gaby
Avatar
Charlie Gordon
"Harpo" a écrit dans le message de news:
467b4ec3$0$5072$
On Fri, 22 Jun 2007 03:06:40 +0200, Gabriel Dos Reis wrote:

Pourquoi insister a aller au dela du code simple et correct ?
Si "y" ne contribue pas a la semantique de la fonction, pourquoi
insister a l'utiliser ?


Il peut y avoir des raisons qui tiennent à des conventions d'appel.

int f1(int x, int y) {
return(x);
}

int f2(int x, int y) {
return(x % y);
}

// code appelant
typedef int (*Fun)(int, int);
Fun f[] = {f1, f2};
...
i = get_fun_index(foo, bar);
n = f[i](a, b);
...

Avec l'option de warning appropriée, le compilateur indique que le
paramètre y est inutilisé dans la fonction f1 et les warnings sont
fatigants.


Non seulement fatigants, mais parfaitement inutiles, et en l'occurrence
insupportables puisque j'utilise aussi -Werror.
Le cas ci-dessus illustre parfaitement la raison qui me fait désactiver ce
warning.
Je trouve aussi particulièrement hideux et lourdingue d'avoir à documenter
les parametres non utilisés de certaines fonctions avec des périphrases
(void)x ou UNUSED(x)...
Omettre le nom de paramètre dans le prototype peut sembler une piste
intéressante, mais en pratique cela obscurcit le code car le nom du
parametre non utilisé est une information utile, surtout quand il y en a
plusieurs.

Chqrlie.


Avatar
Charlie Gordon
"Marc Boyer" a écrit dans le message
de news:
"Marc Boyer" a écrit dans le
message
de news:
Voici donc ce qui devrait arriver dans les pages de man.
Par rapport à ce que nous avions proposé, les parties "WARNING"
et "BUG" ont été oublié, pour ne reprendre que la description
de ce qu'elles font, pas les mises en garde.


Autant dire que la proposition a été vidée de sa substance.
Il faut *FORTEMENT* décourager l'utilisation de ces fonctions décadentes,
surtout strncpy !


C'est une page de man, pas un cours de C.


La page de manuel est là pour dire comment utiliser une API et préciser son
comportement.
La réponse à cette question en ce qui concerne strnpy est simple :

- comment utiliser strncpy ? -> *IL NE FAUT PAS*
- que fait strncpy ? -> explication détaillée du comportement pervers de
cette fonction inutile.

Se contenter de décrire les particularités de leur sémantique ne sert à
rien
: la plupart des programmeurs, même chevronnés pensent en connaitre déjà
l'usage et l'intérêt.


Est-ce que ceux ci liraient la notice "Warning" en fin de page de man?


C'est pourquoi il faut mettre cette recommandation en tête de l'explication.

-O2
-funsigned-char


Est-ce raisonnable ? Est-ce que ça ne laisse pas le programmeur se
reposer sur le fait que les char sont non signés ? Ne vaut il mieux
pas le forcer à précier le signe des char à chaque usage ?


D'une part les char signés par défaut sont une abérration historique qui est
notoirement incohérente avec la sémantique de certaines fonctions de la lib
C :

- getc() retourne un int avec la valeur d'un unsigned char ou EOF. On ne
peut donc pas écrire:

if (getc(fp) == 'é') {
/* jamais vrai si char est signé par défaut */
}
if (getc(fp) == 'ÿ') {
/* vrai en fin de fichier ! */
}
- les functions de test de ctype.h ne peuvent pas distinguer entre EOF et
'ÿ'. Bien sûr il faut leur passer un int avec valeur de unsigned char ou
EOF, mais les 1% de programmeurs qui comprennent la subtilité de cette
question trouvent bien lourd d'avoir à écrire :

int simple_parsenum(const char *p) {
int value = 0;
while (isdigit((unsigned char)*p)) {
value = value * 10 + (*p - '0');
}
return value;
}

D'autre part, dans le code produit chez nous, nous réservons le type char,
et en particulier char * aux chaines de caractères textuelles,
éventuellement encodées en 8 bits, par exemple en utf-8. Pour les autres
usages, nous utilisons des typedef appropriés :
- byte pour le contenu de fichiers, buffers ou flux binaires. (évidemment
c'est un unsigned char, mais byte est plus parlant)
- int8_t ou uint8_t pour les petits entiers que pour des raisons souvent
mauvaises on veut stocker dans des structures compactes sur 8 bits. On
pourrait aussi utiliser des bit fields.
- flag_t pour les champs de 1 bit dans des structures (c'est un typedef sur
unsigned int pour éviter le problème infâme du bitfield de 1 bit dont les
valeurs ne sont ni intuitives ni portables.

Je prends le risque que notre code ne soit pas portable sur des
environnements ou les char sont forcément signés par défaut. Je ne pense
pas que ce soit un risque réel, de telles cibles auront vraisemblablement
d'autres problèmes de portabilité bien plus lourds.

Notez qu'il reste quand même possible dans de telles conditions de
contourner cette limitation en utilisant le préprocesseur, mais c'est
dégueulasse.

-fstrict-aliasing -Wall -W -Werror -Wchar-subscripts -Wundef
-Wshadow
-D"index(s,c)=index__(s,c)"


C'est un truc à vous, ça...


Oui, c'est pour cacher l'éventuel déclaration de la fonction index de BSD
dont la présence combinée au warning -Wshadow déclanche des protestations
vaines à l'utilisation du même identifiant index pour nommer des variables
locales.

-Wcast-align -Wwrite-strings -Wsign-compare
-Wunused
-Wno-unused-parameter


Tient, pourquoi donc ?
Moi, je suis plutôt pour la démarche.
void foo(int x, int y){
(void) y; // unused parameter, parce que blabla...
}


Cette remarque a déjà produit ses effets... Je partage complêtement l'avis
de Harpo et Gabriel Dos Reis.

-Wuninitialized -Wpointer-arith -Wredundant-decls
-Wformat-nonliteral
-Wno-format-zero-length


Vous utilisez des *printf/*scanf sans format ?


Après préprocessing, et pas forcément pour printf lui même, il arrive que
nous ayons:

error_message_func("");

Ce qui fait glousser gcc. D'où le -Wno-format-zero-length

Le plus important dans toutes ces options, c'est -Werror ;-)

Chqrlie.



Avatar
Charlie Gordon
"Thierry B." a écrit dans le message de news:

--{ Gabriel Dos Reis a plopé ceci: }--

| >| void foo(int x, int y){
| >| (void) y; // unused parameter, parce que blabla...
|
| Je trouve ça plutôt bien. Parfois, il m'arrive d'écrire une
| fonction pour laquelle je sais que je vais avoir besoin un
| jour ou l'autre de paramètres supplémentaires.
|
| (void) y; /* unused parameter, parce que blabla... */

Je ne sais pas si elle est orthodoxe : elle ne passe pas le
"code review" chez moi, et un etudiant qui me rend un projet avec ce
machin la dedans aura probablement une note proche de F.

Oui, m'sieur.


void foo(int x, int y) {
if (0 != y)
{
abort_retry_cancel("param y (%d) must be 0n", y);
}
/* et maintenant du code qui fait des choses */
}

C'est mieux, là ?


Non, pas vraiment.
Pourquoi insister sur y alors qu'on veut justement l'oublier.
De plus je trouve cette tournure classique C particulièrement niaise.
Bien sûr je sais quelle bourde elle est censée éviter, mais il est bien plus
efficace de nos jours d'utiliser les warnings du compilateur pour détecter
entre autres les affectations douteuses dans l'expression d'un if, for ou
while.
En fait j'ai du mal avec 0 != y parce que je lis de grandes quantités de
code, pour des revues de qualification, et je trouve cela illisible.
J'accorde beaucoup d'importance à la formulation du code : ce qui se conçoit
bien s'énonce clairement. Or dans le cas présent, le test se lit : "la
valeur de 0 est elle y à cet instant ?"

Chqrlie


Avatar
Charlie Gordon
"Gabriel Dos Reis" a écrit dans le message de
news:
Marc Boyer writes:

| Le coup du (void) me semblait neutre: ajouté au coup
| par coup par le programmeur, et sans impact sur le code
| généré.
| Quel désavantage ? Il surprend le débutant.

Comme je l'ai dit plus tot, je voudrais du code simple, correct,
maintenable ou j'exprime directement les idees.
Si le code est simple, correct et maintenable sans la mutilation,
alors je considere que la mutilation n'est pas seulement un bruit de
fond mais une entrave.


Bien dit !
Et en C, Gaby il entrave un max ;-)

Chqrlie.

Avatar
Charlie Gordon
"Vincent Lefevre" <vincent+ a écrit dans le message de news:
20070620135823$
Dans l'article <46792e76$0$27404$,
Harpo écrit:

Oui, un code doit compiler sans warning, déjà ça fout la trouille à
celui qui ne l'a pas écrit et qui le compile (qu'est-ce que j'ai fait
encore comme connerie moi ?).


Enfin, il y a des warnings qui peuvent uniquement être dûs à des
limitations du compilateur (genre le compilateur qui indique que
telle variable n'a peut-être pas été initialisée). Et forcer une
initialisation à une valeur *arbitraire* peut rendre le code moins
compréhensible et moins maintenable, et empêcher des détections de
bugs avec des outils plus puissants.


Ces quelques rares cas ne pèsent pas lourd en regard des bugs éliminés par
ces warnings salvateurs.
On peut se fendre d'un commentaire pour expliquer l'initialisation a priori
inutile.
Notez aussi que gcc ne trace correctement l'initialisation des variables
qu'avec l'optimisation activée (-O2)

Chqrlie.


Avatar
espie
In article <467cc8ef$0$29304$,
Charlie Gordon wrote:
Omettre le nom de paramètre dans le prototype peut sembler une piste
intéressante, mais en pratique cela obscurcit le code car le nom du
parametre non utilisé est une information utile, surtout quand il y en a
plusieurs.


Dans ce cas, tu peux mettre le nom du parametre inutilise en commentaire...
(c'est generalement ce qui est fait dans le code C++, langage qui possede
cette fonctionnalite) Ca aura exactement autant de poids au niveau
semantique du programme.

Avatar
espie
In article <467cd247$0$11886$,
Charlie Gordon wrote:
La réponse à cette question en ce qui concerne strnpy est simple :

- comment utiliser strncpy ? -> *IL NE FAUT PAS*
C'est faux.


- que fait strncpy ? -> explication détaillée du comportement pervers de
cette fonction inutile.
C'est faux egalement, strncpy n'est pas totalement inutile.


Cette fonction sert exactement dans un cas de figure (plutot limite):
celui des enregistrements limites en taille en memoire, et qui ne sont
pas forcement termines par des zeros.

Il en existe sur au moins les systemes Unix. Par exemple, la structure
utmp est de ce type. Et les noms de fichiers dans les archives ar(1)
possedent aussi des caracteristiques etranges. C'est d'ailleurs assez
souvent le cas dans les formats d'archive un peu compacts: on va jusqu'au
bout d'un champ texte, quitte a virer le zero final.

Je te l'accorde, c'est un cas extremement rare. Et ca n'est pas sur que
strncpy produise le code le plus simple a relire (surtout par manque
de familiarite d'une bonne partie des gens avec ce type de construction),
mais ce n'est pas une fonction `totalement inutile'.