strncpy não faz o que você acha que ele faz…

Não é novidade para ninguém que não existem operadores que manipulam strings em C. Todos os tipos de manipulações desse tipo devem ser feitas através de chamadas de funções e existem três classes delas: As que começam com ‘str’, as que começam com ‘wcs’ e as que começam com ‘mem’, todas elas com protótipos definidos em string.h. O primeiro tipo lida com sequências de bytes onde cada byte (char) é um caractere. O segundo tipo lida com wide chars, através do tipo wchar_t. O terceiro lida com blocos de bytes, sem considerar o caractere terminador NUL ((char)0).

A não ser pelas funções ‘mem*’, as que manipulam strings geralmente não fazem qualquer verificação sobre o conteúdo do buffer. Se sua string tiver 1 caractere ou 4 bilhões, tanto faz. Para evitar comportamentos inesperados (sobrescritas, falha de proteção etc) existe a função de cópia de strings chamada strncpy (e sua equivalente wcsncpy). Supostamente ela copiará, no máximo, n characteres da string original, colocando um NUL no final da string destino se ela for menor que n caracteres. Mas, não é exatamente isso que strncpy faz!

Vou usar o código abaixo como teste e mostrar, no GDB, o que strncpy realmente faz…

#include <stdio.h>
#include <string.h>

void main(void)
{
  char s[21] = "12345678901234567890";

  /* Você espera obter "10\045678...", não é? */
  strncpy(s, "10", 20);

  printf("%s\n", s);
}

Ao executarmos o programa, colocando um breakpoint na linha contendo a chamada para strncpy e dando uma olhada no conteúdo de s, teremos:

$ gcc -g -o test test.c
$ gdb test
GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1
(gdb) l
1 #include <stdio.h>
2 #include <string.h>
3 
4 void main(void)
5 {
6   char s[21] = "12345678901234567890";
7 
8   strncpy(s, "10", 20);
9 
10   printf("%s\n", s);
(gdb) b 8
Breakpoint 1 at 0x400575: file test.c, line 8.
(gdb) r
Starting program: test 

Breakpoint 1, main () at test.c:8
8   strncpy(s, "10", 20);
(gdb) x/21bx s
0x7fffffffe010: 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38
0x7fffffffe018: 0x39 0x30 0x31 0x32 0x33 0x34 0x35 0x36
0x7fffffffe020: 0x37 0x38 0x39 0x30 0x00

Como é esperado, nesse ponto, a strings s tem 21 bytes e termina com 0x00. Mas, olhe só o que acontece ao executar strncpy:

(gdb) n
10   printf("%s\n", s);
(gdb) x/21bx s
0x7fffffffe010:	0x31 0x30 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffe018:	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffe020:	0x00 0x00 0x00 0x00 0x00

Ora, se a string sendo copiada tem apenas 2 bytes (0x31 e 0x30) e o final da string destino deve ser marcada com NUL, então onde estão os bytes seguintes que estavam lá antes?

A semântica de strncpy é sempre preencher o buffer todo! Isso é bem diferente de funções como snprintf, que pararão no ponto onde colocam um NUL no buffer destino…

Anúncios