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
Gabriel Dos Reis
"Charlie Gordon" writes:

[...]

| C'est plutôt une limitation stupide du compilateur si on ne peut pas
| désactiver sélectivement chaque type de warning.

Dans la version experimentale de GCC, il est possible de desactiver
les warnings selectivement.

Je ne suis pas tout a fait d'accord, mais comme je l'ai dit, la
nouvelle generation est plus enthousiaste que moi.

[...]

| Nous sommes hélas tous d'accord.

Yup.


-- Gaby
Avatar
Charlie Gordon
"Marc Boyer" a écrit dans le message
de news:

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


Effectivement, ils ne sont pas totalement d'accord, et moi non plus:

Gabriel Dos Reis écrit:

"Je n'ecris pas ce que je considere une abomination, e.g. le cast
"ci-dessus avec ou sans commentaire.

D'accord

"Et je trouve aussi que les pragmas sont des abominations,

D'accord

"Je laisse le compilateur raler -- c'est une limitation stupide du langage.

Pas d'accord : je ne laisse pas le compilateur râler, j'essaie de désactiver
le warning.
C'est plutôt une limitation stupide du compilateur si on ne peut pas
désactiver sélectivement chaque type de warning.

"Certains utilisent l'ignoble __attributte__((unused)) de GCC, mais
"je ne l'ecris pas dans mes propres codes.

D'accord, et quand je n'ai pas le choix, j'encapsule ça dans un #define
UNUSED(x) pour éviter de river le code à gcc

"Je n'ecris pas et je n'enseigne pas à mes eleves à écrire du code
"populaire. J'essaie d'ecrire du code simple, correct, lisible et
"maintenable. (Je n'y arrive pas toujours, mais j'essaie).

Tout à fait d'accord.

"Mutiler le code (avec ou sans commentaire) ne fait qu'empirer une situation
"deja pas rose, si tu veux mon avis.

Je suis d'accord aussi, si on peut éviter de rajouter ce que j'appellerais
plutôt des scories ou du bruit, cela facilite la lecture. Quand on n'a pas
le choix, et si l'ajout n'est pas explicite comme UNUSED(x) alors le
commentaire est nécessaire. La situation n'est pas "rose" parce que le C
est un langage difficile à maitriser réellement. Il semble facile d'accès à
la lecture du K & R, et on remarquera que leurs exemples sont
remarquablement exempts de "scories", mais dès qu'on se confronte à des
problèmes réels en C, il devient extrêmement difficile de ne pas se vautrer.
La sanction de l'approximation en l'occurrence est plus violente en C que
dans la plupart des autres langages : trou de sécurité ou crash pur et
simple. Au fur et à mesure qu'on persiste à programmer dans ce langage par
goût de la concision et des performances, on apprend l'humilité et la
méfiance systématique. Relire le code pour y trouver des problèmes est en
soi un sport difficile, avec des chances de succès parfois très minces
(pensez APL, C++, Perl...). En C, c'est encore possible et je m'y colle
très souvent comme sans doute aussi Gabriel : or il est d'autant plus
pénible d'avoir à filtrer visuellement et intellectuellement les acrobaties
inutiles que sont les casts en void des parametres non utilisés ou les
résultats non testés de certaines fonctions, que l'attention doit être plus
importante encore sur cette construction éminement dangereuse qu'est le
cast.

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

Tout à fait d'accord, cf ci-dessus.

"Ensuite, autant que faire se peut, un programme doit être simple et
"maintenable comme l'a dit Gabriel Dos Reis. Quand je relis des programmes
"que j'ai écrits il y a à peine quelques années je suis effarré par
"tant d'amateurisme.

Nous sommes hélas tous d'accord.

"Le problème vient du fait qu'il n'y a pas de solution miracle, il
"est même difficile de trouver un style en C qui garde une cohérence
"entre les programmes,

Pareil.

"s'interdire d'utiliser certaines fonctions même si
"c'est justifié n'est que superficiel bien que sans doute utile.

Je trouve que c'est utile et justifié. En fait je trouve même que le
contraire est inutile et sans justification.

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


Pas de problème, j'aime à partager mon expérience.

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.


Pour ceux qui ne connaissent pas : -Werror est une option de gcc qui fait
traite les warning comme des erreurs et donc arrête le traitement du
Makefile.
Je traite aussi systématiquement tous les warning (emacs process buffer et
^X^N ne fait pas la différence avec les erreurs ;-) mais tous nos
développeurs n'ont pas cette habitude et compilent dans des shells séparés,
donc -Werror est une saine école de rigueur.

Chqrlie.




Avatar
Gabriel Dos Reis
(Marc Espie) writes:

[...]

| si j'avais du temps a y consacrer (mais mon temps est deja trop occupe),
| ca vaudrait vraiment le coup de jeter GCC aux orties et de developper
| un vrai compilo mieux foutu sous license BSD...

Nous sommes d'accord :-)

-- Gaby
Avatar
espie
In article ,
Gabriel Dos Reis wrote:
"Charlie Gordon" writes:

[...]

| C'est plutôt une limitation stupide du compilateur si on ne peut pas
| désactiver sélectivement chaque type de warning.

Dans la version experimentale de GCC, il est possible de desactiver
les warnings selectivement.


Allez, je vais etre mauvaise langue: la version experimentale de GCC,
elle a plein de defauts qui compensent largement ces quelques qualites...
en particulier, elle raaaame atrocement (s'il n'y avait que ca... je
citerais bien la gestion memoire calamiteuse du GC, l'extreme duplication
des structures RTL + SSA, le nombre ecrasant de passes d'optimisation
qui se marchent dessus, et cette satanee fonction reload() que personne
ne se resoud a jeter).

Mais bon, c'est un package... il n'y a pas moyen d'avoir la simplicite
du generateur de code d'un vieux GCC avec le parseur revue et corrige
des versions recentes, pour plein de raisons. D'abord, parce que ca ne
fait pas partie des preoccupations des gens qui paient le developpement
de GCC (ben oui, une grosse partie des gens qui bossent sur gcc sont
employes a plein temps de redhat, suse, et autres apple). Ensuite, parce
que Stallman et co ont decide il y a bien longtemps que GCC devrait rester
monolithique, par paranoia des developpements proprietaires, et donc il
est presqu'impossible de greffer une front-end recente sur un back-end
plus ancien...

si j'avais du temps a y consacrer (mais mon temps est deja trop occupe),
ca vaudrait vraiment le coup de jeter GCC aux orties et de developper
un vrai compilo mieux foutu sous license BSD...

Avatar
Antoine Leca
En news:467e31c2$0$29044$,
Charlie Gordon va escriure:
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,


Oui. D'ailleurs elle existe. Elle s'appelle... strncpy() ! (nom mal choisi
ÀMHA, mais c'est un autre problème, cf. infra.)

à savoir si la chaine passée en source est d'une longueur
supérieure à celle du buffer destination.


Non. Le fait que la chaîne source soit trop longue n'est PAS un probleèm
important pour strncpy(), du moins telle qu'elle est définie. Si c'est un
problème pour toi, il te faut te rabattre sur une autre fonction (strlcpy()
est un candidat évident).

Si on connait déjà la longueur de la chaine,
<couic>


Oui. Mais ce n'est pas non plus le cas d'emploi de strncpy()


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



On pourrait citer, voire même commencer par, la raison d'être de strncpy(3),
à savoir le remplissage des entrées des noms dans le système de fichiers de
Unix, entrées qui étaient sur 14 (parfois 30) octets avec remplissage à 0
mais sans le 0 final quand le nom faisait exactement 14 octets.

(J'ai été attrapé par cela le jour où j'ai codé un utilitaire qui manipulait
ces entrées, _sans_ utiliser strncpy(); résultat, je ne marquais pas /tous/
les octets à 0, chose que le noyau supposait être faite, et bien sûr
direction le Oops incompréhensible un peu plus tard... sans compter le
comportement complètement incohérent des fichiers qui disparaissaient tandis
que d'autres avec des noms incohérents surgissaient de nulle part ;-))


Je te l'accorde, c'est un cas extremement rare.



Surtout en 2007 :-)

Et ca n'est pas sur que strncpy produise le code le plus simple
a relire



Ce qui me gêne surtout dans cette histoire, c'est que le nom n'est pas
intuitif. Cette fonction se serait appellée memcpyfill() que personne n'en
serait là à discuter du sujet (en passant sur le fait qu'il ne fallait pas
dépasser 8 caractères).

(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 n'y a guère que son nom qui soit bâtard, la fonctionnalité, elle, est
logique, même si en 2007 les champs texte à longueur limite sont totalement
désuets, surtout en C (par contre, si tu dois te palucher des fichiers texte
à largeur fixe, typique des programmes de gestion Cobol, cela devient
nettement moins désuets; le seul souci, c'est que strncpy() remplit avec des
NUL, et que Cobol attendrait plutôt des X'40', autrement dit des espaces.)


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.


Là, c'est un point de vue qui se défend. Mais pour strncpy() comme pour
gets(), le poids de l'usage était trop lourd, manifestement...


et les cas où elle est utilisée à mauvais escient et avec
pratiquement toujours des conséquences fâcheuses sont légion.


Oui, là on est d'accord.


Antoine



Avatar
Mihamina (R12y) Rakotomandimby
Marc Espie - <f5o5iv$1olh$ :

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


Elle est affectée ou pas, la variable?

Avatar
Charlie Gordon
"Mihamina (R12y) Rakotomandimby" a écrit
dans le message de news:
Marc Espie - <f5o5iv$1olh$ :

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


Elle est affectée ou pas, la variable?


Voici un exemple de ce que gcc ne sait pas reconnaitre:

void func(int n) {
int i;
int j; /* variable non initialisée */

for (i = 0; i < 100; i++) {
do_something_complicated(i);
if (n == 0)
j = do_something_else(i);
do_some_more_stuff(i);
}
if (n == 0)
return j;
return 0;
}

Comme tu peux le voir, j n'est utilisé que si n est nul, et il est
effectivement initialisé dans tous les cas où n est nul, mais c'est non
trivial de s'en assurer quand on est un compilateur.
Donc on a un warning sur le "return j;" comme quoi j serait "non
initialisé".
On supprime ce warning en initialisant j à n'importe quoi dans sa
définition. (ex: int j = 0; /* gcc needs j initialized here */ )

On répugne à ajouter ces initialisations inutiles qu'il faut
systématiquement documenter comme telles, faute de quoi le prochain
relecteur risque de les supprimer et le warning revient.
Dans des cas extrêmes, ces initialisations peuvent ralentir un micro chouia
une boucle interne super optimisée à l'huile de poignet.

Le fait est que le compilo ne peut pas tout détecter. Mais en général il
fait un meilleur travail de vérification que la majorité des programmeurs,
et si on lui demande correctement, il le fait gratuitement, tout le temps, y
compris quand le programmeur est fatigué et même les génies (de
l'informatique) ne sont pas des surhommes, il font des fautes d'inattention
comme tout le monde.

Mon expérience, c'est qu'il est plus productif de faire faire un maximum de
vérifications par le compilateur, quitte de temps en temps à devoir modifier
un code trop subtil, qui de toute façon finira par devoir être maintenu par
un programmeur moins velu et sera une source de bugs subtils.

Chqrlie.


Avatar
Sylvain
Charlie Gordon wrote on 26/06/2007 01:20:

Voici un exemple de ce que gcc ne sait pas reconnaitre:

void func(int n) {
int i;
int j; /* variable non initialisée */

for (i = 0; i < 100; i++) {
do_something_complicated(i);
if (n == 0)
j = do_something_else(i);
do_some_more_stuff(i);
}
if (n == 0)
return j;
return 0;
}

Comme tu peux le voir, j n'est utilisé que si n est nul, [...]
Dans des cas extrêmes, ces initialisations peuvent ralentir un micro chouia
une boucle interne super optimisée à l'huile de poignet.


pourquoi, dans un tel cas, l'huile de poignet ne tapote pas :

void func(int n) {
int i;
int retCode = 0; /* or int retCode; */

if (0 == n){
for (i = 0; i < 100; i++) {
do_something_complicated(i);
retCode = do_something_else(i);
do_some_more_stuff(i);
}
}
else {
for (i = 0; i < 100; i++) {
do_something_complicated(i);
do_some_more_stuff(i);
/* retCode = 0; */ /* depending on initial def. */
}
}
return retCode;
}

placer des tests invariants dans des boucles ne m'a paru élégant.

Sylvain.

Avatar
Charlie Gordon
"Sylvain" a écrit dans le message de news:
46805045$0$5089$
Charlie Gordon wrote on 26/06/2007 01:20:

Voici un exemple de ce que gcc ne sait pas reconnaitre:

void func(int n) {
int i;
int j; /* variable non initialisée */

for (i = 0; i < 100; i++) {
do_something_complicated(i);
if (n == 0)
j = do_something_else(i);
do_some_more_stuff(i);
}
if (n == 0)
return j;
return 0;
}

Comme tu peux le voir, j n'est utilisé que si n est nul, [...]
Dans des cas extrêmes, ces initialisations peuvent ralentir un micro
chouia une boucle interne super optimisée à l'huile de poignet.


pourquoi, dans un tel cas, l'huile de poignet ne tapote pas :

void func(int n) {
int i;
int retCode = 0; /* or int retCode; */

if (0 == n){
for (i = 0; i < 100; i++) {
do_something_complicated(i);
retCode = do_something_else(i);
do_some_more_stuff(i);
}
}
else {
for (i = 0; i < 100; i++) {
do_something_complicated(i);
do_some_more_stuff(i);
/* retCode = 0; */ /* depending on initial def. */
}
}
return retCode;
}

placer des tests invariants dans des boucles ne m'a paru élégant.


Parce que le code en question n'était là que pour illustrer un cas où gcc
produit un warning apparemment injustifié (encore que je n'aie pas vérifié,
j'ai improvisé ces lignes au hasard).

N'oublie pas que ce code n'est qu'un exemple, les do_something... sont des
gros blocs de code qu'il est malvenu et inélégant de dupliquer.
Encore sur l'élégance : 0 == n est une faute de goût qui détonne.
Pourquoi ignorer les conventions de nommages apparentes dans ces quelques
lignes ?
J'ai utilisé des _ pour séparer les mots dans les symboles : retCode est un
intrus ici, ret_code pourrait faire l'affaire, ret ou res encore mieux.
D'ailleurs pourquoi retCode ? ce n'est pas un code, tout au plus une value.

Chqrlie.


Avatar
Sylvain
Charlie Gordon wrote on 26/06/2007 02:41:

Parce que le code en question n'était là que pour illustrer un cas où gcc
produit un warning apparemment injustifié (encore que je n'aie pas vérifié,
j'ai improvisé ces lignes au hasard).


la question reste: et si gcc émettait ses foutu warning lorsque
finalement une autre écriture serait en fait bienvenue.

Encore sur l'élégance : 0 == n est une faute de goût qui détonne.


??

Pourquoi ignorer les conventions de nommages apparentes dans ces quelques
lignes ?


shame on me, j'aurais pu suivre le modèle en effet.

D'ailleurs pourquoi retCode ? ce n'est pas un code, tout au plus une value.


je pensais resCode (result_code) mais j'ai fourché ... comme sur la
position de "/* retCode = 0; */" qui doit se lire en sortie de boucle
naturellement.

Sylvain.