Nominado para la pregunta de entrevista más estúpida de la historia. Y hay una dura competencia.
Konrad Rudolph
119
Usted: Nombra una situación en la que valga la pena preocuparse por la diferencia entre estos dos. Entrevistador: Está bien, está contratado.
Chris Lutz
37
La única diferencia entre los dos es que con la convención posterior, estás asegurado contra errores if(flag = 0)al precio de un poco de legibilidad.
Amarghosh
22
@Amarghosh: A costa de hacer que su código sea difícil de leer y poco intuitivo. Utilice el primero en activar las advertencias del compilador, ganar-ganar.
GManNickG
129
Una vez, un compilador obtuvo esto en su entrevista. Susurró en respuesta, "¿con cuál se quiere ser más rápido?".
Respuestas:
236
Todavía no he visto ninguna respuesta correcta (y ya hay algunas) advertencia: Nawaz señaló la trampa definida por el usuario . Y lamento mi voto a favor apresurado sobre la "pregunta más estúpida" porque parece que muchos no lo hicieron bien y da lugar a una buena discusión sobre la optimización del compilador :)
La respuesta es:
¿Qué es flagel tipo?
En el caso de que en flagrealidad sea un tipo definido por el usuario. Entonces depende de qué sobrecarga deoperator== se seleccione. Por supuesto que puede parecer estúpido que no sean simétricos, pero ciertamente está permitido, y ya he visto otros abusos.
Si flag es incorporado, ambos deben tener la misma velocidad.
Del artículo de Wikipedia sobre x86, apostaría por una Jxxinstrucción para la ifdeclaración: tal vez un JNZ(Jump if Not Zero) o algo equivalente.
Dudo que el compilador pierda una optimización tan obvia, incluso con las optimizaciones desactivadas. Este es el tipo de cosas para las que está diseñada la optimización de mirilla .
EDITAR: Surgió de nuevo, así que agreguemos un poco de ensamblaje (LLVM 2.7 IR)
int regular(int c){if(c ==0){return0;}return1;}int yoda(int c){if(0== c){return0;}return1;}
define i32 @regular(i32 %c) nounwind readnone {
entry:%not.= icmp ne i32 %c,0;<i1>[#uses=1]%.0= zext i1 %not. to i32 ;<i32>[#uses=1]
ret i32 %.0}
define i32 @yoda(i32 %c) nounwind readnone {
entry:%not.= icmp ne i32 %c,0;<i1>[#uses=1]%.0= zext i1 %not. to i32 ;<i32>[#uses=1]
ret i32 %.0}
Incluso si uno no sabe cómo leer el IR, creo que se explica por sí mismo.
@Matthieu: dijiste que no había visto ninguna respuesta correcta todavía ... pero la mía es correcta, creo: P
Nawaz
7
¡bueno! su posible respuesta convierte "la pregunta más estúpida" en "la más engañosa". "Cavemos un hoyo para el candidato y veamos si cae en él ..." :) Supongo que todos asumimos automáticamente que flagdebe ser un número entero o booleano. OTOH, tener una variable nombrada flagde un tipo definido por el usuario es bastante incorrecto en sí mismo, en mi humilde opinión
davka
@Nawaz: Es posible que me haya saltado el último párrafo de su respuesta: p
Matthieu M.
1
@Nawaz: Realmente no corro, por lo general leo preguntas mucho después de que han sido respondidas y la gente tiende a leer solo las primeras respuestas más votadas :) Pero en realidad estoy leyendo cosas sobre optimizaciones del compilador, y esto me pareció un caso típico de optimización trivial, así que pensé en señalarlo para aquellos lectores que realmente se molestan ... Estoy bastante sorprendido de haber obtenido tantos votos a favor. Ahora es mi respuesta más votada, aunque ciertamente no es en la que me esforcé más: / De todos modos, edité mi respuesta y corrigí mi declaración :)
Matthieu M.
2
@mr_eclair: un tipo incorporado es un tipo que está (como el nombre implica) incorporado en el lenguaje. Es decir, está disponible incluso sin una sola #includedirectiva. Por razones de simplicidad, por lo general asciende a int, char, booly similares. Todos los demás tipos se dice que son definidos por el usuario, es decir que existen, ya que son el resultado de algún usuario declarándolos: typedef, enum, struct, class. Por ejemplo, std::stringestá definido por el usuario, aunque ciertamente no lo definió usted mismo :)
Matthieu M.
56
Mismo código para amd64 con GCC 4.1.2:
.loc 140# int f = argc;
movl -20(%rbp),%eax
movl %eax,-4(%rbp).loc 160# if( f == 0 ) {
cmpl $0,-4(%rbp)
jne .L2
.loc 170# return 0;
movl $0,-36(%rbp)
jmp .L4
.loc 180# }.L2:.loc 1100# if( 0 == f ) {
cmpl $0,-4(%rbp)
jne .L5
.loc 1110# return 1;
movl $1,-36(%rbp)
jmp .L4
.loc 1120# }.L5:.loc 1140# return 2;
movl $2,-36(%rbp).L4:
movl -36(%rbp),%eax
.loc 1150# }
leave
ret
+1 por hacer un esfuerzo adicional para demostrar que la optimización del compilador es la misma.
k rey
56
No habrá diferencia en sus versiones.
Supongo que la typebandera of no es un tipo definido por el usuario, sino que es un tipo integrado. ¡Enum es una excepción!. Puede tratar enum como si estuviera integrado. De hecho, ¡los valores de it son uno de los tipos integrados!
En caso de que sea un tipo definido por el usuario (excepto enum), entonces la respuesta depende completamente de cómo haya sobrecargado al operador ==. Tenga en cuenta que debe sobrecargar ==al definir dos funciones, ¡una para cada una de sus versiones!
esta podría ser la única razón posible para hacer esta pregunta, en mi humilde opinión
davka
15
Me sorprendería muchísimo si los compiladores modernos pasaran por alto una optimización tan obvia.
Pedro d'Aquino
3
Que yo sepa ! no es una operación bit a bit
Xavier Combelle
8
@Nawaz: no votó en contra, pero su respuesta es objetivamente incorrecta y es horrible que todavía tenga tantos votos a favor. Para el registro, comparar un número entero con 0 es una sola instrucción de ensamblaje , completamente a la par con la negación. De hecho, si el compilador es un poco estúpido, esto podría ser incluso más rápido que la negación (aunque no es probable).
Konrad Rudolph
6
@Nawaz: sigue siendo un error decir que puede, será o normalmente será más rápido. Si hay una diferencia, entonces la versión "comparar contra cero" será más rápida, ya que la negación uno realmente se traduce en dos operaciones: "negar operando; verificar que el resultado sea distinto de cero". En la práctica, por supuesto, el compilador lo optimiza para producir el mismo código que la versión simple "comparar con cero", pero la optimización se aplica a la versión de negación, para que se ponga al día, y no al revés. Konrad tiene razón.
jalf
27
No hay absolutamente ninguna diferencia.
Sin embargo, puede ganar puntos al responder la pregunta de la entrevista al referirse a la eliminación de errores tipográficos de asignación / comparación:
if(flag =0)// typo here{// code never executes}if(0= flag)// typo and syntactic error -> compiler complains{// ...}
Si bien es cierto que, por ejemplo, un compilador C advierte en el caso del primero ( flag = 0), no existen tales advertencias en PHP, Perl o Javascript o <insert language here>.
@ Matthieu Eh. Debo haber perdido la publicación sobre meta que describe el estilo de refuerzo "adecuado".
Linus Kleen
7
No he votado en absoluto, pero por lo que vale: ¿por qué es tan importante que las personas se expliquen cada vez que emiten un voto? Los votos son anónimos por diseño. Me opongo por completo a la idea de que los votantes negativos siempre deben comentar, porque personalmente no quiero que se suponga que soy el votante negativo solo porque dejé un comentario señalando un problema. ¿Quizás el votante en contra pensó que la mayoría de la respuesta era irrelevante para la pregunta de velocidad? ¿Quizás pensó que fomentaba un estilo de codificación que no aprobaba? ¿Quizás era un idiota y quería que su propia respuesta tuviera la calificación más alta?
David Hedlund
3
La gente debería tener la libertad de votar como quiera, independientemente del motivo. En cuanto a la reputación, esto es casi siempre algo bueno, ya que a menudo provoca que otras personas voten a favor, a contrarrestar el voto en contra inmerecido, cuando en realidad, un solo voto a favor anularía cinco votos en contra inmerecidos.
David Hedlund
26
@David: Los votantes en contra deben explicarse porque este sitio no trata sobre votaciones secretas de popularidad, votaciones anónimas o cosas por el estilo. Este sitio trata sobre el aprendizaje. Si alguien dice que una respuesta es incorrecta al rechazarla, el votante negativo está siendo egoísta con su conocimiento si no explica por qué. Están dispuestos a atribuirse todo el mérito cuando tienen razón, pero no están dispuestos a compartir conocimientos cuando otros están equivocados.
John Dibling
1
Solo para eliminar el problema del estilo de refuerzo, realmente creo que Matthieu pretendía que fuera una broma. Me sorprendería ver que alguien vota en función de estos temas. Dicho esto, no todo el mundo utiliza los votos exactamente de la misma manera. Pude ver la justificación para votar negativamente porque la publicación parece defender un estilo de codificación que el votante podría desaprobar (tenga en cuenta la diferencia entre defender un estilo de codificación: "si escribe su código de esta manera, obtendrá un error de compilación cuando haga este error tipográfico "- y simplemente usando un estilo de codificación, como llaves) En eso ...
David Hedlund
16
No habrá absolutamente ninguna diferencia en cuanto a velocidad. ¿Por qué debería haberlo?
si el compilador estaba completamente retrasado. Ésa es la única razón.
JeremyP
@JeremyP: No puedo imaginar una diferencia incluso si el compilador estuviera retrasado. El escritor del compilador tendría que hacerlo a propósito, por lo que yo sé.
Jon
2
Suponiendo que el procesador tiene una instrucción "prueba si 0", x == 0podría usarla pero 0 == xpodría usar una comparación normal. Dije que tendría que retrasarse.
JeremyP
8
Si la bandera es un tipo definido por el usuario con una sobrecarga asimétrica del operador == ()
OrangeDog
¿Porque podríamos tener virtual operator==(int)un tipo definido por el usuario?
lorro
12
Bueno, hay una diferencia cuando la bandera es un tipo definido por el usuario
struct sInt
{
sInt(int i ): wrappedInt(i){
std::cout <<"ctor called"<< std::endl;}operatorint(){
std::cout <<"operator int()"<< std::endl;return wrappedInt;}booloperator==(int nComp){
std::cout <<"bool operator==(int nComp)"<< std::endl;return(nComp == wrappedInt);}int wrappedInt;};int
_tmain(int argc, _TCHAR* argv[]){
sInt s(0);//in this case this will probably be fasterif(0== s ){
std::cout <<"equal"<< std::endl;}if( s ==0){
std::cout <<"equal"<< std::endl;}}
En el primer caso (0 == s) se llama al operador de conversión y luego el resultado devuelto se compara con 0. En el segundo caso se llama al operador ==.
¿Qué tiene de malo la evaluación comparativa? a veces la práctica te dice más que la teoría
Elzo Valugi
1
Esa es la respuesta que estaba buscando cuando comencé a leer este hilo. Parece que la teoría es más atractiva que la práctica, buscando las respuestas y los votos positivos :)
Samuel Rivas
¿Cómo podría compararlo en la entrevista? Además, creo que el entrevistador ni siquiera sabe lo que significa la evaluación comparativa, por lo que podría haberse ofendido.
IAdapter
La respuesta correcta a la pregunta (IMO) es "Eso depende en gran medida del compilador y del resto del programa. Escribiría un punto de referencia y lo probaría en 5 minutos"
Samuel Rivas
7
Deberían ser exactamente iguales en términos de velocidad.
Sin embargo, observe que algunas personas suelen poner la constante a la izquierda en comparaciones de igualdad (los llamados "condicionales de Yoda") para evitar todos los errores que pueden surgir si escribe =(operador de asignación) en lugar de ==(operador de comparación de igualdad); dado que la asignación a un literal desencadena un error de compilación, se evita este tipo de error.
if(flag=0)// <--- typo: = instead of ==; flag is now set to 0{// this is never executed}if(0=flag)// <--- compiler error, cannot assign value to literal{}
Por otro lado, la mayoría de la gente encuentra los "condicionales de Yoda" raros y molestos, especialmente porque la clase de errores que previenen se pueden detectar también mediante el uso de advertencias adecuadas del compilador.
if(flag=0)// <--- warning: assignment in conditional expression{}
Gracias por hacer eco. Sin embargo, tenga en cuenta que PHP, por ejemplo, no advertirá en caso de asignaciones en condicionales.
Linus Kleen
5
Como han dicho otros, no hay diferencia.
0 tiene que ser evaluado. flagtiene que ser evaluado. Este proceso lleva el mismo tiempo, sin importar de qué lado se coloquen.
La respuesta correcta sería: ambos tienen la misma velocidad.
¡Incluso las expresiones if(flag==0)y if(0==flag)tienen la misma cantidad de caracteres! Si uno de ellos estuviera escrito comoif(flag== 0) , entonces el compilador tendría un espacio adicional para analizar, por lo que tendría una razón legítima para señalar el tiempo de compilación.
Pero como no existe tal cosa, no hay absolutamente ninguna razón por la que uno deba ser más rápido que otro. Si hay una razón, entonces el compilador está haciendo cosas muy, muy extrañas con el código generado ...
Cuál es rápido depende de la versión de == que estés usando. Aquí hay un fragmento que usa 2 posibles implementaciones de ==, y dependiendo de si elige llamar a x == 0 o 0 == x, se selecciona una de las 2.
Si solo está usando un POD, esto realmente no debería importar cuando se trata de velocidad.
#include<iostream>usingnamespace std;class x {public:booloperator==(int x){ cout <<"hello\n";return0;}friendbooloperator==(int x,const x& a){ cout <<"world\n";return0;}};int main(){
x x1;//int m = 0;int k =(x1 ==0);int j =(0== x1);}
Bueno, estoy completamente de acuerdo con todo lo dicho en los comentarios al OP, por el bien del ejercicio:
Si el compilador no es lo suficientemente inteligente (de hecho, no debería usarlo) o la optimización está deshabilitada, x == 0podría compilar en una jump if zeroinstrucción de ensamblaje nativa , mientras0 == x podría ser una comparación más genérica (y costosa) de valores numéricos.
Aún así, no me gustaría trabajar para un jefe que piensa en estos términos ...
Creo que la mejor respuesta es "¿en qué idioma está este ejemplo"?
La pregunta no especificó el idioma y está etiquetada tanto con 'C' como con 'C ++'. Una respuesta precisa necesita más información.
Es una pregunta de programación pésima, pero podría ser buena en el tortuoso departamento de "démosle al entrevistado suficiente cuerda para que se cuelgue o construya un columpio". El problema con ese tipo de preguntas es que generalmente se escriben y pasan de un entrevistador a otro hasta que llegan a personas que realmente no las entienden desde todos los ángulos.
Solo como un aparte (en realidad creo que cualquier compilador decente hará que esta pregunta sea discutible, ya que la optimizará) usando 0 == bandera sobre bandera == 0 evita el error tipográfico en el que se olvida uno de los = (es decir, si accidentalmente escribe flag = 0 se compilará, pero 0 = flag no lo hará), lo cual creo que es un error que todos han cometido en un momento u otro ...
Si hubo alguna diferencia, ¿qué impide que el compilador elija el más rápido una vez? Entonces, lógicamente, no puede haber ninguna diferencia. Probablemente esto es lo que espera el entrevistador. De hecho, es una pregunta brillante.
if(flag = 0)
al precio de un poco de legibilidad.Respuestas:
Todavía no he visto ninguna respuesta correcta (y ya hay algunas) advertencia: Nawaz señaló la trampa definida por el usuario . Y lamento mi voto a favor apresurado sobre la "pregunta más estúpida" porque parece que muchos no lo hicieron bien y da lugar a una buena discusión sobre la optimización del compilador :)
La respuesta es:
En el caso de que en
flag
realidad sea un tipo definido por el usuario. Entonces depende de qué sobrecarga deoperator==
se seleccione. Por supuesto que puede parecer estúpido que no sean simétricos, pero ciertamente está permitido, y ya he visto otros abusos.Si
flag
es incorporado, ambos deben tener la misma velocidad.Del artículo de Wikipedia sobre
x86
, apostaría por unaJxx
instrucción para laif
declaración: tal vez unJNZ
(Jump if Not Zero) o algo equivalente.Dudo que el compilador pierda una optimización tan obvia, incluso con las optimizaciones desactivadas. Este es el tipo de cosas para las que está diseñada la optimización de mirilla .
EDITAR: Surgió de nuevo, así que agreguemos un poco de ensamblaje (LLVM 2.7 IR)
Incluso si uno no sabe cómo leer el IR, creo que se explica por sí mismo.
fuente
flag
debe ser un número entero o booleano. OTOH, tener una variable nombradaflag
de un tipo definido por el usuario es bastante incorrecto en sí mismo, en mi humilde opinión#include
directiva. Por razones de simplicidad, por lo general asciende aint
,char
,bool
y similares. Todos los demás tipos se dice que son definidos por el usuario, es decir que existen, ya que son el resultado de algún usuario declarándolos:typedef
,enum
,struct
,class
. Por ejemplo,std::string
está definido por el usuario, aunque ciertamente no lo definió usted mismo :)Mismo código para amd64 con GCC 4.1.2:
fuente
No habrá diferencia en sus versiones.
Supongo que la
type
bandera of no es un tipo definido por el usuario, sino que es un tipo integrado. ¡Enum es una excepción!. Puede tratar enum como si estuviera integrado. De hecho, ¡los valores de it son uno de los tipos integrados!En caso de que sea un tipo definido por el usuario (excepto
enum
), entonces la respuesta depende completamente de cómo haya sobrecargado al operador==
. Tenga en cuenta que debe sobrecargar==
al definir dos funciones, ¡una para cada una de sus versiones!fuente
No hay absolutamente ninguna diferencia.
Sin embargo, puede ganar puntos al responder la pregunta de la entrevista al referirse a la eliminación de errores tipográficos de asignación / comparación:
Si bien es cierto que, por ejemplo, un compilador C advierte en el caso del primero (
flag = 0
), no existen tales advertencias en PHP, Perl o Javascript o<insert language here>
.fuente
No habrá absolutamente ninguna diferencia en cuanto a velocidad. ¿Por qué debería haberlo?
fuente
x == 0
podría usarla pero0 == x
podría usar una comparación normal. Dije que tendría que retrasarse.virtual operator==(int)
un tipo definido por el usuario?Bueno, hay una diferencia cuando la bandera es un tipo definido por el usuario
En el primer caso (0 == s) se llama al operador de conversión y luego el resultado devuelto se compara con 0. En el segundo caso se llama al operador ==.
fuente
En caso de duda, evalúelo y aprenda la verdad.
fuente
Deberían ser exactamente iguales en términos de velocidad.
Sin embargo, observe que algunas personas suelen poner la constante a la izquierda en comparaciones de igualdad (los llamados "condicionales de Yoda") para evitar todos los errores que pueden surgir si escribe
=
(operador de asignación) en lugar de==
(operador de comparación de igualdad); dado que la asignación a un literal desencadena un error de compilación, se evita este tipo de error.Por otro lado, la mayoría de la gente encuentra los "condicionales de Yoda" raros y molestos, especialmente porque la clase de errores que previenen se pueden detectar también mediante el uso de advertencias adecuadas del compilador.
fuente
Como han dicho otros, no hay diferencia.
0
tiene que ser evaluado.flag
tiene que ser evaluado. Este proceso lleva el mismo tiempo, sin importar de qué lado se coloquen.La respuesta correcta sería: ambos tienen la misma velocidad.
¡Incluso las expresiones
if(flag==0)
yif(0==flag)
tienen la misma cantidad de caracteres! Si uno de ellos estuviera escrito comoif(flag== 0)
, entonces el compilador tendría un espacio adicional para analizar, por lo que tendría una razón legítima para señalar el tiempo de compilación.Pero como no existe tal cosa, no hay absolutamente ninguna razón por la que uno deba ser más rápido que otro. Si hay una razón, entonces el compilador está haciendo cosas muy, muy extrañas con el código generado ...
fuente
Cuál es rápido depende de la versión de == que estés usando. Aquí hay un fragmento que usa 2 posibles implementaciones de ==, y dependiendo de si elige llamar a x == 0 o 0 == x, se selecciona una de las 2.
Si solo está usando un POD, esto realmente no debería importar cuando se trata de velocidad.
fuente
Bueno, estoy completamente de acuerdo con todo lo dicho en los comentarios al OP, por el bien del ejercicio:
Si el compilador no es lo suficientemente inteligente (de hecho, no debería usarlo) o la optimización está deshabilitada,
x == 0
podría compilar en unajump if zero
instrucción de ensamblaje nativa , mientras0 == x
podría ser una comparación más genérica (y costosa) de valores numéricos.Aún así, no me gustaría trabajar para un jefe que piensa en estos términos ...
fuente
Seguramente no hay diferencia en cuanto a velocidades de ejecución. La condición debe evaluarse en ambos casos de la misma manera.
fuente
Creo que la mejor respuesta es "¿en qué idioma está este ejemplo"?
La pregunta no especificó el idioma y está etiquetada tanto con 'C' como con 'C ++'. Una respuesta precisa necesita más información.
Es una pregunta de programación pésima, pero podría ser buena en el tortuoso departamento de "démosle al entrevistado suficiente cuerda para que se cuelgue o construya un columpio". El problema con ese tipo de preguntas es que generalmente se escriben y pasan de un entrevistador a otro hasta que llegan a personas que realmente no las entienden desde todos los ángulos.
fuente
Cree dos programas simples utilizando las formas sugeridas.
Reúna los códigos. Mira la asamblea y podrás juzgar, ¡pero dudo que haya diferencia!
Las entrevistas son cada vez más bajas.
fuente
Solo como un aparte (en realidad creo que cualquier compilador decente hará que esta pregunta sea discutible, ya que la optimizará) usando 0 == bandera sobre bandera == 0 evita el error tipográfico en el que se olvida uno de los = (es decir, si accidentalmente escribe flag = 0 se compilará, pero 0 = flag no lo hará), lo cual creo que es un error que todos han cometido en un momento u otro ...
fuente
Si hubo alguna diferencia, ¿qué impide que el compilador elija el más rápido una vez? Entonces, lógicamente, no puede haber ninguna diferencia. Probablemente esto es lo que espera el entrevistador. De hecho, es una pregunta brillante.
fuente