Dans tous les scénarios de copie / déplacement de chaîne - strcat (), strncat (), strcpy (), strncpy (), etc. - les choses se passent beaucoup mieux ( plus sûres ) si quelques heuristiques simples sont appliquées:
1. Toujours NUL-fill votre (vos) tampon (s) avant d'ajouter des données.
2. Déclarez les tampons de caractères comme [SIZE + 1], avec une macro-constante.
Par exemple, étant donné:
#define BUFSIZE 10
char Buffer[BUFSIZE+1] = { 0x00 }; /* The compiler NUL-fills the rest */
nous pouvons utiliser du code comme:
memset(Buffer,0x00,sizeof(Buffer));
strncpy(Buffer,BUFSIZE,"12345678901234567890");
relativement sûr. Le memset () devrait apparaître avant strncpy (), même si nous avons initialisé Buffer au moment de la compilation, car nous ne savons pas ce que les autres codes placés dedans avant l'appel de notre fonction. Le strncpy () tronquera les données copiées en "1234567890" et ne les terminera pas par NUL. Cependant, comme nous avons déjà rempli tout le tampon NUL - sizeof (Buffer), plutôt que BUFSIZE - il est garanti qu'il y aura une terminaison finale NUL "hors de portée" de toute façon, tant que nous contraignons nos écritures à l'aide de BUFSIZE constante, au lieu de sizeof (Buffer).
Buffer et BUFSIZE fonctionnent également très bien pour snprintf ():
memset(Buffer,0x00,sizeof(Buffer));
if(snprintf(Buffer,BUFIZE,"Data: %s","Too much data") > BUFSIZE) {
/* Do some error-handling */
} /* If using MFC, you need if(... < 0), instead */
Même si snprintf () n'écrit spécifiquement que les caractères BUFIZE-1, suivis de NUL, cela fonctionne en toute sécurité. Donc, nous "gaspillons" un octet NUL superflu à la fin de Buffer ... nous évitons à la fois les conditions de dépassement de mémoire tampon et de chaînes non terminées, pour un coût mémoire assez faible.
Mon appel sur strcat () et strncat () est plus dur: ne les utilisez pas. Il est difficile d'utiliser strcat () en toute sécurité, et l'API pour strncat () est si contre-intuitive que l'effort nécessaire pour l'utiliser correctement annule tout avantage. Je propose le drop-in suivant:
#define strncat(target,source,bufsize) snprintf(target,source,"%s%s",target,source)
Il est tentant de créer un drop-in strcat (), mais ce n'est pas une bonne idée:
#define strcat(target,source) snprintf(target,sizeof(target),"%s%s",target,source)
car la cible peut être un pointeur (donc sizeof () ne renvoie pas les informations dont nous avons besoin). Je n'ai pas de bonne solution "universelle" aux instances de strcat () dans votre code.
Un problème que je rencontre fréquemment de la part des programmeurs «sensibles à strFunc ()» est une tentative de protection contre les débordements de tampon en utilisant strlen (). C'est très bien si le contenu est garanti pour être terminé par NUL. Sinon, strlen () lui-même peut provoquer une erreur de dépassement de tampon (conduisant généralement à une violation de segmentation ou à une autre situation de vidage de mémoire), avant que vous n'atteigniez le code "problématique" que vous essayez de protéger.