es argv[n] escribible?

9 minutos de lectura

C11 5.1.2.2.1/2 dice:

Los parametros argc y argv y las cuerdas apuntadas por el argv La matriz debe ser modificable por el programa y conservar sus últimos valores almacenados entre el inicio y la finalización del programa.

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 mencionada deliberadamente argv y argv[x][y] pero no argv[x]por lo que la intención era que no sea modificable
  • argv es un indicador de no-const objetos, por lo que, en ausencia de una redacción específica en contrario, debemos asumir que son objetos modificables.

  • Relacionado: esta respuesta que afirma que argv[n] no es modificable pero no proporciona ninguna justificación para esa afirmación

    –MM

    9 de septiembre de 2014 a las 5:49

  • cuando dice eso argv se puede modificar, entiendo que eso significa que argv[n] puede ser. (por supuesto los argv puntero sí mismo se puede modificar, es solo un argumento local de función).

    – cdhowie

    9 de septiembre de 2014 a las 5:52

  • @cdhowie ¿Por qué se molestan en decir eso? argc se puede modificar, se le aplica ese mismo “por supuesto”? Creo que solo están hablando de la variable local, no de los punteros.

    – Barmar

    9 de septiembre de 2014 a las 5:53

  • @cdhowie Pero dice “las cuerdas señaló”. argv[n] no es una cadena; es un puntero que apunta al primer carácter de una cadena.

    –MM

    9 de septiembre de 2014 a las 5:54

avatar de usuario
chux – Reincorporar a Monica

OMI, código como argv[1] = "123"; es UB (usando el original argv).


“Los parametros argc y argv y las cuerdas apuntadas por el argv La matriz debe ser modificable por el programa y conservar sus últimos valores almacenados entre el inicio y la finalización del programa”. C11dr y C17dr1 §5.1.2.2.1 2

Recordar que const llegó a C muchos años después de la creación de C.

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

Del mismo modo, incluso si argv hoy debe ser considerado char * const argv[] o alguna otra firma con constla falta de const en el char *argv[] no completa especificar el const-ness necesidades de la argv, argv[]o argv[][]. los constLas necesidades de -ness tendrían que ser impulsadas por la especificación.

Según mi lectura, dado que la especificación no dice nada sobre el tema, es UB.

El comportamiento indefinido se indica de otro modo en esta Norma Internacional mediante las palabras “comportamiento indefinido” o por el omisión de cualquier definición explícita de comportamiento” §4 2


[edit]:

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 dan la firma int argc, char *argv[] eso no debería necesitar. main()a diferencia de otras funciones en C, puede tener una firma alternativa int main(void) y potencialmente otros. main() no es reentrante. A medida que la especificación C hace todo lo posible para detallar lo que se puede modificar: argc, argv, argv[][]es razonable preguntarse 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 programador conservador trataría este gris como UB, en espera de una futura aclaración de especificaciones C.


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

Como “argv[argc] será un puntero nulo”, asignando argv[argc] a algo más que NULL parece ser una violación.

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

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

1 Sin cambios con C17/18. Dado que esa versión estaba destinada a aclarar muchas cosas, refuerza que esta especificación es adecuada y no falta un “argv los elementos de la matriz serán modificables”.

  • “omisión de cualquier definición explícita de comportamiento” – bueno, diríamos int bar = 7; se define a pesar de que el texto “int bar = 7;” no aparece en la norma.

    –MM

    9 sep 2014 a las 20:49

  • @Matt McNabb Normalmente, uno estaría fácilmente de acuerdo con la línea de razonamiento de su comentario si no fuera por C11dr §5.1.2.2.1 2. La especificación se sale del camino para decir que algunas cosas son modificables aunque char *argv[] no necesita esa afirmación. Dado que la especificación indica específicamente la modificabilidad para argvy argv[][](2 de 3) pero no argv[], esa ausencia es significativa – de ahí UB por omisión. En mi opinión, es una debilidad para la especificación implicar modificabilidad para argv[][] y callar argv[].

    – chux – Reincorporar a Monica

    10 de septiembre de 2014 a las 0:38


  • @Matt McNabb Una utilidad para modificar de argv[][] es el uso directo de strtok() en argv[] y los argumentos de la línea de comandos a menudo necesitan ser analizados.

    – chux – Reincorporar a Monica

    10/09/2014 a las 18:30


  • No estoy de acuerdo (ver mi respuesta). ¿Puede señalar alguna implementación real en la que argv[n]=buf ¿Se comporta mal? ¿O alguna implementación que advierte contra hacerlo?

    – david.pfx

    12 de septiembre de 2014 a las 10:13

  • @ david.pfx ¿Anunciar su respuesta aquí? – mmm. No, no puedo encontrar un ejemplo al igual que la especificación no dice específicamente que se puede hacer; de lo contrario, OP lo habría visto y no habría habido dudas. Nos quedamos con un gris en la especificación y estamos tratando de llamarlo blanco o negro. Al final, hasta que la especificación sea más clara, los fabricantes de compiladores y los programadores de C lo harán mejor. Ciertamente, no ve un gris, pero si lo hiciera, ¿cómo codificaría: de la manera que cree que debería ser o de manera conservadora? Para mí, codificaría de forma conservadora debido a la posibilidad de UB.

    – chux – Reincorporar a Monica

    12/09/2014 a las 14:38


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

argv es un modificable char **. Esto significa que argv[i] = x es válida. Pero no dice nada sobre argv[i] siendo en sí mismo modificable. Entonces argv[i][j] = c conduce a un comportamiento indefinido.

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

  • Diciendo “x es modificable” significa que se le permite cambiar x; parece estar interpretándolo como que significa “usted puede cambiar lo que x apunta a, si x es un puntero, o si x no es un puntero, se le permite cambiar x”

    –MM

    09/09/2014 a las 20:45

Se menciona claramente que argv y argv[x][x] es modificable. Si argv es modificable, entonces puede apuntar a otro primer elemento de una matriz de char y por lo tanto argv[x] puede apuntar al primer elemento de alguna otra cadena. Por último argv[x] es modificable también y esa podría ser la razón por la que no hay necesidad de mencionarlo explícitamente en el estándar.

  • ¿Puede proporcionar una referencia en la especificación que respalde esto?

    – templatetypedef

    9 de septiembre de 2014 a las 6:13

  • No creo que esa lógica se mantenga. Cualquier puntero modificable a un almacenamiento no modificable también se puede reasignar para que apunte a un almacenamiento modificable. (Por lo tanto, no podemos dar el paso lógico de que “el puntero se puede reasignar para que apunte a un almacenamiento modificable” implica que “originalmente debe haber estado apuntando a un almacenamiento modificable”).

    –MM

    9 de septiembre de 2014 a las 6:14

  • @MattMcNabb;Aunque el almacenamiento es modificable o no modificable, argv[x] es modificable aquí. Standard dice que el segundo argumento para main debiera ser char *argv[]. Esto significa que arg[x] no es unconst puntero.

    – trucos

    9 de septiembre de 2014 a las 6:28

  • @hacks const int i = 3; int *p = (int *) &i; es perfectamente valido. Ahora p no es un const puntero, pero no se permite usar ese puntero para modificar lo que apunta. Otro ejemplo sin casting es char *s = "hello";. Es por eso que el estándar necesitaba establecer explícitamente que las propias cadenas son modificables. Y el estándar no lo establece explícitamente para la matriz de punteros de cadena.

    usuario743382

    9 de septiembre de 2014 a las 7:46

avatar de usuario
david.pfx

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

La clave está antes en la misma sección:

Si el valor de argc es mayor que cero, el formación miembros argv[0] a través de argv[argc-1] inclusive contendrán punteros a cadenas, a las que el entorno host les da valores definidos por la implementación antes del inicio del programa.

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

Leído en este contexto, la declaración en el sentido de que ‘argv será modificable… y conservará su contenido’ claramente pretende que el contenido de esa matriz sea modificable.

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


Para que quede claro, lo que estoy diciendo es que leo este lenguaje en el sentido de:

[the contents of the] argv [array] y las cadenas a las que apunta la matriz argv serán modificables …

Por lo tanto, tanto los punteros en la matriz como las cadenas a las que apuntan están en la memoria de lectura y escritura, no se hace daño al cambiarlos y ambos conservan sus valores durante la vida del programa. Espero que este comportamiento se encuentre en todas las principales implementaciones de bibliotecas de tiempo de ejecución de C/C++, sin excepción. Esto no es la UB.

La ambigüedad es la mención de argc. Es difícil imaginar algún 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? El estándar establece claramente que una función puede cambiar el valor de sus parámetros, entonces, ¿por qué tratar argc de manera especial a este respecto? Es esta mención inesperada de argc lo que ha desencadenado esta preocupación por argv, que de otro modo pasaría desapercibida. Elimine argc de la oración y la ambigüedad desaparece.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad