Cadenes de caràcters en C

Última modificació per editor1 el 2018/09/16 21:31

Objectius

  • Entendre les propietats de les cadenes de caràcters.
  • Comprendre els mètodes de tractament de cadenes de caràcters.
  • Conèixer alguns principis bàsics de la gestió segura de la memòria.

Introducció

Les cadenes de caràcters tenen un tractament especial en tots els llenguatges de programació. Encara que no són més que un vector de caràcters, la seva gran utilitat fa que en la pràctica es disposi de molts mètodes destinats a tractar aquest tipus de dades, i en molts llenguatges es defineixen com si es tractés d’un altre tipus de dades al nivell dels enters o els reals. En aquesta guía aprofundirem més en aquest tipus de dades i en les diferents opcions que ens proporciona el llenguatge C per a tractar-les.

1. Cadenes de caràcters o strings

1.1. Definició

Una cadena de caràcters o string és una seqüència de caràcters finalitzada pel caràcter ‘\0’. Aquesta seqüència de caràcters s’emmagatzema dins d’un vector de caràcters. Per tant, cal diferenciar entre la mida del vector i la mida de la cadena de caràcters. Per exemple, en la següent declaració:

char a[25]="abc";

Tenim un vector de 25 posicions (25 bytes). La cadena de caràcters, no obstant això, només ocupa 4 (3 + ‘\0’) caràcters (4 bytes), i la resta no s’utilitza. Per tant, la mida del vector indica la longitud màxima de la cadena de caràcters que podem guardar. Per a saber la longitud d’un string, tenim el mètode strlen:

[Exemple 11_01]

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

int main(int argc, char **argv) {

   char c[3]="ab";
   int len;
 
   /* Get the string length */
    len=strlen(c);

   /* Show the length */
    printf("The length is %d\n",len);
 
   return 0;
}

Tingueu en compte que per a poder accedir als mètodes relacionats amb el tractament de cadenes de caràcters, cal afegir la llibreria string.h.

1.2. Assignació

Quan treballem amb vectors i les cadenes de caràcters no són una excepció, no podem fer una assignació directa. O sigui, si intentem el següent:

[Exemple 11_02]

#include <stdio.h>

int main(int argc, char **argv) {

   char s1[3]="ab";
   char s2[3];

   /* BAD assignation */
    s2=s1;

   /* Show the values */
    printf("s1=%s, s2=%s\n", s1, s2);

   /* Change the first char in s2 */
    s2[0]='k';

   /* Show the values */
    printf("s1=%s, s2=%s\n", s1, s2);

   return 0;
}

Poden passar dues coses. La primera és que el compilador detecti aquesta situació i ens doni un error de compilació, i la segona és que quedin assignades a la mateixa zona de memòria, per la qual cosa el segon printf ens mostrarà dues cadenes de caràcters amb els valors kb, ja que en modificar s2 també haurem modificat s1. Aquest tema el veurem amb més profunditat quan treballem amb punters. De moment, hem de ser conscients que no es pot fer una assignació directa entre vectors ni cadenes de caràcters.

En el cas de les cadenes de caràcters, disposem del mètode strcpy per a copiar el contingut d’una string a una altra. L’assignació correcta seria:

[Exemple 11_03]

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

int main(int argc, char **argv) {

   char s1[3]="ab";
   char s2[3];

   /* Correct assignation */
    strcpy(s2,s1);
       
   /* Show the values */
    printf("s1=%s, s2=%s\n", s1, s2);

   /* Change the first char in s2 */
    s2[0]='k';

   /* Show the values */
    printf("s1=%s, s2=%s\n", s1, s2);

   return 0;
}

Cal tenir en compte que els vectors s1 i s2 no han de tenir obligadament la mateixa longitud, però el vector s2 ha de tenir suficient espai per a acomodar la cadena de caràcters que hi ha a s1.

1.3. Comparació

De la mateixa manera que en el cas de l’assignació, la comparació entre dos vectors no es pot fer amb els operadors habituals (==, <, >, ...). El codi següent ens diria que s1 i s2 no són iguals, mentre que tenen la mateixa cadena de caràcters:

[Exemple 11_04]

#include <stdio.h>

int main(int argc, char **argv) {

   char s1[3]="ab";
   char s2[3]="ab";
 
   /* Compare the two strings (ERROR)*/
   if (s1 == s2) {
        printf("EQUALS\n");
    } else {
        printf("NOT EQUALS\n");
    }

   return 0;
}

Per a comparar cadenes de caràcters, tenim el mètode strcmp(s1, s2). Aquest mètode rep com a paràmetres les dues cadenes de caràcters s1 i s2 que volem comparar i ens retorna un valor enter:

  • 0 si s1 i s2 són iguals
  • menor a 0 si s1<s2 (en ordre alfabètic)
  • major a 0 si s1>s2 (en ordre alfabètic)

Per tant, el codi correcte seria:

[Exemple 11_05]

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

int main(int argc, char **argv) {

   char s1[3]="ab";
   char s2[3]="ab";
 
   /* Compare the two strings (ERROR)*/
   if (strcmp(s1,s2) == 0) {
        printf("EQUALS\n");
    } else {
        printf("NOT EQUALS\n");
    }

   return 0;
}

2. Programació segura

Un tema que cal tenir en compte quan treballem amb cadenes de caràcters és no passar-nos mai de l’espai disponible en el vector. Fixeu-vos en el següent programa:

[Exemple 11_06]

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

int main(int argc, char **argv) {

   int a=3;
   char b='a';
   char c[3]="ab";
   int d=3;

   /* Show the initial values */
    printf("a=%d, b=%c, c=%s, d=%d\n",a,b,c,d);

   /* Assign all the space. No '\0' is stored */
    strcpy(c,"abc");
    printf("a=%d, b=%c, c=%s, d=%d\n",a,b,c,d);

   /* Assign more space. Memory of other variables is NOT used */
    strcpy(c,"abcd");
    printf("a=%d, b=%c, c=%s, d=%d\n",a,b,c,d);

   return 0;
}

Si executeu el codi veureu que el resultat és:

a=3, b=a, c=ab, d=3
a=3, b=a, c=abc, d=3
a=3, b=a, c=abcd, d=0

Fixeu-vos que s’ha modificat el valor de la variable d. Si pensem en l’estructura de memòria d’aquest programa, tenim:

MemStringError.png

Quan assignem «abc» a la variable c, ocupem tots els bytes que tenim disponibles (X-6 a X-8) i, per tant, no s’altera el valor de cap altra variable. Quan assignem «abcd», estem utilitzant també el primer byte de la variable d (X-9), amb el que es modifica el seu valor.

Per a evitar aquest tipus de situacions, hi ha versions de la majoria de mètodes que tracten amb cadenes de caràcters i que permeten limitar la longitud a copiar. Aquests mètodes solen afegir una «n» al nom de la funció. El següent codi és robust a aquest tipus de problemes, fixeu-vos que s’utilitza el mètode strncpy i que se li indica la mida del vector de destinació:

[Exemple 11_07]

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

int main (int argc, char ** argv) {

   int a = 3;
   char b = 'a';
   char c[3] = "b";
   int d = 3;

   /* Show the initial values ​​*/
    printf("a=%d, b=%c, c=%s, d=%d\n", a, b, c, d);

   /* Assign all the space. No '\0' is stored */
    strncpy(c, "abc", 3);
    printf("a=%d, b=%c, c=%s, d=%d\n", a, b, c, d);

   /* Assign more space. Memory of other variables is NOT used */
    strncpy(c, "abcd", 3);
    printf("a=%d, b=%c, c=%s, d=%d\n", a, b, c, d);

   return 0;
}

És bo prendre aquest tipus de precaucions sempre, però especialment quan les cadenes de caràcters es creen dins del programa, per exemple concatenant valors o diferents cadenes de caràcters, o quan les introdueix l’usuari.

Resum

En aquesta unitat hem vist les diferents opcions que ens proporciona el llenguatge C per a tractar amb cadenes de caràcters o strings.

Etiquetes:
Creat per editor1 el 2018/09/16 21:30