strcpy, strncpy și strdup în C

Șiruri și memorie (string.h) C89 (strdup: C23)

Copiază un șir C într-un alt tablou (strcpy, strncpy) sau într-o zonă nouă alocată cu malloc (strdup).

Sintaxă
#include <string.h>
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);
char *strdup(const char *s);   // C23

Ce face strcpy, strncpy și strdup

În C nu poți atribui un șir cu = unui tablou (nume = "Ana"; nu compilează). Pentru copiere folosești strcpy(dest, src), care copiază caracterele din src, inclusiv terminatorul '\0'. Tabloul dest trebuie să fie destul de mare: strcpy nu verifică.

strncpy(dest, src, n) scrie exact n octeți în dest. Dacă src este mai scurt, completează restul cu zerouri; dacă este mai lung sau egal, nu mai pune terminatorul. De aceea, după strncpy pui de obicei manual dest[n - 1] = '\0'. Pentru copieri cu limită, snprintf(dest, sizeof dest, "%s", src) este de multe ori mai simplu.

strdup(s), intrată în standard în C23 (înainte era doar POSIX), alocă cu malloc o zonă de strlen(s) + 1 octeți și copiază șirul acolo. Rezultatul trebuie eliberat cu free. Dacă memoria nu poate fi alocată, întoarce NULL.

Parametri

Parametru Descriere
dest Tabloul în care se scrie. Trebuie să aibă loc pentru tot șirul și pentru '\0'.
src Șirul copiat. Zonele dest și src nu au voie să se suprapună.
n La strncpy: numărul exact de octeți scriși în dest.

Ce returnează

strcpy și strncpy întorc dest. strdup întoarce un pointer la copia nouă sau NULL.

Exemple

Exemplul 1: Copiere cu strcpy

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

int main(void) {
    char oras[16];
    strcpy(oras, "Iași");
    printf("Orașul: %s\n", oras);
    strcpy(oras, "Timișoara");
    printf("Orașul: %s\n", oras);
    return 0;
}

Rezultat

Orașul: Iași
Orașul: Timișoara

Exemplul 2: strncpy taie textul și trebuie terminat manual

Bufferul are 6 octeți. strncpy copiază primii 6 octeți din „Constanța” fără terminator, așa că punem '\0' pe ultima poziție și rămân 5 caractere.

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

int main(void) {
    char scurt[6];
    strncpy(scurt, "Constanța", sizeof scurt);
    scurt[sizeof scurt - 1] = '\0';
    printf("[%s]\n", scurt);
    return 0;
}

Rezultat

[Const]

Exemplul 3: strdup: copie pe heap

Copia este independentă de original: modificarea ei nu schimbă textul inițial. La final, memoria se eliberează cu free.

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

int main(void) {
    const char *original = "ana popescu";
    char *copie = strdup(original);
    if (copie == NULL) {
        return 1;
    }
    copie[0] = 'A';
    copie[4] = 'P';
    printf("original: %s\ncopie:    %s\n", original, copie);
    free(copie);
    return 0;
}

Rezultat

original: ana popescu
copie:    Ana Popescu

Greșeli frecvente

  • Tablou destinație prea mic: char s[4]; strcpy(s, "Iași"); scrie 6 octeți (Iași are 5 în UTF-8, plus terminatorul) într-un loc de 4. Este un buffer overflow, una dintre cele mai cunoscute surse de vulnerabilități.
  • Crezi că strncpy pune mereu '\0'. Nu îl pune când sursa are cel puțin n octeți.
  • Uiți free după strdup: memoria rămâne ocupată până la sfârșitul programului.

Comenzi înrudite

  • strcat și strncat: Lipește un șir la sfârșitul altui șir aflat într-un tablou destul de mare.
  • strlen: Numără caracterele unui șir C până la terminatorul ‘\0’, fără să-l includă.
  • memcpy: Copiază n octeți dintr-o zonă de memorie în alta; zonele nu au voie să se suprapună.
  • snprintf și sprintf: Formatează text ca printf, dar îl scrie într-un tablou de caractere; snprintf nu depășește niciodată dimensiunea dată.
  • malloc: Cere sistemului un bloc de memorie de mărimea dată, în timpul rulării, și returnează adresa lui sau NULL dacă nu reușește.
  • free: Eliberează un bloc alocat cu malloc, calloc sau realloc, ca memoria să poată fi refolosită.