tutorial gnucash español c++ c compiler-construction programming-languages

c++ - gnucash tutorial español



Uso inadvertido de=en lugar de== (30)

Parece que

if (x=y) { .... }

en lugar de

if (x==y) { ... }

es una raíz de muchos males.

¿Por qué no todos los compiladores lo marcan como un error en lugar de una advertencia configurable?

Estoy interesado en descubrir casos en los que el constructo if (x=y) es útil.


¿Es esto realmente un error tan común? Aprendí sobre eso cuando aprendí C, y como profesor, he advertido ocasionalmente a mis alumnos y les he dicho que es un error común, pero rara vez lo he visto en código real, incluso de principiantes. Ciertamente no más a menudo que otros errores del operador, como por ejemplo escribir "&&" en lugar de "||".

Entonces, la razón por la que los compiladores no lo marcan como un error (excepto que es un código perfectamente válido) es tal vez que no es la raíz de muchos males.


Colocar la constante en el lado izquierdo de una comparación es la programación defensiva. Seguro que nunca cometerás el error tonto de olvidar ese ''='' extra, pero quién sabe sobre el otro.


Depende del idioma. Java lo señala como un error, ya que solo se pueden usar expresiones booleanas dentro del paréntesis si (a menos que las dos variables sean booleanas, en cuyo caso la asignación también es booleana).

En C, es un modismo bastante común para los indicadores de prueba devueltos por malloc o si después de un tenedor estamos en el proceso principal o secundario:

if ( x = (X*) malloc( sizeof(X) ) { // malloc worked, pointer != 0 if ( pid = fork() ) { // parent process as pid != 0

Los compiladores C / C ++ advertirán con un nivel de advertencia lo suficientemente alto si lo solicita, pero no se puede considerar un error ya que el lenguaje lo permite. A menos que, una vez más, solicite al compilador que trate las advertencias como errores.

Siempre que se comparan con las constantes, algunos autores sugieren usar la constante de prueba == para que el compilador detecte si el usuario olvida el segundo signo de igualdad.

if ( 0 == variable ) { // the compiler will complaint if you mistakenly // write =, as you cannot assign to a constant

De todos modos, debe intentar compilar con la configuración de advertencia más alta posible.


El compilador no lo marcará como un error porque es válido C / C ++. Pero lo que puede hacer (al menos con Visual C ++) es subir el nivel de advertencia para que lo señale como una advertencia y luego decirle al compilador que trate las advertencias como errores. Esta es una buena práctica de todos modos para que los desarrolladores no ignoren las advertencias.

Si realmente hubiera querido decir = en lugar de ==, entonces debe ser más explícito al respecto. p.ej

if ((x = y) != 0)

Teóricamente, se supone que debes poder hacer esto:

if ((x = y))

para anular la advertencia, pero eso no parece funcionar siempre.


El lenguaje de programación D indica esto como un error. Para evitar el problema de querer utilizar el valor más tarde, permite declaraciones como C ++, lo que permite con bucles for .

if(int i = some_fn()) { another_fn(i); }


En la práctica, no lo hago, pero un buen consejo es hacer:

if (true == $ x)

En el caso de que omita un igual, asignar $ x a verdadero obviamente devolverá un error.


Es muy común con las construcciones de bucle de "bajo nivel" en C / C ++, como con las copias:

void my_strcpy(char *dst, const char *src) { while((*dst++ = *src++) != ''/0'') { // Note the use of extra parentheses, and the explicit compare. /* DO NOTHING */ } }

Por supuesto, las asignaciones son muy comunes con los bucles for:

int i; for(i = 0; i < 42; ++i) { printf("%d/n", i); }

Creo que es más fácil leer asignaciones cuando están fuera de las declaraciones if :

char *newstring = malloc(strlen(src) * sizeof(char)); if(newstring == NULL) { fprintf(stderr, "Out of memory, d00d! Bailing!/n"); exit(2); } // Versus: if((newstring = malloc(strlen(src) * sizeof(char))) == NULL) // ew...

Asegúrate de que la tarea sea obvia, por así decirlo (como en los primeros dos ejemplos). No lo ocultes

En cuanto a los usos accidentales ... eso no me sucede demasiado. Una salvaguarda común es poner su variable (lvalues) en el lado derecho de la comparación, pero eso no funciona bien con cosas como:

if(*src == *dst)

¡porque ambos oprands a == son lvalues!

En cuanto a los compiladores ... ¿quién puede culparlos? Escribir compiladores es difícil, y de todos modos deberías escribir programas perfectos para el compilador (¿recuerdas GIGO?). Algunos compiladores (los más conocidos con certeza) proporcionan una función de comprobación del estilo de la lint , pero eso ciertamente no es necesario. Algunos navegadores no validan cada byte de HTML y Javascript que se lanza, entonces, ¿por qué los compiladores?


Es por eso que es mejor escribir:

0 == CurrentItem

En lugar de:

CurrentItem == 0

para que el compilador lo advierta si escribe = en lugar de ==.


Hay muchos usos geniales del operador de asignación en una declaración condicional, y sería un verdadero dolor en el culo ver advertencias sobre cada uno todo el tiempo. Lo que sería bueno sería una función en su IDE que le permite resaltar todos los lugares donde se ha utilizado la asignación en lugar de una verificación de igualdad, o después de escribir algo como esto:

if (x = y) {

entonces esa línea parpadea un par de veces. Lo suficiente como para hacerle saber que ha hecho algo no exactamente estándar, pero no tanto que sea molesto.


Hay varias tácticas para ayudar a detectar esto ... una es fea, la otra es típicamente una macro. Realmente depende de cómo lees tu lenguaje hablado (de izquierda a derecha, de derecha a izquierda).

Por ejemplo:

if ((fp = fopen("foo.txt", "r") == NULL))

Vs:

if (NULL == (fp = fopen(...)))

A veces puede ser más fácil leer / escribir (primero) para qué prueba, lo que hace que sea más fácil detectar una tarea que una prueba. Luego traiga a la mayoría de las personas de comp.lang.c que odian este estilo con pasión.

Entonces, traemos assert ():

#include <assert.h> ... fp = fopen("foo.txt", "r"); assert(fp != NULL); ...

cuando estás en el medio, o al final de un conjunto intrincado de condicionales, assert () es tu amigo. En este caso, si FP == NULL, se genera un abort () y se transmite la línea / archivo del código ofensivo.

Entonces si oops:

if (i = foo)

insinuado de

if (i == foo)

seguido por

assert (i > foo + 1)

... rápidamente detectará esos errores.

Espero que esto ayude :)

En resumen, invertir los argumentos a veces ayuda cuando se depura ... assert () es su amigo de toda la vida y se puede desactivar en los indicadores del compilador en las versiones de producción.


Intenta ver

if( life_is_good() ) enjoy_yourself();

como

if( tmp = life_is_good() ) enjoy_yourself();


La asignación como condicional es legal C y C ++, y cualquier compilador que no lo permita no es un compilador C o C ++ real. Espero que cualquier lenguaje moderno no diseñado para ser explícitamente compatible con C (como lo fue C ++) lo considere un error.

Hay casos en que esto permite expresiones concisas, como el while (*dest++ = *src++); idiomático while (*dest++ = *src++); copiar una cadena en C, pero en general no es muy útil, y lo considero un error en el diseño del lenguaje. Es, en mi experiencia, fácil cometer este error, y es difícil de detectar cuando el compilador no emite una advertencia.


La expresión ''if (0 = x)'' es inútil porque no ayuda cuando ambos lados son variables (''if (x = y)'') y la mayoría (¿todos?) Del tiempo en que debe usar variables constantes en lugar de números mágicos

Otras dos razones por las que nunca uso esta expresión idiomática, en mi humilde opinión, hace que el código sea menos legible y, para ser honesto, el único ''='' es la raíz de muy poco mal. Si prueba su código a fondo (lo que todos hacemos, obviamente), este tipo de error de sintaxis aparece muy rápido.


La mayoría de las veces, los compiladores intentan arduamente permanecer compatibles con versiones anteriores.

Cambiar su comportamiento en este asunto para lanzar errores romperá el código legítimo existente, e incluso comenzar a emitir advertencias sobre él causará problemas con los sistemas automáticos que hacen un seguimiento del código compilándolo automáticamente y buscando errores y advertencias.

Esto es un mal, estamos bastante atrapados en el cajero automático, pero hay formas de eludir y reducir los peligros que conlleva.

Ejemplo:

void *ptr = calloc(1, sizeof(array)); if (NULL = ptr) { // some error }

Esto causa un error de compilación.


Lenguaje estándar C para iterar:

list_elem* curr; while ( (curr = next_item(list)) != null ) { /* ... */ }


Muchos compiladores detectarán esto y le avisarán, pero solo si el nivel de advertencia es lo suficientemente alto.

Por ejemplo:

~> gcc -c -Wall foo.c foo.c: In function ‘foo’: foo.c:5: warning: suggest parentheses around assignment used as truth value


Parte de esto tiene que ver con el estilo personal y los hábitos. Soy agnóstico a leer si (kConst == x) o si (x == kConst). No uso la constante de la izquierda porque históricamente no creo ese error y escribo el código como lo diría o me gustaría leerlo. Veo esto como una decisión personal como parte de una responsabilidad personal de ser un ingeniero consciente y que mejora. Por ejemplo, comencé a analizar los tipos de errores que estaba creando y comencé a rediseñar mis hábitos para no hacerlos, similar a la constante de la izquierda, solo con otras cosas.

Dicho esto, las advertencias del compilador, históricamente, son bastante malas y, aunque este problema se conoce desde hace años, no lo vi en un compilador de producción hasta finales de los 80. También descubrí que trabajar en proyectos que eran portátiles ayudaba a limpiar mucho mi C, como diferentes compiladores y diferentes gustos (es decir, advertencias) y diferentes diferencias semánticas sutiles.


Personalmente, considero que este es el ejemplo más útil.

Supongamos que tiene una función de read() que devuelve el número de bytes leídos, y necesita usar esto en un bucle. Es mucho más simple de usar

while((count = read(foo)) > 0) { //Do stuff }

que tratar de obtener la asignación de la cabeza del bucle, lo que daría lugar a cosas como

while(1) { count = read(foo); if(!(count > 0)) break; //... }

o

count = read(foo); while(count > 0) { //... count = read(foo); }

La primera construcción se siente incómoda, y la segunda repite el código de una manera desagradable.

A menos que, por supuesto, me haya perdido un idioma genial para esto ...


Porque no es ilegal (en C o C ++ de todos modos) y algunas veces es útil ...

if ( (x = read(blah)) > 0) { // now you know how many bits/bytes/whatever were read // and can use that info. Esp. if you know, say 30 bytes // are coming but only got 10 }

La mayoría de los compiladores levantan un hedor real si no pones paréntesis alrededor de la tarea de todos modos, lo cual me gusta.


Preguntó por qué era útil, pero sigue cuestionando los ejemplos que las personas brindan. Es útil porque es conciso.

Sí, todos los ejemplos que lo usan se pueden volver a escribir, como fragmentos de código más largos.


Respuesta simple: una operación de asignación, como x = y, tiene un valor, que es el mismo que el valor recientemente asignado en x. Puede usar esto directamente en una comparación, así que en lugar de

x = y; if (x) ...

puedes escribir

if (x = y) ...

Es menos código para escribir (y leer), lo que a veces es bueno, pero hoy en día la mayoría de las personas está de acuerdo en que debería escribirse de alguna otra forma para aumentar la legibilidad. Por ejemplo, así:

if ((x = y) != 0) ...

Aquí hay un ejemplo realista. Supongamos que quiere asignar algo de memoria con malloc, y ver si funcionó. Se puede escribir paso a paso como este:

p = malloc(4711); if (p != NULL) printf("Ok!");

La comparación con NULL es redundante, por lo que puede volver a escribirlo así:

p = malloc(4711); if (p) printf("Ok!");

Pero dado que la operación de asignación tiene un valor que se puede usar, puede poner toda la tarea en la condición if:

if (p = malloc(4711)) printf("Ok!");

Esto hace lo mismo, pero es más conciso.


Solo he tenido este error tipográfico UNA VEZ en mis 15 años de desarrollo. No diría que está en la parte superior de mi lista de cosas a tener en cuenta. También evito esa construcción de todos modos.

Tenga en cuenta también que algunos compiladores (el que uso) emite una advertencia sobre ese código. Las advertencias pueden tratarse como errores para cualquier compilador que merezca la pena. Ellos también pueden ser ignorados.


puedes usar

if (0=x)

en su lugar, lo que provocará un error de compilación.


una construcción útil es, por ejemplo:

char *pBuffer; if (pBuffer = malloc(100)) { //continue to work here }

editar:
como se mencionó anteriormente, y downvoted varias veces ahora, podría agregar que este no es un estilo especialmente bueno, pero visto con suficiente frecuencia como para decir que es útil. También he visto esto con new , pero me causa más dolor en el pecho.

otro ejemplo, menos controvertido, podría ser:

while (pointer = getNextElement(context)) { //go for it, use the pointer to the new segment of data }

lo que implica que la función getNextElement() devuelve NULL cuando no hay un elemento siguiente para que salga el ciclo.


Como se ha señalado en otras respuestas, hay casos en los que se hacen asignaciones dentro de una condición ofrece una pieza breve, pero legible de código que hace lo que quiere. Además, muchos de los compiladores en marcha hasta la fecha le advertirá si ven una misión donde esperan una condición. (Si eres un fan del enfoque de cero advertencias para el desarrollo, se le han visto estos.)

Un hábito que he desarrollado que me impide ser mordido por esta (al menos en lenguajes C-ish) es que si uno de los dos valores que estoy comparando es una constante (o de lo contrario no es un valor-legal), lo puse en el lado izquierdo del comparador: if (5 == x) { whatever(); }Entonces, si debería escribir accidentalmente if (5 = x), el código no se compilará.


Considere aumentar su compilador con herramientas de análisis estático que detectan dicho código "mal", por ejemplo pelusa .


Creo que los diseñadores de lenguaje C y C ++ notaron que no hay un uso real para prohibirlo porque

  • Los compiladores pueden advertir sobre esto si quieren de todos modos
  • Si no lo acepta, agregará casos especiales al idioma y eliminará una posible función.

No hay complejidad involucrada en permitirlo. C ++ solo dice que se requiere una expresión implícitamente convertible a bool . En C, hay casos útiles detallados por otras respuestas. En C ++, dan un paso más y permiten este además:

if(type * t = get_pointer()) { // .... }

Lo que en realidad limita el alcance de t a solo el if y sus cuerpos.


if ((k==1) || (k==2)) is a conditional if ((k=1) || (k=2) ) is BOTH a conditional AND an assignment statement

  • Aquí está la explicación *

Al igual que la mayoría de los lenguajes, C funciona de adentro hacia afuera en orden por precedencia del operador.

Primero intenta establecer k a 1, y tiene éxito.

Result: k = 1 and Boolean = ''true''

Siguiente: establece k a 2, y tiene éxito.

Result: k = 2 and Boolean = ''true''

Siguiente: evalúa (verdadero || verdadero)

Result: k still = 2, and Boolean = true

Finalmente, luego resuelve el condicional: If (true)

Result: k = 2 and the program takes the first branch.

En casi 30 años de programación, no he visto una razón válida para usar esta construcción, aunque si existe probablemente tenga que ver con la necesidad de ofuscar deliberadamente su código.

Cuando una de nuestras nuevas personas tiene un problema, esta es una de las cosas que busco, junto con no colocar un terminador en una cadena, y copiar una declaración de depuración de un lugar a otro y no cambiar el ''% i a'' % s ''para que coincida con el nuevo campo que están volcando.

Esto es bastante común en nuestra tienda porque cambiamos constantemente entre C y Oracle PL / SQL; if (k = 1) ES la sintaxis correcta en PL / SQL.



Acerca de los usos válidos de if (i = 0)

El problema es que estás tomando el problema al revés. La notación "si" no se trata de comparar dos valores como en algunos otros idiomas.

La instrucción "si" de C / C ++ espera cualquier expresión que se evalúe como un valor booleano o nulo / no nulo. Esta expresión puede incluir dos valores de comparación, y / o puede ser mucho más compleja.

Por ejemplo, puede tener:

if(i >> 3) { std::cout << "i is less than 8" << std::endl }

Lo que demuestra que, en C / C ++, la expresión if no está limitada a == y =. Cualquier cosa servirá, siempre que se pueda evaluar como verdadera o falsa (C ++), o cero distinta de cero (C / C ++).

Otro uso válido de C ++:

if(MyObject * pObject = dynamic_cast<MyInterface *>(pInterface)) { pObject->doSomething() ; }

Y estos son usos simples de la expresión if (tenga en cuenta que esto también se puede usar en la línea de declaración for loop). Usos más complejos existen.

Acerca de los usos avanzados de if (i = 0) en C ++ [Citado de mí]

Después de descubrir un duplicado de esta pregunta en ¿ En qué caso si (a = b) es una buena idea? , Decidí completar esta respuesta con una bonificación adicional, es decir, inyección variable en un ámbito, que es posible en C ++ porque if se evaluará su expresión, incluida una declaración de variable, en lugar de limitarse a comparar dos operandos como se hace en otros idiomas:

Entonces, citando de mí mismo :

Otro uso sería usar lo que se llama Inyección de Variables C ++. En Java, existe esta genial palabra clave:

synchronized(p) { // Now, the Java code is synchronized using p as a mutex }

En C ++, puedes hacerlo también. No tengo el código exacto en mente (ni el artículo exacto de DDJ donde lo descubrí), pero esta simple definición debería ser suficiente para fines de demostración:

#define synchronized(lock) / if (auto_lock lock_##__LINE__(lock)) synchronized(p) { // Now, the C++ code is synchronized using p as a mutex }

De la misma manera, mezclando inyección con if y para declaración, puede declarar una macro foreach primitiva (si desea un foreach de fuerza industrial, use boost''s).

Vea los siguientes artículos para una implementación menos ingenua, más completa y más robusta:

¿Cuántos errores de este tipo realmente suceden?

Raramente. De hecho, todavía tengo que recordar uno, y soy profesional desde hace 8 años. Supongo que sucedió, pero luego, en 8 años, produje una cantidad considerable de errores. Es solo que este tipo de errores no sucedieron lo suficiente como para que los recuerde por frustración.

En C, tendrás más errores debido a los desbordamientos del búfer, como:

void doSomething(char * p) { strcpy(p, "Hello World, how are you /?/n") ; } void doSomethingElse() { char buffer[16] ; doSomething(buffer) ; }

De hecho, Microsoft se quemó tan duro debido a que agregaron una advertencia en Visual C ++ 2008 desaprobando strcpy !!!

¿Cómo se puede evitar la mayoría de los errores?

La primera "protección" contra este error es "dar la vuelta" a la expresión: como no puede asignar un valor a una constante, esto:

if(0 = p) // ERROR : It should have been if(0 == p). WON''T COMPILE !

No compilará

Pero encuentro que esta es una solución bastante pobre, porque trata de esconderse detrás del estilo, lo que debería ser una práctica de programación general, es decir: cualquier variable que no se supone que debe cambiar debe ser constante.

Por ejemplo, en lugar de:

void doSomething(char * p) { if(p == NULL) // POSSIBLE TYPO ERROR return ; size_t length = strlen(p) ; if(length == 0) // POSSIBLE TYPO ERROR printf("/"%s/" length is %i/n", p, length) ; else printf("the string is empty/n") ; }

Intentar "const" tantas variables como sea posible te evitará la mayoría de los errores tipográficos, incluidos los que no están dentro de las expresiones "if":

void doSomething(const char * const p) // CONST ADDED HERE { if(p == NULL) // NO TYPO POSSIBLE return ; const size_t length = strlen(p) ; // CONST ADDED HERE if(length == 0) // NO TYPO POSSIBLE printf("/"%s/" length is %i/n", p, length) ; else printf("the string is empty/n") ; }

Por supuesto, no siempre es posible (ya que algunas variables deben cambiar), pero descubrí que la mayoría de las variables que uso son constantes (sigo inicializándolas una vez, y luego, solo las leo).

Conclusión

Usualmente, veo código usando la notación if (0 == p), pero sin la notación const.

Para mí, es como tener un bote de basura para reciclables y otro para no reciclable, y luego, al final, arrojarlos en el mismo contenedor.

Por lo tanto, no loro un hábito de estilo fácil esperando que mejore mucho su código. No lo hará. Use las construcciones del lenguaje tanto como sea posible, lo que significa, en este caso, usar la notación if (0 == p) cuando esté disponible, y el uso de la palabra clave const tanto como sea posible.