Este código fuente está activando una cadena en C. ¿Cómo lo hace?

9 minutos de lectura

Este codigo fuente esta activando una cadena en C ¿Como
ian colton

Estoy leyendo un código de emulador y he contrarrestado algo realmente extraño:

switch (reg){
    case 'eax':
    /* and so on*/
}

¿Cómo es esto posible? Pensé que solo podías switch en tipos integrales. ¿Está ocurriendo algún truco macro?

  • no es la cuerda 'eax' y enumera el valor entero constante

    – 0___________

    07/08/2017 a las 15:35


  • Comillas simples, no dobles. Una constante de carácter se promociona a int, por lo que es legal. Sin embargo, el valor de una constante de varios caracteres está definido por la implementación, por lo que es posible que el código no funcione como se esperaba en otro compilador. Por ejemplo, eax puede ser 0x65, 0x656178, 0x65617800, 0x786165, 0x6165o algo mas.

    – Davislor

    8 de agosto de 2017 a las 2:11


  • @Davislor: dado el nombre de la variable “reg”, y el hecho de que eax es un registro x86, supongo que el comportamiento definido por la implementación estaba destinado a estar bien, porque es el mismo en todas partes donde se usa en el código. Con tal de que 'eax' != 'ebx', por supuesto, por lo que solo falla uno o dos de sus ejemplos. Aunque puede haber algún código en alguna parte que en efecto asuma *(int*)("eax") == 'eax'y por lo tanto falla la mayoría de sus ejemplos.

    –Steve Jessop

    8 de agosto de 2017 a las 13:21


  • @SteveJessop No estoy en desacuerdo con lo que dice, pero existe el peligro real de que alguien intente compilar el código en un compilador diferente, incluso para la misma arquitectura, y obtenga un comportamiento diferente. Por ejemplo, 'eax' podría compararse igual a 'ebx' o para 'ax'y la declaración de cambio no funcionaría según lo previsto.

    – Davislor

    08/08/2017 a las 21:50


  • Todo ese misterio se habría disipado rápidamente si hubiera buscado/nos hubiera mostrado el tipo de datos de reg.

    – esto

    8 de agosto de 2017 a las 22:43

Este codigo fuente esta activando una cadena en C ¿Como
Betsabé

(Solo usted puede responder a la parte de “trucos de macros”, a menos que pegue más código. Pero no hay mucho aquí para que funcionen las macros; formalmente, no puede redefinir palabras clave; el comportamiento al hacer eso no está definido).

Para lograr la legibilidad del programa, el desarrollador ingenioso está explotando comportamiento definido por la implementación. 'eax' es no una cadena, pero constante de varios caracteres. Tenga en cuenta con mucho cuidado los caracteres de comillas simples alrededor eax. Lo más probable es que te esté dando una int en su caso, eso es exclusivo de esa combinación de caracteres. (Muy a menudo, cada carácter ocupa 8 bits en un 32 bits int). Y todos saben que puedes switch en una int!

Finalmente, una referencia estándar:

El estándar C99 dice:

6.4.4.4p10: “El valor de una constante de carácter entero que contiene más de un carácter (por ejemplo, ‘ab’), o que contiene un carácter o una secuencia de escape que no corresponde a un carácter de ejecución de un solo byte, está definido por la implementación. “

  • En caso de que alguien vea eso y entre en pánico, se requiere que “definido por la implementación” funcione y que su compilador lo documente de alguna manera apropiada (el estándar no requiere que el comportamiento sea intuitivo o que la documentación sea buena, pero …). Esto es “seguro” de usar para un codificador que entiende completamente lo que está escribiendo, a diferencia de “indefinido”.

    – Leushenko

    07/08/2017 a las 18:25


  • @Justin Si bien podría, sería bastante perverso. Si no hace lo que sugiere la respuesta, lo más probable es que la siguiente posibilidad sea que solo use el primer carácter e ignore el resto.

    – Barmar

    07/08/2017 a las 21:54


  • @ZanLynx No estoy seguro, pero creo que la función es anterior a Unicode y otros estándares MBCS. Los “números mágicos” que parecen texto en los volcados de memoria y las identificaciones de fragmentos de formato de archivo de estilo RIFF fueron las primeras aplicaciones que conozco.

    –Russell Borogove

    8 de agosto de 2017 a las 3:55

  • @ jpmc26 Este no es un comportamiento indefinido, está definido por la implementación. Entonces, a menos que la documentación del compilador mencione demonios, tu nariz está a salvo.

    – Barmar

    8 de agosto de 2017 a las 4:45

  • @ZanLynx: Me temo que la intención original es anterior a Unicode, UTF-8 y cualquier codificación de caracteres multibyte en casi 20 años. constante de varios caracteres eran solo una forma práctica de expresar números enteros que representan grupos de 2, 3 o 4 bytes (dependiendo del tamaño de bytes e int). Las inconsistencias entre implementaciones y arquitecturas llevaron al comité a declarar esto como implementación definidalo que significa que no hay una forma portátil de calcular el valor de 'ab' desde 'a' y 'b'.

    – chqrlie

    8 de agosto de 2017 a las 6:41


1647536953 802 Este codigo fuente esta activando una cadena en C ¿Como
Vlad de Moscú

De acuerdo con el Estándar C (6.8.4.2 La sentencia switch)

3 La expresión de cada etiqueta de caso será una expresión constante entera

y (6.6 Expresiones constantes)

6 Una expresión constante entera será de tipo entero y solo tendrá operandos que sean constantes enteras, constantes de enumeración,
constantes de carácter, tamaño de expresiones cuyos resultados son constantes enteras y constantes flotantes que son los operandos inmediatos de las conversiones. Los operadores de conversión en una expresión constante entera solo convertirán tipos aritméticos en tipos enteros, excepto como parte de un operando en el operador sizeof.

ahora que es 'eax'?

El estándar C (6.4.4.4 Constantes de caracteres)

2 Una constante de carácter entero es una secuencia de uno o más caracteres multibyte entre comillas simplescomo en ‘x’…

Entonces 'eax' es una constante de carácter entero según el párrafo 10 de la misma sección

  1. …El valor de una constante de carácter entero que contiene más de un carácter (p. ej., ‘ab’), o que contiene un carácter o una secuencia de escape que no se asigna a un carácter de ejecución de un solo byte, está definido por la implementación.

Entonces, de acuerdo con la primera cita mencionada, puede ser un operando de una expresión constante entera que puede usarse como una etiqueta de caso.

Preste atención a que una constante de carácter (entre comillas simples) tiene tipo int y no es lo mismo que un literal de cadena (una secuencia de caracteres entre comillas dobles) que tiene un tipo de matriz de caracteres.

1647536953 721 Este codigo fuente esta activando una cadena en C ¿Como
Dobladillo Stig

Como han dicho otros, este es un int constante y su valor real está definido por la implementación.

Supongo que el resto del código se parece a algo así

if (SOMETHING)
    reg='eax';
...
switch (reg){
    case 'eax':
    /* and so on*/
}

Puedes estar seguro de que ‘eax’ en la primera parte tiene el mismo valor que ‘eax’ en la segunda parte, así que todo funciona, ¿verdad? … incorrecto.

En un comentario, @Davislor enumera algunos valores posibles para ‘eax’:

0x65, 0x656178, 0x65617800, 0x786165, 0x6165o algo mas

¿Observe el primer valor potencial? eso es solo 'e', ignorando los otros dos caracteres. El problema es que el programa probablemente usa 'eax', 'ebx', y así. Si todas estas constantes tienen el mismo valor que 'e' terminas con

switch (reg){
    case 'e':
       ...
    case 'e':
       ...
    ...
}

Esto no se ve muy bien, ¿verdad?

Lo bueno de “definido por implementación” es que el programador puede consultar la documentación de su compilador y ver si hace algo sensato con estas constantes. Si lo hace, casa gratis.

La parte mala es que algún otro pobre puede tomar el código e intentar compilarlo usando algún otro compilador. Error de compilación instantánea. El programa no es portátil.

Como señaló @zwol en los comentarios, la situación no es tan mala como pensaba, en el mal caso, el código no se compila. Esto al menos le dará un nombre de archivo exacto y un número de línea para el problema. Aún así, no tendrá un programa de trabajo.

  • aparte de alguna forma de assert('eax' != 'ebx'); //if this fails you can't compile the code because... ¿Hay algo que el autor original podría hacer para evitar otras fallas del compilador sin reemplazar la construcción por completo?

    – Dan está jugando a la luz del fuego

    8 de agosto de 2017 a las 14:01


  • Dos etiquetas de caso con el mismo valor son una violación de restricción (6.8.4.2p3: “… ninguna de las dos expresiones constantes de caso en la misma declaración de cambio tendrá el mismo valor después de la conversión”), por lo tanto, siempre que todo el código trata los valores de estas constantes como opacos, se garantiza que funcionará o no se compilará.

    – zwol

    8 de agosto de 2017 a las 17:33


  • La peor parte es que el pobre hombre que compila en otro compilador probablemente no verá ningún tiempo de compilación error (encender ints está bien); en lugar de, tiempo de ejecución aparecerán errores…

    – tucuxi

    9 de agosto de 2017 a las 12:01

El fragmento de código utiliza una rareza histórica llamada constante de caracteres de varios caracterestambién conocido como multi-caracteres.

'eax' es una constante entera cuyo valor está definido por la implementación.

Aquí hay una página interesante sobre caracteres múltiples y cómo se pueden usar pero no se debe:

http://www.zipcon.net/~swhite/docs/computers/languages/c_multi-char_const.html


Mirando hacia atrás más lejos en el espejo retrovisor, así es como el manual C original de Dennis Ritchie de los buenos viejos tiempos ( https://www.bell-labs.com/usr/dmr/www/cman.pdf ) constantes de caracteres especificadas.

2.3.2 Constantes de caracteres

Una constante de carácter tiene 1 o 2 caracteres entre comillas simples ” ' ”. Dentro de una constante de carácter, una comilla simple debe estar precedida por una barra invertida ”\”. Ciertos caracteres no gráficos, y ”\” en sí mismo, se puede escapar de acuerdo con la siguiente tabla:

    BS \b
    NL \n
    CR \r
    HT \t
    ddd \ddd
    \ \\

El escape ”\ddd” consta de la barra invertida seguida de 1, 2 o 3 dígitos octales que se toman para especificar el valor del carácter deseado. Un caso especial de esta construcción es ”\0” (no seguido de un dígito) que indica un carácter nulo.

Las constantes de carácter se comportan exactamente como números enteros (no, en particular, como objetos de tipo carácter). De acuerdo con la estructura de direccionamiento del PDP-11, una constante de carácter de longitud 1 tiene el código para el carácter dado en el byte de orden inferior y 0 en el byte de orden superior; una constante de carácter de longitud 2 tiene el código para el primer carácter en el byte bajo y el del segundo carácter en el byte de orden alto. Las constantes de carácter con más de un carácter son inherentemente dependientes de la máquina y deben evitarse.

La última frase es todo lo que necesitas recordar sobre esta curiosa construcción: Las constantes de carácter con más de un carácter son inherentemente dependientes de la máquina y deben evitarse.

¿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