Durante el análisis de código, PVS-Studio analiza el flujo de datos y opera sobre los valores de las variables. Los valores se toman de constantes o se infieren de expresiones condicionales. Los llamamos valores virtuales. Recientemente, los mejoramos para que funcionen con constantes de varios caracteres y esta fue la razón para crear una nueva regla de diagnóstico.
Introduccion
Un literal de varios caracteres está
definido por la implementación , por lo que diferentes compiladores pueden codificar estos literales de diferentes maneras. Por ejemplo, GCC y Clang establecen un valor basado en el orden de los caracteres en un literal, mientras que MSVC los mueve según el tipo de carácter (regular o de escape).
Por ejemplo, el literal 'T \ x65s \ x74' se codificará de diferentes maneras, dependiendo del compilador. Una lógica similar tuvo que agregarse al analizador. Como resultado, creamos una nueva regla de diagnóstico V1039 para identificar dichos literales en el código. Tales literales son peligrosos en proyectos multiplataforma que utilizan múltiples compiladores para el ensamblaje.
Diagnóstico V1039
Considera un ejemplo. El siguiente código, compilado por varios compiladores, se comportará de manera diferente:
#include <stdio.h> void foo(int c) { if (c == 'T\x65s\x74') // <= V1039 { printf("Compiled with GCC or Clang.\n"); } else { printf("It's another compiler (for example, MSVC).\n"); } } int main(int argc, char** argv) { foo('Test'); return 0; }
Un programa compilado por diferentes compiladores imprimirá diferentes mensajes en la pantalla.
Para un proyecto que utiliza un compilador específico, esto no se notará, pero la transferencia puede causar problemas, por lo que debe reemplazar dichos literales con constantes numéricas simples, por ejemplo, cambie 'Prueba' a 0x54657374.
Para demostrar la diferencia entre compiladores, escribimos una pequeña utilidad donde se toman secuencias de 3 y 4 caracteres, por ejemplo, 'GHIJ' y 'GHI', y su representación en la memoria después de que se muestra la compilación.
Código de utilidad:
#include <stdio.h> typedef int char_t; void PrintBytes(const char* format, char_t lit) { printf("%20s : ", format); const unsigned char *ptr = (const unsigned char*)&lit; for (int i = sizeof(lit); i--;) { printf("%c", *ptr++); } putchar('\n'); } int main(int argc, char** argv) { printf("Hex codes are: G(%02X) H(%02X) I(%02X) J(%02X)\n",'G','H','I','J'); PrintBytes("'GHIJ'", 'GHIJ'); PrintBytes("'\\x47\\x48\\x49\\x4A'", '\x47\x48\x49\x4A'); PrintBytes("'G\\x48\\x49\\x4A'", 'G\x48\x49\x4A'); PrintBytes("'GH\\x49\\x4A'", 'GH\x49\x4A'); PrintBytes("'G\\x48I\\x4A'", 'G\x48I\x4A'); PrintBytes("'GHI\\x4A'", 'GHI\x4A'); PrintBytes("'GHI'", 'GHI'); PrintBytes("'\\x47\\x48\\x49'", '\x47\x48\x49'); PrintBytes("'GH\\x49'", 'GH\x49'); PrintBytes("'\\x47H\\x49'", '\x47H\x49'); PrintBytes("'\\x47HI'", '\x47HI'); return 0; }
El resultado de la utilidad compilada por Visual C ++:
Hex codes are: G(47) H(48) I(49) J(4A) 'GHIJ' : JIHG '\x47\x48\x49\x4A' : GHIJ 'G\x48\x49\x4A' : HGIJ 'GH\x49\x4A' : JIHG 'G\x48I\x4A' : JIHG 'GHI\x4A' : JIHG 'GHI' : IHG '\x47\x48\x49' : GHI 'GH\x49' : IHG '\x47H\x49' : HGI '\x47HI' : IHG
La salida de una utilidad compilada por GCC o Clang:
Hex codes are: G(47) H(48) I(49) J(4A) 'GHIJ' : JIHG '\x47\x48\x49\x4A' : JIHG 'G\x48\x49\x4A' : JIHG 'GH\x49\x4A' : JIHG 'G\x48I\x4A' : JIHG 'GHI\x4A' : JIHG 'GHI' : IHG '\x47\x48\x49' : IHG 'GH\x49' : IHG '\x47H\x49' : IHG '\x47HI' : IHG
Conclusión
El diagnóstico V1039 se agregó al analizador PVS-Studio versión
7.03 , que se lanzó recientemente. Puede descargar la última versión del analizador en
la página de descarga .

Si desea compartir este artículo con una audiencia de habla inglesa, utilice el enlace a la traducción: Svyatoslav Razmyslov.
Los peligros de usar constantes de varios caracteres