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
Thierry B.
--{ Gabriel Dos Reis a plopé ceci: }--

| 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 */
| }
|

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 ?

En fait, c'est assez lié à mon contexte personnel. Je fais

pas mal de C pour bricoler des images. Il m'arrive parfois
d'imaginer un traitement, de le coder à la rache, tout en
sachant bien que plus tard, j'aimerais bien lui passer
un paramètre ou un flag supplémentaire.

Comme toutes mes fonctions de traitement sont dans une
kludgesque bibliothèque en .so, et que les petits machins
de traitement sont éparpillés aux quatre coins des disques,
j'essaye de me protéger des coredumps quand les signatures
ne concordent pas...

Je sais, c'est du bricolage, désolé :)


--
Il vaut mieux être belle et rebelle que moche et remoche.

Avatar
Thierry B.
--{ Charlie Gordon a plopé ceci: }--

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.


Ah ? C'est donc une mauvaise habitude ? Pourtant, ça m'a parfois
préservé de quelques belles bétises pénibles à retrouver...

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 ?"


"la valeur 0 est-elle _celle de_ y à cet instant ?"


--
Donc chez VU, on avait mis en place un système de boîtier très design,
qui permettait aux actionnaires de voter en appuyant au choix sur un
des trois boutons disponibles (oui, non, n'a pas compris la question).

Avatar
Charlie Gordon
"Marc Espie" a écrit dans le message de news:
f5iup0$2ukk$
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.



Si c'est vrai. Dans les cas que tu mentionnes ci-dessous, on aurait intérêt
à utiliser une fonction spécifique qui retourne l'information utile, à
savoir si la chaine passée en source est d'une longueur supérieure à celle
du buffer destination. Si on connait déjà la longueur de la chaine, et
qu'on a vérifié qu'elle peut "tenir" dans la structure destination, 1 memcpy
et 1 memset font l'affaire et sont bien plus explicites que strncpy dont
presque personne ne connait la sémantique précise.

- 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'.


Tu as décrit les raisons historiques qui ont produit cette fonction bâtarde.
Il eut été préférable de ne pas l'inclure dans la librairie C à l'époque
(c'est très ancien, 1976?) et en tous cas de ne pas la normer en 1986.
Après tout l'ANSI a supprimé à l'époque tout ce qui concernait Unix de trop
près (I/O et autres appels système), ils auraient dû aussi virer strncpy qui
n'était utilisée correctement que dans les drivers de système de fichier et
autres utilitaires système.

Je conseille vivement à ceux des lecteurs de ce forum qui estiment que
j'exagère de relire attentivement la spécification de cette saloperie qui
n'a rien à faire dans le standard.

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

copie dans dest les caractères de src jusqu'au n-ième en s'arrêtant au
premier ''.
elle complète alors la destination avec des '' jusqu'à la (n-1)-ième
position.
elle écrit donc toujours exactement n char dans la destination.

Donc si la destination est trop petite, la chaine qui y est recopiée n'est
pas terminée par un ''
si au contraire elle est suffisante, cette chaine est certes terminée par un
'', mais le reste de la destination est remis à '', ce qui pratiquement
toujours inutile et relativement coûteux pour des grands buffers.

Quand je dis que cette fionction ne sert à rien, je devrais plutot dire que
les cas où son comportement est nécessaire sont inexistants, et les cas où
elle est utilisée à mauvais escient et avec pratiquement toujours des
conséquences fâcheuses sont légion.

dont acte.

Chqrlie.


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

--{ Charlie Gordon a plopé ceci: }--

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.


Ah ? C'est donc une mauvaise habitude ? Pourtant, ça m'a parfois
préservé de quelques belles bétises pénibles à retrouver...


Utilise le bon outil pour cela : le compilateur et ses warnings, il te
pointera sans doute beaucoup d'autres bugs.

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 ?"


"la valeur 0 est-elle _celle de_ y à cet instant ?"


Non, se poser la question dans cet ordre est très dérangeant : 0 n'a pas de
valeur, 0 *est* une valeur.
L'expression écrite est donc : 0 est-il égal à la valeur de y
C'est trop surprenant comme tournure, d'ailleurs en programmation objet on
ferait le contraire : l'objet est toujours en premier, ici l'objet c'est y.
Même en restant en C de base, on met en premier la variable, cela donne un
ordre cohérent avec tous les autres idiomes du C :

for (i = 0; i < n; i++) ...
while (p < end) ...
switch (y) {
case xxx:...
}
if (y == FIRST_CASE) {
do something
}
if (y == BAD_CASE) }
do something else
}
mettre y en premier dans ces deux derniers tests rend beaucoup plus clair
que c'est la même variable qui est testée, c'est plus régulier, ça se lit
mieux, ça laisse moins de place aux bugs pour se cacher.

Non, pas de doutre, si cette contorsion syntaxique a eu son utilité dans des
temps anciens où les compilateurs étaient niais, elle est aujourd'hui
contre-productive et peut être remisée au musée des horreurs.

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)
| {
|
| if (0 != y)
| {
| abort_retry_cancel("param y (%d) must be 0n", y);
| }
|
| /* et maintenant du code qui fait des choses */
| }
|

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 ?

En fait, c'est assez lié à mon contexte personnel. Je fais

pas mal de C pour bricoler des images. Il m'arrive parfois
d'imaginer un traitement, de le coder à la rache, tout en
sachant bien que plus tard, j'aimerais bien lui passer
un paramètre ou un flag supplémentaire.

Comme toutes mes fonctions de traitement sont dans une
kludgesque bibliothèque en .so, et que les petits machins
de traitement sont éparpillés aux quatre coins des disques,
j'essaye de me protéger des coredumps quand les signatures
ne concordent pas...

Je sais, c'est du bricolage, désolé :)


Plusieurs conseils :
- mets toujours les prototypes des fonctions non-statiques dans un header et
inclus le partout où les fonctions sont utilisées, et bien sûr là où elles
sont définies
(en C, avec gcc, on peut forcer des vérifications strictes de cette
méthodologie
avec -Wredundant-decls -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations)
- utilise make et décris correctement les dépendances (gcc peut le faire
automatiquement)
- compile en mode C++ : les signatures seront vérifiées à l'édition de
liens, statique ou dynamique.

Chqrlie.

PS: ça m'arrive de coder à l'arrache, mais cette nouvelle la variante est
intéressante : http://www.cafenware.org/la-rache/
C'est un peu comme Lady Di qui travailla d'arrache-pied à la lutte contre
les mines anti-personnels


Avatar
Gabriel Dos Reis
"Thierry B." writes:

| --{ Gabriel Dos Reis a plopé ceci: }--
|
| >| 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 */
| >| }
| >|
| >
| > 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 ?
| >
| En fait, c'est assez lié à mon contexte personnel. Je fais
| pas mal de C pour bricoler des images. Il m'arrive parfois
| d'imaginer un traitement, de le coder à la rache, tout en
| sachant bien que plus tard, j'aimerais bien lui passer
| un paramètre ou un flag supplémentaire.

Donc, nous ne parlons pas de code de production, ou un projet a
rendre, mais bien d'un bricolage qui va disparaitre dans un avenir
tres immediat. En general, je n'ai pas de commentaire a faire dans ce
cas.

-- Gaby
Avatar
Ael Rowan Terence
"Charlie Gordon" a écrit dans le message de
news:467e3589$0$12382$
"Thierry B." a écrit dans le message de news:


if (y == FIRST_CASE) {
do something


L'avantage de l'écriture if (FIRST_CASE==y)
est de detecter l'erreur de frappe if (y = FIRST_CASE).

Avatar
Marc Boyer
Le 23-06-2007, Charlie Gordon a écrit :
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 :


Nous sommes d'accord.

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,
[SNIP]

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.


OK

-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.


Je n'ai peut-etre pas bien lu, mais je n'ai pas vu qu'ils partagent
le même avis. Si j'ai bien compris, Gabriel préfère laisser raler le
compilo, et Harpo (si j'ai bien compris le message du 21 juin) ne
juge pas choquant le (void).

De plus, j'avoue ne pas avoir réussi à comprendre la motivation
de Gabriel (et cela m'arrive souvent). Ton avis pourrait m'interesser
(ceci dit, vu que je vais etre hors news pendant une semaine,
poser des questions dont je ne pourrais pas lire la réponse
avant 8 jours, est-ce très poli ?).

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


Je ne l'utilise pas parce que je lis toujours mes warning,
mais dans un contexte de longue chaine de compilation, j'imagine
que c'est différent.

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



Avatar
Charlie Gordon
"Ael Rowan Terence" a écrit dans le message de news:
f5nr2s$1jm$

"Charlie Gordon" a écrit dans le message de
news:467e3589$0$12382$
"Thierry B." a écrit dans le message de news:


if (y == FIRST_CASE) {
do something


L'avantage de l'écriture if (FIRST_CASE==y)
est de detecter l'erreur de frappe if (y = FIRST_CASE).


Oui, je sais bien pourquoi cet idiome hideux est utile, mais il existe des
outils bien plus efficaces pour détecter ces fautes de frappe et aussi des
erreurs plus subtiles comme les confusions sur la précédence des opérateurs.
Tous les compilateurs modernes ont des options pour produire des warnings
sur ce genre de choses. C'est évidemment un scandale que ces avertissements
ne soient pas actifs par défaut, mais en les activant et en traitant
systématiquement les warnings (-Werror empêche de les ignorer ;-) on règle
ces problèmes sans avoir à écrire du code illisible.

gcc -Wall -W -Werror est un bon début.

Chqrlie.


Avatar
espie
In article <467f8efc$0$18494$,
Charlie Gordon wrote:

Oui, je sais bien pourquoi cet idiome hideux est utile, mais il existe des
outils bien plus efficaces pour détecter ces fautes de frappe et aussi des
erreurs plus subtiles comme les confusions sur la précédence des opérateurs.
Tous les compilateurs modernes ont des options pour produire des warnings
sur ce genre de choses. C'est évidemment un scandale que ces avertissements
ne soient pas actifs par défaut, mais en les activant et en traitant
systématiquement les warnings (-Werror empêche de les ignorer ;-) on règle
ces problèmes sans avoir à écrire du code illisible.

gcc -Wall -W -Werror est un bon début.


La je sens qu'on ne va pas etre d'accord. Les warnings de gcc, meme ceux
actives par -Wall -W, ne sont pas universels. Il y a une bonne dose de
controverse dedans. Deja, le coup des variables non utilisees qui oblige
a rajouter des __attribute__ un peu partout. Ensuite, certains warnings
lies a la portabilite qui sont *chiants* (par exemple, gcc me dit
regulierement que certains tests sont toujours vrais, ce qui est *normal*
puisque le test ne sert a quelque chose *que* sur une plateforme 64 bits.
Tu me diras, je pourrais mettre des #ifdef, mais beurk, quoi. Les #ifdef
plat de spaghetti, non. Surtout quand le code est plus clair lorsque le
test est toujours la (et qu'on sait bien qu'il va disparaitre a la
compilation s'il n'est pas necessaire). Sans compter tous les cas ou gcc
s'emmele les pinceaux dans un algorithme un peu complexe, et m'informe
que telle variable n'est pas forcement initialisee (alors qu'elle l'est),
ce qui m'obligerait, pour passer le cap du -Werror, a rajouter une affectation
totalement inutile...

Bon, c'est intrinseque au langage: les techniques de compilation actuelles,
extremement couteuses en temps de calcul dans le cas de gcc, ne savent
toujours pas se depetrer correctement de plein de situations courantes...
le langage n'aide pas... tous les systemes actuels rajoutent des annotations
supplementaires pour essayer d'en faire quelque chose de decent (mais bon,
rien que le fait qu'il faille *rajouter* des cast pour pouvoir ecrire
certaines fonctions de la libc temoigne des multiples deficiences du
langage).

Vouloir transformer C en quelque chose de suffisamment rigoureux pour
passer le -Werror conduit facilement a la folie... et j'en sais quelque
chose.

Par exemple, les warnings en question evoluent d'une version a l'autre...
je ne compte plus le nombre de fois ou le passage a une nouvelle version
de gcc m'a force a corriger ce genre de trucs...