Los 10 errores principales en proyectos de C ++ para 2018

Ya han pasado tres meses desde que termin贸 2018. Para muchos, vol贸 casi imperceptiblemente, pero para nosotros, los desarrolladores de PVS-Studio, result贸 estar muy saturado. Trabajamos duro, luchamos sin miedo por el avance del an谩lisis est谩tico a las masas y buscamos nuevos errores en proyectos de c贸digo abierto escritos en C, C ++, C # y Java. 隆Los diez m谩s interesantes que hemos recopilado para ti en este art铆culo!



Buscamos lugares interesantes utilizando el analizador de c贸digo est谩tico PVS-Studio . Puede detectar errores y vulnerabilidades potenciales en el c贸digo escrito en los idiomas mencionados anteriormente.

Si est谩 interesado en buscar errores usted mismo, siempre puede descargar y probar nuestro analizador. Proporcionamos una versi贸n gratuita del analizador para estudiantes y programadores entusiastas, una licencia gratuita para desarrolladores de proyectos de c贸digo abierto, as铆 como una versi贸n de prueba para todo. Qui茅n sabe, 驴quiz谩s para el pr贸ximo a帽o puedas estar entre los 10 mejores? :)

Nota: sugiero que el lector se revise a s铆 mismo y, antes de mirar la advertencia del analizador, intente identificar las anomal铆as 茅l mismo. 驴Cu谩ntos errores puedes encontrar?

D茅cimo lugar


Fuente: Y nuevamente al espacio: c贸mo visit贸 el unicornio Stellarium

Este error fue descubierto al verificar el planetario virtual Stellarium.

El fragmento de c贸digo anterior, aunque peque帽o, est谩 plagado de un error bastante complicado:

Plane::Plane(Vec3f &v1, Vec3f &v2, Vec3f &v3) : distance(0.0f), sDistance(0.0f) { Plane(v1, v2, v3, SPolygon::CCW); } 

Has encontrado

Advertencia de PVS-Studio: V603 El objeto fue creado pero no se est谩 utilizando. Si desea llamar al constructor, debe usar 'this-> Plane :: Plane (....)'. Plane.cpp 29

El autor del c贸digo quer铆a inicializar parte de los campos del objeto utilizando otro constructor anidado en el principal. Es cierto, en cambio, solo logr贸 crear un objeto temporal que ser铆a destruido al abandonar su 谩rea de visibilidad. Por lo tanto, varios campos del objeto permanecer谩n sin inicializar.

En lugar de una llamada de constructor anidada, debe usar el constructor delegante introducido en C ++ 11. Por ejemplo, podr铆as hacer esto:

 Plane::Plane(Vec3f& v1, Vec3f& v2, Vec3f& v3) : Plane(v1, v2, v3, SPolygon::CCW) { distance = 0.0f; sDistance = 0.0f; } 

Entonces todos los campos requeridos se inicializar谩n correctamente. 驴No es maravilloso?

Noveno lugar


Fuente: Perl 5: C贸mo se ocultan los errores de macro

En noveno lugar hace alarde de una macro notable del c贸digo fuente de Perl 5.

Al recopilar errores para escribir el art铆culo, mi colega Svyatoslav se encontr贸 con una advertencia emitida por el analizador sobre el uso de una macro. Aqu铆 esta:

 PP(pp_match) { .... MgBYTEPOS_set(mg, TARG, truebase, RXp_OFFS(prog)[0].end); .... } 

Para averiguar cu谩l era el problema, Svyatoslav profundiz贸. Abri贸 la definici贸n de macro y vio que conten铆a varias macros anidadas m谩s, algunas de las cuales tambi茅n ten铆an macros anidadas. Fue tan dif铆cil de entender que tuve que usar un archivo preprocesado. Pero, por desgracia, esto no ayud贸. En lugar de la l铆nea de c贸digo anterior, Svyatoslav descubri贸 esto:

 (((targ)->sv_flags & 0x00000400) && (!((targ)->sv_flags & 0x00200000) || S_sv_only_taint_gmagic(targ)) ? (mg)->mg_len = ((prog->offs)[0].end), (mg)->mg_flags |= 0x40 : ((mg)->mg_len = (((targ)->sv_flags & 0x20000000) && !__builtin_expect(((((PL_curcop)->cop_hints + 0) & 0x00000008) ? (_Bool)1 :(_Bool)0),(0))) ? (ssize_t)Perl_utf8_length( (U8 *)(truebase), (U8 *)(truebase)+((prog->offs)[0].end)) : (ssize_t)((prog->offs)[0].end), (mg)->mg_flags &= ~0x40)); 

Advertencia PVS-Studio: V502 Quiz谩s el operador '?:' Funciona de una manera diferente a la esperada. El operador '?:' Tiene una prioridad menor que el operador '&&'. pp_hot.c 3036

Creo que ser谩 dif铆cil encontrar tal error con mis ojos. Honestamente, meditamos en este c贸digo durante mucho tiempo y llegamos a la conclusi贸n de que, de hecho, no hay ning煤n error aqu铆. Pero en cualquier caso, este es un ejemplo bastante entretenido de c贸digo ilegible.

Se dice que las macros son malvadas. Por supuesto, hay momentos en que resultan ser indispensables, pero si puede reemplazar la macro con una funci贸n, definitivamente deber铆a hacerlo.

Las macros anidadas son especialmente pesadas. No solo porque son dif铆ciles de entender, sino tambi茅n porque pueden dar resultados impredecibles. Si el autor de una macro accidentalmente comete un error en dicha macro, ser谩 mucho m谩s dif铆cil encontrarla que en una funci贸n.

Octavo lugar


Fuente: cromo: otros errores

El siguiente ejemplo fue tomado de una serie de art铆culos sobre el an谩lisis del proyecto Chromium. Se cubri贸 en la biblioteca WebRTC.

 std::vector<SdpVideoFormat> StereoDecoderFactory::GetSupportedFormats() const { std::vector<SdpVideoFormat> formats = ....; for (const auto& format : formats) { if (cricket::CodecNamesEq(....)) { .... formats.push_back(stereo_format); } } return formats; } 

Advertencia de PVS-Studio: V789 CWE-672 Los iteradores para el contenedor de 'formatos', utilizados en el bucle basado en rango, se vuelven inv谩lidos cuando se llama a la funci贸n 'push_back'. stereocodecfactory.cc 89

El error es que el tama帽o del vector de formatos cambia dentro del bucle basado en rango. Los bucles basados 鈥嬧媏n el rango se basan en iteradores, por lo que cambiar el tama帽o del contenedor dentro de dichos bucles puede provocar la invalidaci贸n de estos iteradores.

Este error persistir谩 si reescribe el bucle utilizando iteradores expl铆citos. Por lo tanto, para mayor claridad, puede traer este c贸digo:

 for (auto format = begin(formats), __end = end(formats); format != __end; ++format) { if (cricket::CodecNamesEq(....)) { .... formats.push_back(stereo_format); } } 

Por ejemplo, cuando se usa el m茅todo push_back , se puede liberar un vector, y luego los iteradores apuntar谩n a un 谩rea de memoria no v谩lida.

Para evitar tales errores, debe cumplir con la regla: nunca cambie el tama帽o del contenedor dentro del bucle, cuyas condiciones est谩n vinculadas a este contenedor. Esto se aplica a bucles basados 鈥嬧媏n rango y bucles que utilizan iteradores. Puede leer sobre qu茅 operaciones pueden conducir a la invalidaci贸n de iteradores en las discusiones sobre StackOverflow.

S茅ptimo lugar


Fuente: Godot: uso regular de analizadores de c贸digo est谩tico

El primer ejemplo de la industria de los videojuegos ser谩 un fragmento de c贸digo que descubrimos en el motor de juegos Godot. Puede que tenga que sudar para descubrir el error con sus ojos, pero estoy seguro de que nuestros lectores sofisticados pueden manejarlo:

 void AnimationNodeBlendSpace1D::add_blend_point( const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index) { ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS); ERR_FAIL_COND(p_node.is_null()); ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used); if (p_at_index == -1 || p_at_index == blend_points_used) { p_at_index = blend_points_used; } else { for (int i = blend_points_used - 1; i > p_at_index; i++) { blend_points[i] = blend_points[i - 1]; } } .... } 

Advertencia de PVS-Studio: V621 CWE-835 Considere inspeccionar el operador 'para'. Es posible que el bucle se ejecute incorrectamente o no se ejecute en absoluto. animation_blend_space_1d.cpp 113

Consideremos la condici贸n del ciclo con m谩s detalle. La variable de contador se inicializa con el valor blend_points_used - 1 . Al mismo tiempo, en funci贸n de las dos comprobaciones anteriores (en ERR_FAIL_COND y en if ), queda claro que en el momento en que se ejecuta el bucle, blend_points_used siempre ser谩 mayor que p_at_index . Por lo tanto, la condici贸n del bucle siempre ser谩 verdadera o el bucle no se ejecutar谩 en absoluto.

Si blend_points_used es 1 == p_at_index , entonces el ciclo no se ejecuta.

En todos los dem谩s casos, la comprobaci贸n i> p_at_index siempre ser谩 verdadera, ya que el contador i aumenta en cada iteraci贸n del bucle.

Puede parecer que el ciclo se ejecutar谩 para siempre, pero no lo es.

Primero, habr谩 un desbordamiento de enteros de la variable i , que es un comportamiento indefinido. Por lo tanto, confiar en esto no vale la pena.

Si fuera de tipo unsigned int , luego de que el contador alcance el valor m谩ximo posible, el operador i ++ lo convertir铆a en 0 . Este comportamiento est谩 definido por el est谩ndar y se denomina "ajuste sin firmar". Sin embargo, debe tener en cuenta que el uso de dicho mecanismo tampoco es una buena idea .

Eso fue primero, 隆pero todav铆a hay segundo! El hecho es que ni siquiera alcanzar谩 el desbordamiento de enteros. Donde antes la matriz se va al extranjero. Esto significa que se intentar谩 acceder al 谩rea de memoria fuera del bloque asignado para la matriz. Y esto tambi茅n es un comportamiento vago. Ejemplo cl谩sico :)

Para que sea m谩s f谩cil evitar tales errores, solo puedo dar un par de recomendaciones:

  1. Escriba c贸digo m谩s simple e intuitivo
  2. Haga una revisi贸n de c贸digo m谩s exhaustiva y escriba m谩s pruebas para el c贸digo reci茅n escrito
  3. Use analizadores est谩ticos;)


Sexto lugar


Fuente: Amazon Lumberyard: The Cry of the Soul

Otro ejemplo de la industria de gamedev, es decir, del c贸digo fuente del motor AAA de Amazon Lumberyard.

 void TranslateVariableNameByOperandType(....) { // Igor: yet another Qualcomm's special case // GLSL compiler thinks that -2147483648 is // an integer overflow which is not if (*((int*)(&psOperand->afImmediates[0])) == 2147483648) { bformata(glsl, "-2147483647-1"); } else { // Igor: this is expected to fix // paranoid compiler checks such as Qualcomm's if (*((unsigned int*)(&psOperand->afImmediates[0])) >= 2147483648) { bformata(glsl, "%d", *((int*)(&psOperand->afImmediates[0]))); } else { bformata(glsl, "%d", *((int*)(&psOperand->afImmediates[0]))); } } bcatcstr(glsl, ")"); .... } 

Advertencia de PVS-Studio: V523 La declaraci贸n 'then' es equivalente a la declaraci贸n 'else'. toglsloperand.c 700

Amazon Lumberyard se est谩 desarrollando como un motor multiplataforma. Por lo tanto, los desarrolladores est谩n tratando de soportar tantos compiladores como sea posible. El programador Igor se top贸 con el compilador Qualcomm, como nos dicen los comentarios.

No se sabe si Igor pudo completar su tarea y hacer frente a los controles "paranoicos" del compilador, pero dej贸 un c贸digo muy extra帽o. Es extra帽o que tanto las ramas de la declaraci贸n if como las dem谩s contengan un c贸digo absolutamente id茅ntico. Lo m谩s probable es que dicho error se haya producido como resultado de un descuidado Copy-Paste.

Ni siquiera s茅 qu茅 se puede aconsejar aqu铆. Por lo tanto, solo deseo que los desarrolladores de Amazon Lumberyard tengan 茅xito en la correcci贸n de errores, 隆y buena suerte para el programador Igor!

Quinto lugar


Fuente: una vez m谩s, el analizador PVS-Studio result贸 ser m谩s atento que una persona

Una historia interesante sucedi贸 con el siguiente ejemplo. Mi colega Andrei Karpov estaba preparando un art铆culo sobre la pr贸xima prueba del marco Qt. En el curso de escribir errores notables, se encontr贸 con una advertencia del analizador, que consider贸 falsa. Aqu铆 est谩 el fragmento de c贸digo relevante y la advertencia:

 QWindowsCursor::CursorState QWindowsCursor::cursorState() { enum { cursorShowing = 0x1, cursorSuppressed = 0x2 }; CURSORINFO cursorInfo; cursorInfo.cbSize = sizeof(CURSORINFO); if (GetCursorInfo(&cursorInfo)) { if (cursorInfo.flags & CursorShowing) // <= V616 .... } 

Advertencia de PVS-Studio: V616 CWE-480 La constante con nombre 'CursorShowing' con el valor de 0 se utiliza en la operaci贸n bit a bit. qwindowscursor.cpp 669

Es decir, PVS-Studio jur贸 en un lugar donde, obviamente, 隆no hubo error! No puede ser que la constante CursorShowing sea 0 , porque literalmente un par de l铆neas encima se inicializan a 1 .

Como se us贸 una versi贸n inestable del analizador para la verificaci贸n, Andrei dud贸 de la exactitud de la advertencia. Examin贸 cuidadosamente esta secci贸n de c贸digo varias veces, y a煤n no encontr贸 un error. Como resultado, escribi贸 este falso positivo al rastreador de errores para que otros colegas pudieran corregir la situaci贸n.

Y solo con un an谩lisis detallado qued贸 claro que PVS-Studio volvi贸 a estar m谩s atento que una persona. El valor 0x1 se asigna a la constante nombrada cursorShowing , y la operaci贸n constante de bit "y" involucra la constante nombrada CursorShowing . Estas son constantes completamente diferentes, porque la primera comienza con una letra min煤scula y la segunda con una letra may煤scula.

El c贸digo se compila correctamente, porque la clase QWindowsCursor realmente contiene una constante con ese nombre. Aqu铆 est谩 su definici贸n:

 class QWindowsCursor : public QPlatformCursor { public: enum CursorState { CursorShowing, CursorHidden, CursorSuppressed }; .... } 

Si no asigna expl铆citamente una constante enum con nombre, se inicializar谩 de manera predeterminada. Como CursorShowing es el primer elemento de una enumeraci贸n, se establecer谩 en 0 .

Para evitar tales errores, no debe dar a las entidades nombres demasiado similares. Debe seguir especialmente esta regla con mucho cuidado si estas entidades son del mismo tipo o se pueden lanzar impl铆citamente entre s铆. De hecho, en tales casos ser谩 casi imposible detectar un error a simple vista, y el c贸digo incorrecto se compilar谩 con 茅xito y vivir谩 felizmente dentro de su proyecto.

Cuarto lugar


Fuente: disparamos en el pie, procesando los datos de entrada

Nos estamos acercando a los tres primeros finalistas, y el error del proyecto FreeSWITCH es el siguiente.

 static const char *basic_gets(int *cnt) { .... int c = getchar(); if (c < 0) { if (fgets(command_buf, sizeof(command_buf) - 1, stdin) != command_buf) { break; } command_buf[strlen(command_buf)-1] = '\0'; /* remove endline */ break; } .... } 

Advertencia de PVS-Studio: V1010 CWE-20 Los datos contaminados no verificados se usan en el 铆ndice: 'strlen (command_buf)'.

El analizador advierte que la expresi贸n strlen (command_buf) - 1 usa datos no verificados. Y realmente: si command_buf resulta estar vac铆o desde el punto de vista de la cadena de lenguaje C (que contiene un solo car谩cter - '\ 0'), entonces strlen (command_buf) devolver谩 0 . En este caso, se llamar谩 a command_buf [-1] , que representa un comportamiento indefinido. Problemas

La esencia de este error no es ni siquiera por qu茅 sucede, sino c贸mo sucede. Este error es uno de esos ejemplos agradables que puede "tocar" por su cuenta, reproducir. Puede iniciar FreeSwitch, realizar algunas acciones que conducir谩n a la ejecuci贸n de la secci贸n de c贸digo anterior y pasar al programa una l铆nea vac铆a para la entrada.

Como resultado, con un movimiento de mu帽eca, un programa de trabajo convierte (no, no pantalones cortos elegantes ) en uno que no funciona. Los detalles sobre c贸mo reproducir este error se pueden encontrar en el art铆culo fuente en el enlace de arriba, pero por ahora dar茅 un resultado claro:



Recuerde que la entrada puede ser cualquier cosa, y siempre debe verificarla. Entonces el analizador no jurar谩, y el programa ser谩 m谩s confiable.

Ahora es el momento de lidiar con nuestros ganadores: 隆nos estamos moviendo a la final!



Tercer lugar


Fuente: NCBI Genome Workbench: Endangered Research

Los tres ganadores son abiertos por un c贸digo del proyecto NCBI Genome Workbench, un conjunto de herramientas para estudiar y analizar datos gen茅ticos. Aunque no es necesario ser un superhombre gen茅ticamente modificado para encontrar un error aqu铆, muchos son conscientes de esta posibilidad.

 /** * Crypt a given password using schema required for NTLMv1 authentication * @param passwd clear text domain password * @param challenge challenge data given by server * @param flags NTLM flags from server side * @param answer buffer where to store crypted password */ void tds_answer_challenge(....) { .... if (ntlm_v == 1) { .... /* with security is best be pedantic */ memset(hash, 0, sizeof(hash)); memset(passwd_buf, 0, sizeof(passwd_buf)); ... } else { .... } } 

Advertencias de PVS-Studio:

  • V597 El compilador podr铆a eliminar la llamada de funci贸n 'memset', que se utiliza para vaciar el b煤fer 'hash'. La funci贸n memset_s () debe usarse para borrar los datos privados. challenge.c 365
  • V597 El compilador podr铆a eliminar la llamada de funci贸n 'memset', que se utiliza para vaciar el b煤fer 'passwd_buf'. La funci贸n memset_s () debe usarse para borrar los datos privados. challenge.c 366

驴Lograste encontrar un error? Si es as铆, entonces ... 隆bien hecho! ... bueno, o a煤n un superhombre gen茅ticamente modificado.

El hecho es que los compiladores de optimizaci贸n modernos pueden hacer mucho para que el programa ensamblado funcione m谩s r谩pido. En particular, los compiladores pueden rastrear que el b煤fer pasado a memset no se usa en ning煤n otro lugar.

En este caso, pueden eliminar la llamada de memset "innecesaria" y tienen todo el derecho de hacerlo. Entonces, un b煤fer que almacena datos importantes puede permanecer en la memoria para el deleite de los atacantes.

En este contexto, el comentario literario "con seguridad es bueno ser pedante" parece a煤n m谩s divertido. A juzgar por el muy peque帽o n煤mero de advertencias emitidas para este proyecto, los desarrolladores se esforzaron mucho por tener cuidado y escribir un c贸digo seguro. Sin embargo, como podemos ver, omitir esta falla de seguridad es muy simple. De acuerdo con la Enumeraci贸n de Debilidad Com煤n, un defecto se clasifica como CWE-14 : Eliminaci贸n del C贸digo por parte del Compilador para Borrar Buffers.

Para borrar la limpieza de memoria, use la funci贸n memset_s () . No solo es m谩s seguro que memset () , sino que tampoco puede ser "ignorado" por el compilador.

Segundo lugar


Fuente: c贸mo PVS-Studio result贸 ser m谩s atento que tres programadores y medio

El medallista de plata de este top nos lo envi贸 uno de nuestros clientes. Estaba seguro de que el analizador generaba falsas advertencias.

Eugene recibi贸 la carta, la escane贸 brevemente y se la envi贸 a Svyatoslav. Svyatoslav mir贸 pensativamente la secci贸n de c贸digo enviada por el cliente y pens贸: "驴Puede el analizador estar tan descaradamente equivocado?" Por lo tanto, fue a consultar con Andrei. Tambi茅n revis贸 el sitio y decidi贸: de hecho, el analizador da falsos positivos.

驴Qu茅 puedes hacer? Necesitas arreglarlo. Y solo cuando Svyatoslav comenz贸 a hacer ejemplos sint茅ticos para formalizar la tarea como rastreador de errores, se dio cuenta de lo que estaba sucediendo.

De hecho, hubo errores en el c贸digo, pero ninguno de los programadores pudo detectarlos. Honestamente, el autor de este art铆culo tampoco tuvo 茅xito.

隆Y esto a pesar del hecho de que el analizador claramente emiti贸 advertencias para los lugares equivocados!

驴Puedes encontrar un error tan astuto? Ponte a prueba para la vigilancia y la atenci贸n.


Advertencia de PVS-Studio:
  • V560 Una parte de la expresi贸n condicional siempre es falsa: (ch> = 0x0FF21). decodew.cpp 525
  • V560 Una parte de la expresi贸n condicional siempre es verdadera: (ch <= 0x0FF3A). decodew.cpp 525
  • V560 Una parte de la expresi贸n condicional siempre es falsa: (ch> = 0x0FF41). decodew.cpp 525
  • V560 Una parte de la expresi贸n condicional siempre es verdadera: (ch <= 0x0FF5A). decodew.cpp 525

Si tiene 茅xito, 隆no tendr谩 mi respeto!

El error radica en el hecho de que el operador de negaci贸n l贸gica (!) No se aplica a toda la condici贸n, sino solo a su primera subexpresi贸n:

 !((ch >= 0x0FF10) && (ch <= 0x0FF19)) 

Si se cumple esta condici贸n, el valor de la variable ch se encuentra en el intervalo [0x0FF10 ... 0x0FF19]. Por lo tanto, las cuatro comparaciones adicionales ya no tienen sentido: siempre ser谩n verdaderas o falsas.

Para evitar tales errores, vale la pena seguir algunas reglas. Primero, es muy conveniente y claro alinear el c贸digo con una tabla. En segundo lugar, no sobrecargue las expresiones con par茅ntesis. Por ejemplo, este c贸digo se puede reescribir as铆:

 const bool isLetterOrDigit = (ch >= 0x0FF10 && ch <= 0x0FF19) // 0..9 || (ch >= 0x0FF21 && ch <= 0x0FF3A) // A..Z || (ch >= 0x0FF41 && ch <= 0x0FF5A); // a..z if (!isLetterOrDigit) 

Luego, en primer lugar, el n煤mero de par茅ntesis se vuelve mucho m谩s peque帽o y, en segundo lugar, aumenta la probabilidad de "atrapar" un error cometido por los ojos.

Y ahora, cereza: 隆nos estamos moviendo al primer lugar!

Primer lugar


Fuente: System in shock: errores interesantes en los c贸digos fuente del legendario System Shock

隆Entonces, el finalista de nuestro top de hoy es un error del legendario System Shock! Este juego, lanzado en 1994, se convirti贸 en el progenitor e inspirador de juegos ic贸nicos como Dead Space, BioShock y Deus Ex.

Pero primero, tengo que admitir algo. Lo que le mostrar茅 ahora no contiene ning煤n error. En general, ni siquiera es un fragmento de c贸digo, 隆pero no pude resistirme a no compartir esto con ustedes!

El hecho es que en el proceso de analizar el c贸digo fuente del juego, mi colega Victoria encontr贸 muchos comentarios interesantes. Aqu铆 y all谩, de repente, hubo bromas y comentarios ir贸nicos, e incluso versos:

 // I'll give you fish, I'll give you candy, // I'll give you, everything I have in my hand // that kid from the wrong side came over my house again, // decapitated all my dolls // and if you bore me, you lose your soul to me // - "Gepetto", Belly, _Star_ // And here, ladies and gentlemen, // is a celebration of C and C++ and their untamed passion... // ================== TerrainData terrain_info; // Now the actual stuff... // ======================= // this is all outrageously horrible, as we dont know what // we really need to deal with here // And if you thought the hack for papers was bad, // wait until you see the one for datas... - X // Returns whether or not in the humble opinion of the // sound system, the sample should be politely obliterated // out of existence // it's a wonderful world, with a lot of strange men // who are standing around, and they all wearing towels 

Para nuestros lectores de habla rusa, hice una traducci贸n gratuita aproximada:
 //    ,    , //    ,       //      //      //     //     ,      // - "Gepetto", Belly, _Star_ //  ,   , //    C  C++     // ================== TerrainData terrain_info; //    ... // ======================= //    ,     , //         //    ,          //      ,      ... - X //       , //         //   ,     //   ,     

Los desarrolladores del juego dejaron estos comentarios a principios de los noventa ... Por cierto, Doug Church, el dise帽ador jefe de System Shock, tambi茅n estaba escribiendo c贸digo. Qui茅n sabe, 驴quiz谩s alguno de estos comentarios fue escrito por 茅l personalmente? Espero que sobre los hombres con toallas, este no sea asunto suyo :)

Conclusi贸n


En conclusi贸n, quiero agradecer a mis colegas por buscar nuevos errores y escribir art铆culos sobre ellos. Gracias chicos! Sin ti, este art铆culo no hubiera resultado tan interesante.

Tambi茅n quiero hablar un poco sobre nuestros logros, porque durante todo un a帽o hemos estado involucrados en algo m谩s que solo buscar errores. Tambi茅n desarrollamos y mejoramos el analizador, como resultado de lo cual sufri贸 cambios significativos.

Por ejemplo, agregamos soporte para varios compiladores nuevos y ampliamos la lista de reglas de diagn贸stico. Tambi茅n proporcionamos soporte inicial para los est谩ndares MISRA C y MISRA C ++ . La innovaci贸n m谩s importante y lenta fue el soporte de un nuevo lenguaje. 隆S铆, ahora podemos analizar el c贸digo Java ! Y hemos actualizado el 铆cono:)

Tambi茅n quiero agradecer a nuestros lectores. 隆Gracias por leer nuestros art铆culos y escribirnos! Sus comentarios son muy agradables e importantes para nosotros.

En esto, nuestros 10 errores principales de C ++ para el a帽o 2018 llegaron a su fin. 驴Qu茅 lugares te gustaron m谩s y por qu茅? 驴Encontraste ejemplos interesantes en 2018? 隆Cu茅ntanoslo en los comentarios!

隆Hasta la pr贸xima!



Si desea compartir este art铆culo con una audiencia de habla inglesa, utilice el enlace a la traducci贸n: George Gribkov. Los 10 errores principales de los proyectos de C ++ encontrados en 2018

Source: https://habr.com/ru/post/444570/


All Articles