Exatamente como isso soa, supondo que você esteja acostumado à maneira abreviada pela qual C e UNIX atribuem palavras, ele duplica as strings :-)
Tendo em mente que, na verdade, não faz parte do próprio padrão ISO C (a) (é uma coisa POSIX), está efetivamente fazendo o mesmo que o código a seguir:
char *strdup(const char *src) {
char *dst = malloc(strlen (src) + 1); // Space for length plus nul
if (dst == NULL) return NULL; // No memory
strcpy(dst, src); // Copy the characters
return dst; // Return the new string
}
Em outras palavras:
Ele tenta alocar memória suficiente para conter a cadeia antiga (mais um caractere '\ 0' para marcar o final da cadeia).
Se a alocação falhar, ele define errno
a ENOMEM
e retorna NULL
imediatamente. Definir errno
como ENOMEM
é algo que malloc
faz no POSIX, portanto não precisamos explicitamente fazê-lo no nosso strdup
. Se você não é compatível com POSIX, a ISO C não exige a existência de, ENOMEM
portanto, não a incluí aqui (b) .
Caso contrário, a alocação funcionou, então copiamos a string antiga para a nova string (c) e retornamos o novo endereço (que o chamador é responsável por liberar em algum momento).
Lembre-se de que é a definição conceitual. Qualquer escritor de biblioteca que mereça seu salário pode ter fornecido um código altamente otimizado direcionado ao processador específico que está sendo usado.
(a) No entanto, as funções que começam com str
e uma letra minúscula são reservadas pelo padrão para instruções futuras. De C11 7.1.3 Reserved identifiers
:
Cada cabeçalho declara ou define todos os identificadores listados em sua sub-cláusula associada e * opcionalmente declara ou define identificadores listados em sua sub-cláusula de instruções futuras da biblioteca associada. **
As instruções futuras para string.h
podem ser encontradas em C11 7.31.13 String handling <string.h>
:
Nomes de funções que começam com str
, mem
ou wcs
e uma letra minúscula podem ser adicionados às declarações no <string.h>
cabeçalho.
Portanto, você provavelmente deveria chamar de outra coisa, se quiser estar seguro.
(b) A alteração seria basicamente substituída if (d == NULL) return NULL;
por:
if (d == NULL) {
errno = ENOMEM;
return NULL;
}
(c) Observe que eu uso strcpy
para isso, pois mostra claramente a intenção. Em algumas implementações, pode ser mais rápido (já que você já sabe o tamanho) usar memcpy
, pois elas podem permitir a transferência de dados em partes maiores ou em paralelo. Ou talvez não :-) Mantra de otimização nº 1: "meça, não pense".
De qualquer forma, se você decidir seguir esse caminho, faria algo como:
char *strdup(const char *src) {
size_t len = strlen(src) + 1; // String plus '\0'
char *dst = malloc(len); // Allocate space
if (dst == NULL) return NULL; // No memory
memcpy (dst, src, len); // Copy the block
return dst; // Return the new string
}