¿Por qué argc no es una constante?

104
int main( const int argc , const char[] const argv)

Como el artículo n. ° 3 de C ++ efectivo dice "Use const siempre que sea posible", empiezo a pensar "¿por qué no hacer estos parámetros 'constantes' const"?

¿Existe algún escenario en el que argcse modifique el valor de en un programa?

Dinushan
fuente
38
Eres libre de declarar argccomo const.
Oliver Charlesworth
14
He visto muchos códigos de calidad de producción que hacen esto:--argc
Aniket Inge
2
Estoy pensando que tiene que ver con el legado de C, pero no sabría cómo.
Luis Machuca
13
Sospecho que "Usar constante siempre que sea posible" se refiere a cosas pasadas por referencia, donde cualquier modificación dentro de la función persiste una vez que la función ha salido. Esto no es cierto con las cosas que se pasan por valor, como un número entero, por lo que no se gana nada al hacerlo const; de hecho, pasar argccomo un const intmedio que luego no puede usar argccomo, digamos, un contador dentro de la función.
Steve Melnikoff
4
@SteveMelnikoff: No estoy de acuerdo en que no haya nada que ganar haciendo constun parámetro de paso por valor. Ver, por ejemplo, stackoverflow.com/a/8714278/277304 y stackoverflow.com/a/117557/277304
leonbloy

Respuestas:

114

En este caso, la historia es un factor. C definió estas entradas como "no constantes", y la compatibilidad con (una buena parte del) código C existente fue uno de los primeros objetivos de C ++.

Algunas API de UNIX, como getopt, en realidad manipulan argv[], por lo que no se pueden crear también constpor esa razón.

(Aparte: Curiosamente, aunque getoptel prototipo sugiere que no modificará argv[]pero puede modificar las cadenas señaladas, la página de manual de Linux indica que getoptpermuta sus argumentos, y parece que saben que están siendo traviesos . La página de manual de Open Group no menciona esta permutación).

Poniendo consten argcy argvno comprar mucho, y sería invalidar algunos de la vieja escuela prácticas de programación, tales como:

// print out all the arguments:
while (--argc)
    std::cout << *++argv << std::endl;

He escrito este tipo de programas en C y sé que no estoy solo. Copié el ejemplo de alguna parte .

Joe Z
fuente
1
-1 Re "Algunas API de UNIX, como getopt, en realidad manipulan argv [], por lo que no se puede convertir en constante por esa razón también". Se puede agregar libremente un nivel superior consta argv, sin afectar lo que puede hacer cualquier función C. Además, getoptse declara como int getopt(int argc, char * const argv[], const char *optstring);. Y aquí constno es el nivel superior, pero declara que los punteros son const, una promesa de no modificarlos (aunque las cadenas a las que apuntan posiblemente se modifiquen).
Saludos y hth. - Alf
@ Cheersandhth.-Alf: getopt permuta la argv[]matriz por defecto, pero no modifica las cadenas. (La palabra permute viene directamente de la página del manual). Eso significa que la argvmatriz en sí no es constincluso si las cadenas a las que apunta lo son. ¿Cómo permutar la argv[]matriz no manipularla?
Joe Z
1
Lo siento, los documentos que miré son inconsistentes: la firma no permite tal permutación. Ver ejemplo . Sin embargo, el siguiente texto dice que permuta. Entonces, eliminaré el voto negativo.
Saludos y hth. - Alf
1
Es decir, tendrás que editar un poco para "desbloquear" para que pueda eliminar el voto negativo. Y no, estás malinterpretando el prototipo. Mire el ejemplo que proporcioné y el error de compilación, "asignación de ubicación de solo lectura".
Saludos y hth. - Alf
1
@ Cheersandhth.-Alf: Es interesante ... Busqué el prototipo en el encabezado, y está char* const* ___argvahí, pero la interfaz realmente se adhiere const char **. Tanta confusión. Había confundido la precedencia de the []y the *con respecto a the const. Mis disculpas.
Joe Z
36

El estándar C (ISO / IEC 9899: 2011) dice:

5.1.2.2.1 Inicio del programa

¶1 La función llamada al inicio del programa se nombra main. La implementación no declara ningún prototipo para esta función. Se definirá con un tipo de retorno de int y sin parámetros:

int main(void) { /* ... */ }

o con dos parámetros (a los que se hace referencia aquí como argcy argv, aunque se pueden utilizar nombres, ya que son locales a la función en la que se declaran):

int main(int argc, char *argv[]) { /* ... */ }

o equivalente; 10) o de alguna otra manera definida por la implementación.

¶2 Si se declaran, los parámetros de la mainfunción obedecerán las siguientes restricciones:

  • El valor de argcno será negativo.
  • argv[argc] será un puntero nulo.
  • Si el valor de argces mayor que cero, los miembros de la matriz argv[0]hasta argv[argc-1]inclusive contendrán punteros a cadenas, a las que el entorno host da valores definidos por la implementación antes del inicio del programa. La intención es proporcionar al programa información determinada antes del inicio del programa desde cualquier otro lugar del entorno alojado. Si el entorno de host no es capaz de suministrar cadenas con letras tanto en mayúsculas como en minúsculas, la implementación debe garantizar que las cadenas se reciban en minúsculas.
  • Si el valor de argces mayor que cero, la cadena apuntada por argv[0] representa el nombre del programa; argv[0][0]será el carácter nulo si el nombre del programa no está disponible en el entorno del host. Si el valor de argces mayor que uno, las cadenas apuntado por argv[1]medio argv[argc-1] representar los parámetros del programa.
  • Los parámetros argcy argvlas cadenas a las que apunta la argvmatriz serán modificables por el programa y conservarán sus últimos valores almacenados entre el inicio y la finalización del programa.

10) Por lo tanto, intse puede reemplazar por un nombre typedef definido como int, o el tipo de argvse puede escribir como char **argv, y así sucesivamente.

Tenga en cuenta la última viñeta. Dice que ambos argcy argvdeberían ser modificables. No es necesario modificarlos, pero pueden modificarse.

Jonathan Leffler
fuente
Interesante. En una nota semi-relacionada, me encontré con un error que causaba un bloqueo que resultó ser porque el QApplication(argc,argv)constructor de Qt se definió con argcpor referencia . Eso me sorprendió.
HostileFork dice que no confíes en SE
23

argcnormalmente no es una constante porque la firma de función para main()fechas anteriores const.

Dado que argc es una variable de pila, cambiarla no afectará a nada más que a su propio procesamiento de línea de comando.

Por supuesto, consteres libre de declararlo si quieres.

razeh
fuente
8

Un nivel superior consten un argumento formal no es parte del tipo de función. Puede agregarlo o eliminarlo como desee: solo afecta lo que puede hacer con el argumento en la implementación de la función.

Entonces, para argcque pueda agregar libremente, agregue un archivo const.

Pero argvno se pueden hacer los datos del carácter constsin cambiar la firma de la función. Lo que significa que entonces no es una de las mainfirmas de función estándar y no tendrá que ser reconocida como una mainfunción. Entonces, no es una buena idea.


Una buena razón para no usar los mainargumentos estándar en programas que no son de juguete es que en Windows no pueden representar los argumentos reales del programa, como los nombres de archivo con caracteres internacionales. Eso es porque en Windows están codificados por convención muy fuerte como Windows ANSI. En Windows, puede implementar algunas GetCommandLinefunciones de acceso a argumentos más portátiles en términos de la función API.


En resumen, nada impide que se agreguen consta argc, pero el más útil const-ness en argvque le daría un no-estándar de mainla función, la mayoría probablemente no reconocido como tal. Afortunadamente (de manera irónica) hay buenas razones para no usar los mainargumentos estándar para código serio portátil. En pocas palabras, para la práctica solo admiten el antiguo ASCII, con solo letras del alfabeto inglés.

Saludos y hth. - Alf
fuente
1
Si está desarrollando para Windows con cygwin u otra libc que admita correctamente UTF-8 (hasta donde yo sé, cygwin es el único en este momento, pero tengo a alguien trabajando en otra opción), la mainfirma estándar funciona bien y usted puede recibir argumentos Unicode arbitrarios.
R .. GitHub DEJA DE AYUDAR A ICE
@R también funcionan bien en la mayoría de las plataformas * nix. la cuestión no es si existen plataformas donde funcionan. pero, muy linda iniciativa , y resulta que estoy haciendo lo mismo, más o menos en serio
Saludos y hth. - Alf
4

La firma de maines algo así como un artefacto histórico de C. Históricamente Cno lo tenía const.

Sin embargo, puede declarar su parámetro constya que los efectos de const son solo en tiempo de compilación.

Jorge
fuente
Cuando dices "C histórico", ¿qué tan histórico estamos hablando aquí?
Joe Z
8
constse añadió a C89 / C90; antes de eso, no era parte de C.
Jonathan Leffler
@JonathanLeffler: Sabes, lo había olvidado. Aprendí C en 1992. Antes de eso, era un hacker Pascal, BASIC y ensamblador.
Joe Z
2

Porque argces una variable local (y, en C ++, no una referencia o algo así), y porque el lugar especial de mainsignifica que los chanchullos de compatibilidad con versiones anteriores le otorgan una gran cantidad de libertad sin ninguna razón convincente para forzar a las aplicaciones a hacerla constante.

main() {}

int main() {}

main() { return 0; }

main(int argc, char* argv[]) { return 0; }

int main(const int argc, const char** argv) { /* no return*/ }

estas y muchas otras variaciones se compilarán en una amplia gama de compiladores C y C ++.

Entonces, en última instancia, no es que argc no sea constante, solo que no tiene que serlo, pero puede serlo si quieres que lo sea.

http://ideone.com/FKldHF , ejemplo de C:

main(const int argc, const char* argv[]) { return 0; }

http://ideone.com/m1qc9c , ejemplo de C ++

main(const int argc) {}
kfsone
fuente
Tenga en cuenta que las variantes sin un tipo de retorno explícito ya no son válidas en C99, y mucho menos en C11, aunque los compiladores continúan permitiéndolas por razones de compatibilidad con versiones anteriores. Los compiladores de C ++ no deberían aceptar las variantes sin un tipo de retorno.
Jonathan Leffler
@JonathanLeffler Sí, sin embargo, el último ejemplo de ideone allí ( ideone.com/m1qc9c ) es G ++ 4.8.2 con -std=c++11. Clang también lo acepta pero cede warning: only one parameter on 'main' declaration [-Wmain], y MSVC solo protesta por el especificador de tipo de retorno que falta y la falta de una referencia a argc. Nuevamente, 'travesuras' y 'libertad de
acción
1

Aparte de las razones históricas, una buena razón para mantener argc y argv non- constes que la implementación del compilador no sabe qué vas a hacer con los argumentos de main, solo sabe que debe darte esos argumentos.

Cuando está definiendo sus propias funciones y prototipos asociados, sabe qué parámetros puede hacer const y cuáles modificará su función.

Llevado al extremo, podría declarar que todos los parámetros de todas las funciones deben declararse const, y luego, si tuviera una razón para cambiarlos (por ejemplo, disminuir un índice para buscar en una matriz), tendría que hacer que no sean constvariables locales y copie los constvalores de los argumentos en esas variables. Eso lo convierte en un trabajo ajetreado y un LOC adicional sin ningún beneficio real. Un analizador estático decente se activará si no está modificando el valor de un argumento y le recomendará que cree el parámetro const.

Sam Skuce
fuente