¿Es argv escribible?

C11 5.1.2.2.1 / 2 dice:

Los parámetros argc y argv y las cadenas apuntadas por la matriz argv serán modificables por el progtwig y conservarán sus últimos valores almacenados entre el inicio del progtwig y la terminación del progtwig.

Mi interpretación de esto es que especifica:

 int main(int argc, char **argv) { if ( argv[0][0] ) argv[0][0] = 'x'; // OK char *q; argv = &q; // OK } 

sin embargo, no dice nada sobre:

 int main(int argc, char **argv) { char buf[20]; argv[0] = buf; } 

Es argv[0] = buf; ¿permitido?

Puedo ver (al menos) dos posibles argumentos:

  • La cita anterior menciona deliberadamente argv y argv[x][y] pero no argv[x] , por lo que la intención era que no es modificable
  • argv es un puntero a objetos no const , por lo que en ausencia de una redacción específica en sentido contrario, debemos suponer que son objetos modificables.

IMO, código como argv[1] = "123"; es UB.


“Los parámetros argc y argv y las cadenas apuntadas por la matriz argv serán modificables por el progtwig y conservarán sus últimos valores almacenados entre el inicio del progtwig y la terminación del progtwig”. C11dr §5.1.2.2.1 2

Recordemos que const entró en C muchos años después de la creación de C.

Muy parecido a char *s = "abc"; es válido cuando debe ser const char *s = "abc"; . La necesidad de const no era necesaria; de lo contrario, el código existente se habría roto con la introducción de const .

Del mismo modo, incluso si argv hoy se debe considerar char * const argv[] o alguna otra firma con const , la falta de const en el char *argv[] no completa las necesidades de const -ness del argv , argv[] , o argv[][] . Las necesidades de constness deberían ser impulsadas por la especificación.

Desde mi lectura, ya que la especificación no dice nada sobre el tema, es UB.

El comportamiento indefinido se indica de otra manera en esta Norma Internacional con las palabras “comportamiento indefinido” u omisión de cualquier definición explícita de comportamiento “§4 2


[editar]:

main() es una función muy especial es C. Lo que está permitido en otras funciones puede o no estar permitido en main() . La especificación C detalla los atributos sobre sus parámetros que dieron la firma int argc, char *argv[] que no debería necesitar. main() , a diferencia de otras funciones en C, puede tener una firma alternativa int main(void) y potencialmente otras. main() no es reentrante. A medida que la especificación C se desvía de detallar qué se puede modificar: argc , argv , argv[][] , es razonable cuestionar si argv[] es modificable debido a su omisión de la especificación que afirma que el código puede.

Dada la especialidad de main() y la omisión de especificar que argv[] como modificable, un progtwigdor conservador trataría esta grisura como UB, pendiente de aclaración futura de especificaciones C.


Si argv[i] es modificable en una plataforma dada, ciertamente el rango de i no debe exceder argc-1 .

Como ” argv[argc] será un puntero nulo”, la asignación de argv[argc] a algo distinto de NULL parece ser una violación.

Aunque las cadenas son modificables, el código no debe exceder la longitud original de la cadena.

 char *newstr = "abc"; if (strlen(newstr) <= strlen(argv[1])) strcpy(argv[1], newstr); 

argc es solo un int y es modificable sin ninguna restricción.

argv es un char ** modificable char ** . Significa que argv[i] = x es válido. Pero no dice nada acerca de que argv[i] sea ​​en sí mismo modificable. Entonces argv[i][j] = c conduce a un comportamiento indefinido.

La función getopt de la biblioteca estándar C modifica argc y argv pero nunca modifica las matrices char reales.

Se menciona claramente que argv y argv[x][x] son modificables. Si argv es modificable, puede apuntar a otro primer elemento de una matriz de caracteres y, por lo tanto, argv[x] puede señalar el primer elemento de alguna otra cadena. Finalmente, argv[x] se puede modificar y esa podría ser la razón por la que no hay necesidad de mencionarlo explícitamente en el estándar.

La respuesta es que argv es una matriz y sí, su contenido es modificable.

La clave es anterior en la misma sección:

Si el valor de argc es mayor que cero, los miembros de la matriz argv [0] a argv [argc-1] inclusive contendrán punteros a las cadenas, que reciben los valores definidos por la implementación por el entorno de host antes del inicio del progtwig.

A partir de esto, está claro que argv debe considerarse como una matriz de una longitud específica (argc). Entonces * argv es un puntero a esa matriz, habiendo decaído a un puntero.

Leído en este contexto, la afirmación de que ‘argv debe ser modificable … y conservar sus contenidos’ claramente tiene la intención de que los contenidos de esa matriz sean modificables.

Admito que sigue habiendo cierta ambigüedad en la redacción, particularmente en lo que podría suceder si se modifica argc.


Para que quede claro, lo que estoy diciendo es que leo este lenguaje como un significado:

[el contenido de] argv [matriz] y las cadenas apuntadas por la matriz argv serán modificables …

Entonces, tanto los punteros en el arreglo como las cadenas a las que apuntan están en la memoria de lectura-escritura, no se hace daño al cambiarlos, y ambos preservan sus valores durante la vida del progtwig. Esperaría que este comportamiento se encuentre en todas las principales implementaciones de la biblioteca en tiempo de ejecución C / C ++, sin excepción. Esto no es UB.

La ambigüedad es la mención de argc. Es difícil imaginar cualquier propósito o implementación en la que el valor de argc (que parece ser simplemente un parámetro de función local) no pueda cambiarse, entonces ¿por qué mencionarlo? La norma establece claramente que una función puede cambiar el valor de sus parámetros, entonces ¿por qué tratar a argc especialmente a este respecto? Es esta mención inesperada de argc lo que ha desencadenado esta preocupación sobre argv, que de otro modo pasaría sin comentario. Elimina argc de la oración y la ambigüedad desaparece.