Clang vs GCC para mi proyecto de desarrollo de Linux

175

Estoy en la universidad, y para un proyecto estamos usando C. Hemos explorado GCC y Clang, y Clang parece ser mucho más fácil de usar que GCC. Como resultado, me pregunto cuáles son las ventajas o desventajas de usar clang, a diferencia de GCC, para desarrollar en C y C ++ en Linux.

En mi caso, esto se usaría para programas de nivel de estudiante, no para producción.

Si uso Clang, ¿debo depurar con GDB y usar GNU Make, o usar otro depurador y hacer la utilidad?

Haziz
fuente
77
Por lo que puedo decir, Clang todavía está lejos de ser "maduro", especialmente en lo que respecta al soporte estándar de la biblioteca. No obstante, tiene mensajes de error fantásticos, por lo que siempre puede acercarse a un misterioso error de compilación probando el código en Clang. Clang también puede compilar C ++ a C, creo.
Kerrek SB
3
@KerrekSB: ¿qué elemento del "soporte de biblioteca estándar" falta en el sonido metálico?
Stephen Canon
2
@StephenCanon: La última vez que lo probé, tuve que usar libstdc ++ (que, por lo que entiendo, no es parte de Clang). Y justo el otro día tuvimos este problema . De todos modos, no estoy siguiendo el límite, por lo que mi punto de vista puede ser completamente obsoleto.
Kerrek SB
44
@KerrekSB: En cuanto a su enlace, Clang no funciona en Windows puro. Sin embargo, funciona en MinGW. Con respecto a la biblioteca estándar, no hay una biblioteca estándar real que forme parte de Clang en este momento. Clang está incluido con libc ++ en OSX, sin embargo, libc ++ no está totalmente portado en otros entornos, por lo que esos Clang necesitan otra implementación de la Biblioteca estándar para instalarse. En Linux, libstdc ++ funciona.
Matthieu M.
1
@KerrekSB: C ++ 98 es 100% compatible. C ++ 11 es principalmente compatible (la última vez que lo comprobé, <atomic>no es compatible, tal vez faltan algunas otras cosas pequeñas ... No puedo usarlo, por lo que no estoy completamente al día).
James McNellis el

Respuestas:

122

EDITAR:

Los chicos de gcc realmente mejoraron la experiencia de diagnóstico en gcc (ah competición). Crearon una página wiki para mostrarla aquí . gcc 4.8 ahora también tiene diagnósticos bastante buenos (soporte de color agregado gcc 4.9x). Clang todavía está a la cabeza, pero la brecha se está cerrando.


Original:

Para los estudiantes, recomendaría incondicionalmente Clang.

El rendimiento en términos de código generado entre gcc y Clang ahora no está claro (aunque creo que gcc 4.7 todavía tiene el liderazgo, aún no he visto puntos de referencia concluyentes), pero para que los estudiantes aprendan, en realidad no importa.

Por otro lado, los diagnósticos extremadamente claros de Clang son definitivamente más fáciles de interpretar para los principiantes.

Considere este fragmento simple:

#include <string>
#include <iostream>

struct Student {
std::string surname;
std::string givenname;
}

std::ostream& operator<<(std::ostream& out, Student const& s) {
  return out << "{" << s.surname << ", " << s.givenname << "}";
}

int main() {
  Student me = { "Doe", "John" };
  std::cout << me << "\n";
}

Notarás de inmediato que falta el punto y coma después de la definición de la Studentclase, ¿verdad :)?

Bueno, gcc también lo nota , de una manera:

prog.cpp:9: error: expected initializer before ‘&’ token
prog.cpp: In function int main()’:
prog.cpp:15: error: no match for operator<<’ in std::cout << me
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:112: note: candidates are: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>& (*)(std::basic_ostream<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:121: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ios<_CharT, _Traits>& (*)(std::basic_ios<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:131: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::ios_base& (*)(std::ios_base&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:169: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:173: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:177: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(bool) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/ostream.tcc:97: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(short int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:184: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(short unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/ostream.tcc:111: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:195: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:204: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long long int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:208: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long long unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:213: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(double) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:217: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(float) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:225: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long double) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:229: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(const void*) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/ostream.tcc:125: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_streambuf<_CharT, _Traits>*) [with _CharT = char, _Traits = std::char_traits<char>]

Y Clang tampoco protagoniza exactamente aquí, pero aún así:

/tmp/webcompile/_25327_1.cc:9:6: error: redefinition of 'ostream' as different kind of symbol
std::ostream& operator<<(std::ostream& out, Student const& s) {
     ^
In file included from /tmp/webcompile/_25327_1.cc:1:
In file included from /usr/include/c++/4.3/string:49:
In file included from /usr/include/c++/4.3/bits/localefwd.h:47:
/usr/include/c++/4.3/iosfwd:134:33: note: previous definition is here
  typedef basic_ostream<char>           ostream;        ///< @isiosfwd
                                        ^
/tmp/webcompile/_25327_1.cc:9:13: error: expected ';' after top level declarator
std::ostream& operator<<(std::ostream& out, Student const& s) {
            ^
            ;
2 errors generated.

Elegí a propósito un ejemplo que desencadena un mensaje de error poco claro (proveniente de una ambigüedad en la gramática) en lugar de los ejemplos típicos de "Oh, Dios mío, Clang leyó mi mente". Aún así, notamos que Clang evita la avalancha de errores. No hay necesidad de asustar a los estudiantes.

Matthieu M.
fuente
2
Um ... la última vez que lo revisé, leí un artículo que publicaba varios puntos de referencia en los que el ruido metálico casi voló el agua en cualquier prueba. Fuente: clang.llvm.org/features.html#performance
31
@AscensionSystems: tenga cuidado, esas pruebas muestran el rendimiento del binario Clang en sí (y eso fue hace un tiempo), no el rendimiento del binario que estaba compilando.
Matthieu M.
Ese es un buen punto, me interesaría ver una comparación de pie entre los ejecutables compilados. Tengo la impresión de que el sonido metálico hace un trabajo mucho mejor en la optimización, pero en realidad no he visto ningún punto de referencia. Lo comprobaré.
44
@AscensionSystems: aquí está el último banco que conozco al comparar gcc 4.6 con llvm 3.0, que muestra una ventaja neta de gcc en promedio. También puede ser interesante el banco DragonEgg , DragonEgg es un complemento que permite usar el front-end gcc (y posiblemente el optimizador) y luego el back-end LLVM para generar el código.
Matthieu M.
1
La última vez que lo verifiqué, los puntos de referencia de phoronix eran muy poco confiables: los indicadores del compilador no se documentaron correctamente, pero los resultados sugirieron que las cosas no se configuraron correctamente.
Eamon Nerbonne
35

A partir de ahora, GCC tiene un soporte mucho mejor y más completo para las funciones de C ++ 11 que Clang. Además, el generador de código para GCC realiza una mejor optimización que el de Clang (en mi experiencia, no he visto ninguna prueba exhaustiva).

Por otro lado, Clang a menudo compila código más rápido que GCC y produce mejores mensajes de error cuando hay algo mal con su código.

La elección de cuál usar realmente depende de qué cosas son importantes para usted. Valoro el soporte de C ++ 11 y la calidad de generación de código más de lo que valoro la conveniencia de la compilación. Debido a esto, uso GCC. Para usted, las compensaciones pueden ser diferentes.

Mankarse
fuente
3
Aquí está el último artículo de Phoronix que compara GCC 4.6 frente a Clang 3.0 , así como un artículo anterior específico de la plataforma excavadora. Dependiendo de los puntos de referencia, el ganador es uno u otro (en el artículo anterior, también aparece gcc 4.7), por lo que personalmente no me queda claro cuál está funcionando mejor.
Matthieu M.
¿Por qué no usar ambos? Clang para el desarrollo y GCC para la producción.
segfault
55
@segfault: Eso es lo que estoy haciendo actualmente. Esta respuesta es bastante antigua y ya no es del todo cierta. Tanto Clang como GCC han mejorado significativamente desde que lo escribí (en particular, Clang ahora coincide con el soporte general de C ++ 11 de GCC, y GCC ha mejorado sus mensajes de error y la velocidad de compilación). Ahora sugeriría usar ambos, con una ligera preferencia hacia Clang porque el código fuente de Clang es mucho más fácil de entender que la fuente de GCC.
Mankarse
23

Utilizo ambos porque a veces dan mensajes de error diferentes y útiles.

El proyecto Python fue capaz de encontrar y corregir una serie de pequeños buglets cuando uno de los desarrolladores principales intentó compilar con clang.

Raymond Hettinger
fuente
1
¿Qué piensas sobre el uso de clang para compilaciones de depuración pero gcc para versiones optimizadas?
Olical
55
Es razonable desarrollar con Clang y lanzar con GCC, pero asegúrese de que su versión de GCC pase su conjunto de pruebas (con y sin NDEBUG).
Raymond Hettinger
2
Gracias por la respuesta. Lo he estado probando por un tiempo y funciona muy bien. También recibo diferentes conjuntos de advertencias, lo cual es genial.
Olical 13/12/12
11

Utilizo Clang y GCC, creo que Clang tiene algunas advertencias útiles, pero para mis propios puntos de referencia de trazado de rayos: es consistentemente un 5-15% más lento que GCC (por supuesto, tome eso con un grano de sal, pero intentó usar indicadores de optimización similares para ambos).

Entonces, por ahora, uso el análisis estático de Clang y sus advertencias con macros complejas: (aunque ahora las advertencias de GCC son bastante buenas, gcc4.8 - 4.9).

Algunas consideraciones

  • Clang no tiene soporte para OpenMP, solo importa si aprovechas eso, pero como lo hago, es una limitación para mí. (*****)
  • Es posible que la compilación cruzada no sea tan compatible (FreeBSD 10, por ejemplo, todavía usa GCC4.x para ARM), gcc-mingw, por ejemplo, está disponible en Linux ... (YMMV).
  • Algunos IDE aún no admiten el análisis de la salida de Clangs ( QtCreator, por ejemplo, *****). EDITAR: QtCreator ahora es compatible con la salida de Clang
  • Algunos aspectos de GCC están mejor documentados y dado que GCC ha existido por más tiempo y se usa ampliamente, es posible que le resulte más fácil obtener ayuda con advertencias / mensajes de error.

***** - estas áreas están en desarrollo activo y pronto pueden ser compatibles

ideasman42
fuente
También uso OpenMP, pero estoy pensando en cambiarme a TBB, que creo que funcionaría con Clang.
1
TBB puede ser una alternativa viable para OpenMP en algunos casos (pero solo para C ++ por lo que puedo decir), para C no es compatible, también para proyectos grandes, cambiar de OpenMP a otra cosa podría no valer la pena, especialmente si Clang eventualmente Soporta OpenMP de todos modos.
ideasman42 01 de
7

Para los programas de nivel de estudiante, Clang tiene el beneficio de que, por defecto, es más estricto. El estándar C. Por ejemplo, la siguiente versión K&R de Hello World es aceptada sin previo aviso por GCC, pero Clang la rechaza con algunos mensajes de error bastante descriptivos:

main()
{
    puts("Hello, world!");
}

Con GCC, tiene que darlo -Werrorpara que realmente haga un punto sobre que este no es un programa C89 válido. Además, aún necesita usar c99u gcc -std=c99obtener el lenguaje C99.

Fred Foo
fuente
8
gccgeneralmente debería invocarse con al menos -Wall, lo que advierte sobre este programa. clangproduce buenas advertencias / errores, sin embargo.
caf
2
@caf: que es exactamente el punto que estoy tratando de hacer, con GCC tienes que pasarle las opciones. Fuera de la caja, puede ser demasiado tolerante para fines de enseñanza.
Fred Foo
Eso puede ser cierto, pero es un punto bastante menor. Lo que es más importante es la calidad de los mensajes de error. GCC 4.6 se ha vuelto bastante bueno, aunque entiendo que el sonido metálico está haciendo algo de magia real allí.
Kerrek SB
2
@dreamlax: verdadero; también hay gnu99y gnu++98y gnu++0x. Sin embargo, creo que son extensiones genuinas , es decir, compilarán el código estándar ISO sin problemas. Aquí están los detalles: para C , para C ++ .
Kerrek SB
1
Este programa no debe producir errores o advertencias. Se ajusta a la norma.
Miles Rout
3

Creo que el ruido metálico podría ser una alternativa.

GCC y clang tienen algunas diferencias en expresiones como a+++++a, y tengo muchas respuestas diferentes con mis compañeros que usan clang en Mac mientras uso gcc.

El CCG se ha convertido en el estándar, y el sonido metálico podría ser una alternativa. Porque el CCG es muy estable y el sonido metálico aún está en desarrollo.

Songziming
fuente
55
Clang se está preparando rápidamente para reemplazar GCC por completo en el mundo de Linux, y lo ha hecho en gran medida en el mundo de BSD. Reemplazó a GCC en Mac hace años. El argot es algo bueno. Creo que GCC podría convertirse en una alternativa, personalmente, y eso me alegraría.
coder543
55
La expresión a +++++ a no está definida, por lo tanto, espere obtener una respuesta diferente en cada compilador, o incluso en diferentes versiones del mismo compilador. Incluso podría obtener diferentes resultados para esa expresión en el mismo compilador cuando se compila en diferentes momentos. Eso es lo que significa "indefinido".
Lelanthran
1
a+++++adebería fallar, ya que se analiza como a ++ ++ + acuál es un error de sintaxis.
Miles Rout
@Lelanthran eso no es lo que significa indefinido. Tiene un comportamiento indefinido, por lo que el compilador puede fallar al compilar eso, o puede lanzar en tiempo de ejecución o bloquear la CPU para que necesite hacer un restablecimiento completo o algo aún más siniestro.
Antti Haapala