[WikiItic] [TitleIndex] [WordIndex

Sobre els literals de tipus string

Tot i que a C el tipus string com a tal no existeix (i si que existeixen taules de char), existeixen els literals de tipus string. Així doncs, podem escriure coses com:

   1 printf("Hola mon");

en què "Hola mon" és un literal string. La naturalesa d'aquest literal és una mica excepcional en el context del llenguatge. Aquesta pàgina mira d'aclarir-la. La referència normativa de l'estàndard és ISO C99 i en podeu trobar una còpia d'una versió draft a http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf.

El tipus dels literals string

D'acord amb l'estàndard (§6.4.5), els literals string són de tipus char[] i tenen un temps de vida equivalent al del programa, i.e.: taules de chars. Noti's que no són de tipus const char[] i, per tant hauríen de ser modificables, com es fa en el següent fragment:

char *p;

p = "El borbó no fa por"
*(p+5) = 'm'

Això no obstant, el codi anterior no és correcte. De fet l'estàndard diu en el mateix punt que:

Així doncs, malgrat el tipus de dades no incorpora el modificador const, un literal string s'ha de considerar no modificable. Altrament hi ha el risc d'observar un comportament inesperat. Per tant, modificar un literal no és un error en el sentit estricte (el compilador no detectarà error) però és un ús erroni del literal. Aquesta semàntica s'introdueix a l'estàndard C89.

Per què no es considera `const char[]`?

Per què si a efectes pràctics un literal string cal considerar-lo constant, no és de tipus const char[]? Doncs per una qüestió de compatibilitat enrera. No és inusual tenir codi com el següent:

   1 int length(char *p) {
   2   int l = 0;
   3   while (*p++) l++;
   4   return l;
   5 }
   6 
   7 ...
   8 
   9 int a;
  10 a = length("Hola");

Si el tipus del literal fos const char[] la funció no es podria cridar com en l'exemple, atès que espera un apuntador modificable. Respectar funcions com l'anterior, que eren correctes a C de K&R implica mantenir el tipus dels literals com char [].

Per què considerar-lo constant?

Per quina raó no és interessant poder modificar el literal? Hi ha dues raons pel cap baix:

  1. Per una qüestió d'estil. No és gaire elegant modificar un element que des del punt de vista sintàctic té tot l'aspecte d'una constant.
  2. Per una raó d'optimització. Obre la porta a que el compilador representi diversos literals que són iguals una sola vegada en memòria. En certes aplicacions pot suposar un estalvi notable.

Literals i inicialitzacions

Quan un literal string s'usa en una inicialització d'una taula, les coses prenen un sentit diferent. Per exemple, el següent codi és correcte:

   1 chat t[] = "hola";
   2 
   3 t[3] = 'c';

En aquest cas t no fa referència al literal. t és una taula de caracters que s'ha inicialitzat a partir d'un literal, però no és el literal. Per tant, no hi ha inconvenient a modificar t.

Però... i si m'equivoco?

Si accidentalment modifico un literal string què succeirà? Doncs d'acord amb l'estàndard no es pot saber què succeirà: té un comportament indefinit. El que és segur és que no hi haurà cap error detectat. La majoria de compiladors miren d'avisar de la situació amb un warning si poden detectar-la i, en molts casos, el mal ús acaba senzillament en un error d'execució. Per aconseguir-ho els literals es desen en una zona de memòria marcada com read-only. Un accés a aquesta zona per modificar-la comporta que el procés avorti.

Aspectes punxeguts

Aquesta estranya consideració dels literals té alguns aspectes punxeguts. Vegem-ne alguns:

Observeu el següent codi:

   1 int main(void) {
   2    "hola"[0] = 'c';
   3    return 0;
   4 }

És un programa sintàcticament correcte i, en principi, no hauria de generar cap error (potser algun warning). Això no obstant, és erroni fa molt mal als ulls!!

Observeu ara aquest altra fragment de codi:

   1 void f(char t[]) {
   2    t[1] = 'c';
   3 }
   4 
   5 int main(void) {
   6    char m[10] = "hola";
   7 
   8    f(m);
   9    f("hola");
  10    return 0;
  11 }

En aquest programa la primera crida a f() és correcta però la segona no. El compilador tampoc ho pot detectar atès que, estrictament, no és un error sinó un mal us.

Recomanacions d'ús

De cara a evitar problemes a causa de la singularitat del literal string es recomana:

  1. Declarar sempre els apuntadors associats a un literal com a const, amb la finalitat que la modificació no volguda del literal esdevingui un error detectable pel compilador.

       1  const char *p = "hola";
    

    Segueix la recomanació de SEI C CERT Coding Standard, STR05-C. Use pointers to const when referring to string literals.


2023-07-03 11:46