2011-10-27 2 views
2
function(char *a, char *b) 
{ 

    char newStr[100]; 

    strncpy(newStr, a, sizeof(newStr)); //Line 1 - copy no more than 100 bytes 

    strncat(newStr, b, (sizeof(newStr) - strlen(newStr))); //Line 2 - ? 

    newStr[99] = NULL; //Line 3 - null terminate string 

} 

Ligne 2: Correct de spécifier 100 octets moins strlen de ce qu'est un recopié d'un pour assurer que je ne copie pas sur les 100 octets?Comment: sauvegarder la mémoire - strncat()?

Merci.

+1

Ce doit être l'un des griefs les plus ennuyeux avec C - le fait que 'strncpy' ne * pas * retourner le nombre d'octets écrits ou un pointeur vers la fin, mais plutôt un pointeur vers le début (que nous connaissons déjà, puisque c'est l'un des arguments) ... –

+0

Même si vous avez besoin de mettre fin à la chaîne vous-même, 'NULL' est la mauvaise chose à utiliser. C'est une constante null * pointeur *. Un caractère nul est ''\ 0'' (ou juste' 0'). –

+0

@KerrekSB: 'strncpy' est une relique d'une autre époque - elle n'a jamais été utilisée pour remplacer' strcpy'. Utilisez 'strlcpy' à la place, bien qu'il ne soit pas disponible partout. –

Répondre

2

C'est presque correct. Tout d'abord, la ligne de terminaison NUL:

newStr[99] = NULL; 

est erronée.

strncat toujours NUL-teriminates, et le troisième paramètre est le nombre maximal d'octets à écrire PAS, y compris le NUL. Hypothétiquement, si strncat ne terminait pas NUL, le problème avec cette ligne est qu'elle écrirait toujours sur le dernier élément du tableau, même si la chaîne réelle pourrait être beaucoup plus courte. Si a et b étaient "Hello" et "world!", Le tableau final serait:

H | e | l | l | o |, | | w | o | r | l | d |! | g | i | b | b | e | r | i | s | h

gibberish représente le contenu précédent de la matrice à ces positions. Seulement à 99, après la plupart de ces restes non initialisés, il y aurait un NUL.

EDIT: En outre, Keith a raison à propos de strncpy. Sa fonction est en partie droite, mais la seconde fonction peut déborder du buffer, puisqu'elle ne prend pas en compte la chaîne qui est déjà là. Les deux lignes combinées peuvent écrire 199 caractères (y compris NUL).

En outre, j'avais tort sur le troisième paramètre, y compris le NUL. Cela nous laisse (modifié à partir de Keith):

void function(char *a, char *b) 
{ 
    char newStr[100]; 

    /* Make newStr an empty string so you can catenate onto it */ 
    newStr[0] = '\0'; 
    strncat(newStr, a, sizeof newStr - 1); 
    strncat(newStr, b, sizeof newStr - strlen(newStr) - 1); 

    /* Presumably you do something with newStr here */ 
} 
+0

ok, je vois ce que vous dites sur pourquoi strncat() doit se terminer automatiquement par null. Utiliser les octets et strlen pour obtenir le bon nombre d'octets dans strncat() semblait étrange, mais sur ce système ils sont la même chose. –

+0

@Tommy, voir ma mise à jour. Il y a quelques corrections importantes. –

3

strncpy() ne fait pas ce que vous pensez probablement qu'il fait.

strncat() est une version «plus sûre» de strcat() qui vous permet de spécifier la taille de la matrice cible.

strncpy() est n'est pas la version "plus sûre" correspondante de strcpy(). Si le tableau cible est trop grand, strncpy() le remplira par des caractères nuls; 99% du temps, cela est inutile, car vous n'avez besoin que d'un seul '\0' pour marquer la fin d'une chaîne. Pire, si le tableau cible est trop petit, strncpy() va copier autant de caractères qu'il le peut et laisser la cible non terminée.

strncpy() a été conçu pour une structure de données obscure utilisée par les premiers systèmes Unix pour stocker les noms de fichiers. Un nom de fichier était stocké dans une mémoire tampon de 14 octets de longueur fixe complétée par des octets nuls. Si le nom de fichier avait exactement 14 caractères, il n'y aurait pas de terminateur nul. Ce n'est pas une chaîne.

Dans le cas peu probable que ce soit le type de structure de données que vous voulez, alors strncpy() est juste l'affaire. Sinon, ne l'utilisez pas; il suffit d'utiliser strcpy() après avoir confirmé que la cible est assez grande.

Voilà comment je pourrais écrire cette fonction:

void function(char *a, char *b) 
{ 
    char newStr[100]; 

    /* Make newStr an empty string so you can concatenate onto it */ 
    newStr[0] = '\0'; 
    strncat(newStr, a, sizeof newStr - 1);     /* edited */ 
    strncat(newStr, b, sizeof newStr - strlen(newStr) - 1); /* edited */ 

    /* Presumably you do something with newStr here */ 
} 

Notes:

  1. Déclare le type de retour de la fonction. Si vous ne le déclarez pas explicitement, votre compilateur sera probablement int par défaut, mais c'est un mauvais style et une fonctionnalité de langage obsolète.
  2. Évitez strncat().
  3. J'ai utilisé '\0', et non NULL, pour mettre fin à la chaîne. NULL est un zéro pointeur constante; ne l'utilisez pas pour indiquer un caractère nul .

Il y a une inefficacité importante ici: la deuxième strncat() doit re-numérisation depuis le début de newStr. Pour un petit nombre de chaînes courtes, ce n'est pas une grosse affaire, mais pour un grand nombre de chaînes qui sont converties en un grand tableau cible, cela peut causer de sérieux ralentissements. Il existe des moyens de contourner ce problème, mais ils sont soit non standard (strlcpy(), strlcat()) ou incommodes.

EDIT: Merci à Matthew d'avoir signalé des erreurs dans mon code. Je pense Je les ai fixés; Je suis sûr que je peux compter sur quelqu'un pour me frapper au-dessus de la tête si j'ai remplacé les vieilles erreurs par de nouvelles.

Une alternative est:

snprintf(newStr, sizeof newStr, "%s%s", a, b); 
+0

Votre deuxième appel strncat est faux, car il écrira 100 octets * après * la chaîne existante. –

+0

Et le premier est éteint par un (j'ai fait la même erreur dans ma réponse originale). –

+0

+1 pour 'snprintf' car vous pouvez spécifier"% s% s% s% s "et ne pas avoir à vous soucier de l'O (n * m) caché dans les' strcat() 's répétées – Dave

Questions connexes