gozag
¿Hay alguna manera de advertir o prohibir concatenaciones de cadenas literales como:
const char *a = "foo" " bar";
Pasé horas buscando un error en una gran matriz estática que tenía
const char * a[] = {"foo" "bar"};
en lugar de
const char * a[] = {"foo", "bar"};
ciudadano
Clang tiene una advertencia -Concatenación de cadenas de caracteres que está diseñado explícitamente para detectar tales errores:
warning: suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with a comma? [-Wstring-concatenation]
char const *a[] = { "ok", "foo" "bar", "ok"};
^
,
Esto no funcionará exactamente para el ejemplo de juguete que mostró porque necesita tener varios inicializadores y solo perder comas en un par de lugares, es decir:
// no warning
char const *b[] = {"foo" "bar"};
// no warning
char const *c[] = {"ok", "foo" "bar"};
// no warning
char const *d[] = {"foo" "bar", "ok"};
Pero cuando tiene una gran cantidad de inicializadores en una matriz y solo comete un error tipográfico en un par de lugares, esto parece ideal.
Aquí está un manifestación.
GCC no parece tener una advertencia equivalente, pero hay una pedido para que se agregue.
Tenga en cuenta que esto solo funciona para la inicialización de matrices. tu ejemplo de
const char *x = "foo" " bar";
no será detectado por esta advertencia (o cualquier otra que yo sepa).
También tenga en cuenta que habilitar esta advertencia puede generar muchos falsos positivos, pero puede usarlo con moderación cuando intente detectar errores.
-
¡También encontré uno para clang-tidy! clang.llvm.org/extra/clang-tidy/checks/bugprone/…
– gozag
19 de mayo a las 12:35
-
-
GCC generalmente advertirá sobre la concatenación de cadenas con
-Wtraditional
(Solo C), pero habilitar esa opción probablemente no sea recomendable.– nielsen
19 de mayo a las 14:32
-
¿Hay un indicador GCC para detectar la concatenación de cadenas literales?
– phuclv
21 de mayo a las 10:50
No precisamente. La concatenación de cadenas literales es una parte indispensable de la gramática C/C++ y tiene muchos casos de uso. Por lo tanto, se necesita algún tipo de esfuerzo y eso puede frustrar el objetivo de detectar un error garrafal.
Sin embargo, la concatenación de cadenas funciona de manera muy estricta en dos literales de cadena que aparecen uno tras otro con solo un espacio en blanco en el medio, por lo que romper el espacio en blanco provocará un error. Por ejemplo, en este caso podrías haber escrito:
const *char[] = {("foo") ("bar")}; // Error
lo que causaría un error mientras que la declaración prevista no lo haría:
const *char[] = {("foo"), ("bar")}; // OK
Entonces, en resumen, no puede tener alguna forma de decirle explícitamente al compilador que dos literales de cadena pueden concatenarse y fallar en todos los demás casos, por lo que tendrá que decirle al compilador explícitamente cuando un literal de cadena puede estar concatenado. no ser concatenado.
-
“… decirle al compilador explícitamente cuándo no se puede concatenar un literal de cadena…” que tal poner un
,
¿entre ellos? Siento que hemos completado el círculo.– Richard Critten
19 de mayo a las 12:19
-
@RichardCritten Sí, el punto principal es que creo que la solución que busca el OP no existe dentro de los servicios del compilador C/C++.
– nielsen
19 de mayo a las 12:24
-
Creo que la mayoría de los usos “indispensables” implican la concatenación de literales de cadena con macros, por ejemplo, las macros utilizadas en cadenas de formato printf. Es más raro que realmente necesite concatenar solo literales de cadena.
– Barmar
20 de mayo a las 23:04
-
Concateno literales de cadenas desnudos todo el tiempo sin que haya macros involucradas, simplemente porque me gusta envolver líneas de código. La solución propuesta de envolver cada cadena/elemento individual entre paréntesis es intrigante. A pesar de una experiencia bastante amplia escribiendo y leyendo código C y C++, no habría sabido de inmediato si esa sintaxis era válida. Ciertamente tiene sentido que lo sea, pero también me parece sospechoso. Me inclinaría a marcar esto en una revisión de código. Si bien es interesante y una posible solución al problema mencionado, como sugiere Richard, nos lleva al punto de partida.
– Cody Gray – en huelga
♦21 de mayo a las 7:10
-
@CodyGray: Descubrí que los paréntesis eran sorprendentes inicialmente, pero no me llevó mucho pensar por qué es válido: un literal de cadena es un objeto de tipo
const char*
(o posiblementechar*
en C, se me olvida). Los paréntesis pueden aparecer en expresiones y evaluarse como la subexpresión envuelta. Una lista de inicializadores quiere una lista deconst char*
expresiones Tuve que pensarlo durante un par de segundos, pero si una base de código usara esto en todas partes, estaría acostumbrado. Entonces, la pregunta más importante es si este ruido sintáctico adicional para leer el código es peor que el posible problema.– Peter Cordes
22 de mayo a las 4:33
Juan
Cualquiera de las macros a continuación hace que sea imposible concatenar accidentalmente dos cadenas.
PPC (preprocesador C) las macros son increíbles en general. También es legal tener una coma final al final de una lista de elementos.
Puedes hacer algo como esto:
#define STRINGCOMMA(a) a,
const char *x[] = {
STRINGCOMMA("foo")
STRINGCOMMA("bar")
};
O incluso:
#define QUOTESTRINGCOMMA(a) #a,
const char *x[] = {
QUOTESTRINGCOMMA(foo)
QUOTESTRINGCOMMA(bar)};
La coma se agrega por usted, y sería ilegal que usted mismo lo hiciera accidentalmente.
Si está interesado, también es posible llevar este concepto más allá para permitir la creación de listas paralelas con los mismos argumentos, pero con un procesamiento diferente:
#define VARLIST \
DO(foo) \
DO(bar)
#define DO(a) #a,
const char *x[] = {
VARLIST
};
#undef DO
Esto sería útil si desea crear una lista de enumeraciones y una lista de cadenas, a partir de la misma lista de nombres.
Pasé horas encontrando un error en una gran matriz estática…
Bueno, puedes hacer esto:
char const * a [] =
{ "foo"
, "bar"
, "baz"
, "asdf"
, "ghjk"
};
Simpatizo con tu dolor. Probablemente todos hemos estado allí. Pero no querría prohibir la concatenación de cadenas por completo, porque gran parte del código depende de ello deliberadamente.
– Steve cumbre
19 de mayo a las 11:44
Uso esto cuando tengo una cadena de registro muy alta, por lo que una advertencia de este tipo sería algo molesto para mí. Dado que existe un uso válido de esta función, no creo que haya una manera fácil de automatizar la detección de este error tipográfico. Debe asegurarse de que el código esté bien probado.
– Marek R.
19 de mayo a las 11:47
@MarekR Sí, no es infalible. pero no tiene que ser. Es más importante no perderse ninguno. En la práctica, su ejemplo es probablemente muy raro. Otra cosa a verificar son las líneas que terminan con
"
, posibles espacios en blanco al final. Esos pueden ser atrapados congrep '"\s*$'
Si es deseado.– Tom Karzes
19 de mayo a las 12:17
La concatenación de cadenas literales es lógica fase 6 en el proceso de compilación, que ocurre antes de la tokenización. Probablemente no puedas hacer nada sobre el problema.
– Richard Critten
19 de mayo a las 12:17
Algunos candidatos (y/o puntos de partida): ¿Hay un indicador GCC para detectar la concatenación de cadenas literales? (2015. “Recientemente arreglé un error… alguien olvidó un
,
despuésstring3
“) y ¿Por qué permitir la concatenación de literales de cadena? (2010. “Recientemente me picó un insecto sutil… Olvidé el,
despuéstwo
)-Peter Mortensen
21 de mayo a las 11:29