2009-10-07 25 views
5

C++ en MS Visual Studio 2008. Nivel de advertencia 4 más una carga de advertencias adicionales habilitadas también. Esperaría que esto diera una advertencia al menos, pero más probablemente un error del compilador?¿Por qué/cómo se compila?

declaración de la función es la siguiente:

int printfLikeFunction(
    const int    bufferLength, 
    char * const   buffer, 
    const char * const  format, 
    ...); 

el uso de código - no es un error tipográfico: aunque el ARRAY_SIZE de OutputBuffer se pasa, en sí OutputBuffer no es - sin duda esto no debería compilar:

printfLikeFunction(ARRAY_SIZE(outputBuffer), "Format: %s, %s", arg1, arg2); 

Claramente esto está mal y se ha cometido un error. Sin embargo, el compilador debería haberlo atrapado. El parámetro del búfer debe ser un char-pointer, y se le está pasando un literal de cadena que es un const char-pointer. Esto debe ser un error. (arg1 y arg2 son (posiblemente const) char punteros también, por lo que casualmente la declaración coincide incluso sin que outputBuffer esté en el lugar correcto).

En tiempo de ejecución, este código se bloquea cuando intenta escribir en el literal de cadena. No me sorprende, simplemente no entiendo cómo se permitió compilar.

(Aunque, dicho sea de paso, esto es probablemente por lo que sprintf_s tiene los parámetros de búfer y tamaño en un orden diferente a esta función - hace que tales errores fallen de manera inequívoca).

Respuesta

9

C++ tiene una laguna especial para literales de cadena para compatibilidad con el código de estilo C pre const. Aunque los literales de cadena son matrices de const char, se pueden convertir a un puntero a caracteres no const.

Parafraseo 4.2/2 [conv.array]: un literal de cadena 'estrecha' se puede convertir a un valor de referencia de tipo puntero a no constchar. La conversión solo se considera cuando hay un tipo de objetivo explícito (por ejemplo, un parámetro de función) y no cuando se requiere una conversión de valor lvalor general a valor r.

Esta conversión está en desuso, pero aún está disponible. Tenga en cuenta que aunque la conversión permite que el literal se convierta en un puntero al tipo no const char, todavía invocará un comportamiento indefinido para tratar de modificar cualquiera de los caracteres del literal de cadena a través de este puntero.

+0

Wow. ¡Qué decisión más loca! Pero, sí, acabo de consultar el estándar y tienes toda la razón. ¡Increíble! –

+0

Es de esperar que Visual Studio emita una advertencia al usar funciones obsoletas y descabelladas del estándar. –

+0

Sí ... incluso si la "locura" es subjetiva, no lo es, así que me sorprende que esto no haya producido una advertencia. –

2

Me parece recordar que el compilador tiene una opción que controla cómo se tratan los literales de cadena. Por defecto, se tratan como char * para no romper una gran cantidad de códigos no const existentes, pero puede cambiarlos para tratarlos como const char *. Esto puede ayudar a desencadenar el error que está buscando.

(posterior) No tengo un compilador de Microsoft a la mano en este momento, pero mirando a través de la referencia en MSDN no puedo encontrar tal opción. Puedo estar pensando en GCC, que sí tiene esa opción.

+0

Mire la respuesta de Charles (http://stackoverflow.com/questions/1530330/why-how-does-this-compile/1530469#1530469). Hay una conversión en desuso de cualquier literal de cadena estrecha a 'char *' que permite la compilación del código antiguo. IMO es un PITA, porque permite que un código como el anterior se compile sin ningún error, pero así son las cosas. – sbi

+0

Interesante, gracias! –

1

int printfLikeFunction ( const int bufferLength, carbón búfer * const, const char * const formato, ...);

el parámetro del búfer se especifica como char * const, por lo que solo protege la dirección del búfer que se va a modificar, no el contenido del búfer.

Para evitar que se escriba el búfer, debe declararlo como const char * const como el formato.

el compilador permite escribir en el búfer ya que lo declara como char *. el modificador const postfix solo impide reasignar el valor del buffer en su función.

ver http://jriddell.org/const-in-cpp.html para ver impacto modificador const en variables

+1

Creo que el problema es que el OP olvidó pasar el parámetro 'buffer', y lo que debería ser el parámetro' format' se está tomando como 'buffer'. – dave4420

+0

Supongo que el buffer debe modificarse, aquí es donde se escribirá la cadena de salida. El problema aquí es que el literal de cadena se trata como un char *, mientras que debe tratarse como un const char *. –

+0

I el búfer debe modificarse y se pasa una constante literal como valor de parámetro de búfer, habrá corrupción de memoria ya que el compilador colocará el literal en una dirección fija en la memoria. – dweeves

1

literales de cadena en C son punteros const char (char*const), no punteros const a const caracteres (const char* const).

C++ originalmente siguió el uso de C, pero luego el estándar ANSI C++ fue modificado en algún momento (no estoy seguro de cuándo) para hacerlos const char* const.Los productos de Microsoft tradicionalmente han tendido a valorar la compatibilidad con versiones anteriores sobre cumplimiento: puede haber una opción de compilación para forzar el "nuevo" comportamiento, pero dado que los ejemplos de cadenas literales en MSDN no son const, dudo que exista.

+2

En realidad, en C++, los literales de cadena estrecha son de tipo 'const char []', que es implícitamente convertible a 'const char *'. Como Charles explicó, hay, sin embargo, una conversión obsoleta a 'char *'. – sbi

Cuestiones relacionadas