Indicado para a pergunta mais estúpida de todas as entrevistas. E há uma competição acirrada.
Konrad Rudolph
119
Você: Cite uma situação em que a diferença entre esses dois possa valer a pena. Entrevistador: Ok, você está contratado.
Chris Lutz
37
A única diferença entre os dois é que, com a convenção posterior, você está seguro contra insetos if(flag = 0)ao preço de um pouco de legibilidade.
Amarghosh
22
@Amarghosh: Ao custo de tornar seu código difícil de ler e não intuitivo. Use o primeiro ao ativar os avisos do compilador, ganha-ganha.
GManNickG
129
Uma vez, um redator do compilador recebeu isso em sua entrevista. Ele sussurrou em resposta, "qual você quer que seja mais rápido?".
Respostas:
236
Ainda não vi nenhuma resposta correta (e já existem algumas) ressalva: Nawaz apontou a armadilha definida pelo usuário . E eu me arrependo de ter votado apressadamente na "pergunta mais estúpida" porque parece que muitos não acertaram e isso dá espaço para uma boa discussão sobre a otimização do compilador :)
A resposta é:
Qual é flago tipo de?
No caso em que flagrealmente é um tipo definido pelo usuário. Então, depende de qual sobrecarga operator==está selecionada. Claro que pode parecer estúpido que eles não sejam simétricos, mas certamente é permitido, e eu já vi outros abusos.
Se flagfor embutido, ambos deverão ter a mesma velocidade.
A partir do artigo da Wikipédia em x86, eu apostaria para uma Jxxinstrução para a ifdeclaração: talvez um JNZ(Salte Se não for Zero) ou algum equivalente.
Eu duvido que o compilador perca uma otimização tão óbvia, mesmo com otimizações desativadas. Este é o tipo de coisa para a qual o Peephole Optimization foi projetado.
EDIT: Sprang up novamente, então vamos adicionar um pouco de montagem (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}
Mesmo que a pessoa não saiba ler o RI, acho que é autoexplicativo.
@Matthieu: você disse que ainda não vi nenhuma resposta correta ... mas a minha está correta, eu acho: P
Nawaz
7
Boa! sua resposta possível transforma "a pergunta mais estúpida" em "os truques / mesquinhos". "vamos cavar um buraco para o candidato e ver se ele cai nele ..." :) Acho que todos nós assumimos automaticamente que flagdeve ser inteiro ou booleano. OTOH, ter uma variável chamada flagde um tipo definido pelo usuário é bastante errado em si mesmo, IMHO
davka
@Nawaz: Posso ter pulado o último parágrafo de sua resposta: p
Matthieu M.
1
@Nawaz: Eu realmente não corro, geralmente leio perguntas muito depois de terem sido respondidas e as pessoas tendem a ler apenas as primeiras respostas mais votadas :) Mas na verdade estou lendo coisas sobre otimizações de compiladores, e isso me pareceu um Caso típico de otimização trivial, pensei em apontá-lo para os leitores que realmente se importam ... Estou muito surpreso de ter recebido tantos votos positivos. Agora é minha resposta mais votada, embora certamente não seja aquela em que eu coloquei mais esforço: / De qualquer forma, editei minha resposta e corrigi minha declaração :)
Matthieu M.
2
@mr_eclair: um tipo embutido é um tipo que é (como o nome indica) embutido na linguagem. Ou seja, está disponível mesmo sem uma única #includediretiva. Para simplificar, que normalmente equivale a int, char, boole similares. Todos os outros tipos são disse a ser definida pelo usuário, que é que eles existem, porque eles são o resultado de algum usuário declarando-os: typedef, enum, struct, class. Por exemplo, std::stringé definido pelo usuário, embora você certamente não o tenha definido :)
Matthieu M.
56
Mesmo código para amd64 com 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 para ir mais longe para provar que a otimização do compilador é a mesma.
k rey
56
Não haverá diferença em suas versões.
Estou assumindo que o typesinalizador de não é um tipo definido pelo usuário, mas sim algum tipo integrado. Enum é exceção! . Você pode tratar enum como se estivesse embutido. Na verdade, seus valores são de um tipo embutido!
No caso, se for do tipo definido pelo usuário (exceto enum), então a resposta depende inteiramente de como você sobrecarregou o operador ==. Observe que você deve sobrecarregar ==definindo duas funções, uma para cada uma de suas versões!
esta poderia ser a única razão possível para fazer esta pergunta, IMHO
davka
15
Eu ficaria extremamente surpreso se os compiladores modernos perdessem essa otimização óbvia.
Pedro d'Aquino
3
No meu conhecimento ! não é uma operação bit a bit
Xavier Combelle
8
@Nawaz: não votou negativamente, mas sua resposta está factualmente errada e é horrível que ainda tenha tantos votos positivos. Para o registro, comparar um número inteiro com 0 é uma única instrução de montagem , completamente parecida com a negação. Na verdade, se o compilador for um pouco estúpido, isso poderia até ser mais rápido do que a negação (embora não seja provável).
Konrad Rudolph
6
@Nawaz: ainda é errado dizer que pode, será ou geralmente será mais rápido. Se houver diferença, então a versão "compare com zero" será mais rápida, já que a negação um se traduz em duas operações: "negar operando; verifique se o resultado é diferente de zero". Na prática, é claro, o compilador o otimiza para produzir o mesmo código que a versão simples "compare com zero", mas a otimização está sendo aplicada à versão de negação, para fazê-la alcançá-la, e não o contrário. Konrad está certo.
jalf
27
Não há absolutamente nenhuma diferença.
Você pode ganhar pontos ao responder a essa pergunta da entrevista, referindo-se à eliminação de erros de atribuição / comparação, embora:
if(flag =0)// typo here{// code never executes}if(0= flag)// typo and syntactic error -> compiler complains{// ...}
Embora seja verdade, que, por exemplo, um compilador C avisa no caso do anterior ( flag = 0), não existem tais avisos em PHP, Perl ou Javascript ou <insert language here>.
@Matthieu Huh. Devo ter perdido o post sobre meta descrevendo o estilo de órtese "adequado".
Linus Kleen
7
Não votei nada, mas vale a pena: por que é tão importante que as pessoas se expliquem sempre que votam? Os votos são anônimos por design. Oponho-me inteiramente à ideia de que os downvoters devam sempre comentar, porque pessoalmente não quero ser considerado o downvoter apenas porque deixei um comentário apontando um problema. Talvez o downvoter tenha pensado que a maioria da resposta era irrelevante para a questão da velocidade? Talvez ele tenha pensado que encorajava um estilo de codificação que ele não aprovava? Talvez ele fosse um idiota e quisesse que sua própria resposta tivesse a pontuação mais alta?
David Hedlund
3
As pessoas devem ser livres para votar como quiserem, independentemente do motivo. Em termos de reputação, isso é quase sempre uma coisa boa, pois muitas vezes provoca outras pessoas a votar positivamente, para contrariar o downvote imerecido, quando na verdade, um único upvote cancelaria cinco downvotes imerecidos.
David Hedlund
26
@David: Os Downvoters devem se explicar porque este site não é sobre votos secretos de popularidade, votação anônima ou algo parecido. Este site é sobre aprendizado. Se alguém disser que uma resposta está incorreta, o downvoter está sendo egoísta com seu conhecimento se não explicar o porquê. Eles estão dispostos a receber todo o crédito quando estão certos, mas não estão dispostos a compartilhar conhecimento quando os outros estão errados.
John Dibling
1
Só para tirar a questão do estilo estimulante do caminho, eu realmente acho que Matthieu considerou isso uma piada. Eu ficaria surpreso em ver que alguém dá seus votos dependendo dessas questões. Dito isso, nem todo mundo usa os votos exatamente da mesma maneira. Eu pude ver a razão para downvoting porque a postagem parece defender um estilo de codificação que o eleitor pode desaprovar (observe a diferença entre defender um estilo de codificação - "se você escrever seu código assim, obterá um erro do compilador quando fizer este erro de digitação "- e simplesmente usando um estilo de codificação, como colchetes) Nesse ...
David Hedlund
16
Não haverá absolutamente nenhuma diferença em termos de velocidade. Por que deveria haver?
se o compilador foi completamente retardado. Essa é a única razão.
JeremyP
@JeremyP: Não consigo imaginar diferença, mesmo que o compilador fosse retardado. O redator do compilador teria que fazer isso de propósito , pelo que posso dizer.
Jon
2
Supondo que o processador tenha uma instrução "teste se 0", x == 0pode ser usado, mas 0 == xpode usar uma comparação normal. Eu disse que teria que ser retardado.
JeremyP
8
Se o sinalizador for um tipo definido pelo usuário com uma sobrecarga assimétrica do operador == ()
OrangeDog
Porque nós pode ter virtual operator==(int)em um tipo definido pelo usuário?
Lorro
12
Bem, há uma diferença quando o sinalizador é um tipo definido pelo usuário
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;}}
No primeiro caso (0 == s), o operador de conversão é chamado e, em seguida, o resultado retornado é comparado a 0. No segundo caso, o operador == é chamado.
o que há de errado com o benchmarking? às vezes, a prática diz mais do que a teoria
Elzo Valugi
1
Essa é a resposta que eu procurava quando comecei a ler este tópico. Parece que a teoria é mais atraente do que a prática, olhando as respostas e votos positivos :)
Samuel Rivas
como ele poderia avaliar na entrevista? Além disso, acho que o entrevistador nem sabe o que significa benchmarking, então ele pode ter ficado ofendido.
IAdapter
A resposta certa para a pergunta (IMO) é "Isso depende muito do compilador e do resto do programa. Eu escreveria um benchmark e testaria em 5 minutos"
Samuel Rivas
7
Eles devem ser exatamente os mesmos em termos de velocidade.
Observe, entretanto, que algumas pessoas costumam colocar a constante à esquerda em comparações de igualdade (os chamados "condicionais de Yoda") para evitar todos os erros que podem surgir se você escrever =(operador de atribuição) em vez de ==(operador de comparação de igualdade); uma vez que a atribuição a um literal aciona um erro de compilação, esse tipo de erro é evitado.
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 outro lado, a maioria das pessoas acha as "condicionais do Yoda" estranhas e irritantes, especialmente porque a classe de erros que elas evitam também pode ser detectada usando avisos do compilador adequados.
if(flag=0)// <--- warning: assignment in conditional expression{}
Obrigado por ecoar. Note, entretanto, que o PHP, por exemplo, não avisará no caso de atribuições em condicionais.
Linus Kleen
5
Como outros já disseram, não há diferença.
0tem que ser avaliado. flagtem que ser avaliado. Esse processo leva o mesmo tempo, não importa de que lado estão colocados.
A resposta certa seria: ambos têm a mesma velocidade.
Mesmo as expressões if(flag==0)e if(0==flag)têm a mesma quantidade de caracteres! Se um deles foi escrito como if(flag== 0), o compilador teria um espaço extra para analisar, então você teria um motivo legítimo para apontar o tempo de compilação.
Mas, como não existe tal coisa, não há absolutamente nenhuma razão para que um seja mais rápido do que o outro. Se houver um motivo, o compilador está fazendo coisas muito, muito estranhas no código gerado ...
Qual é o mais rápido depende de qual versão de == você está usando. Aqui está um snippet que usa 2 implementações possíveis de == e, dependendo se você escolhe chamar x == 0 ou 0 == x, um dos 2 é selecionado.
Se você estiver usando apenas um POD, isso realmente não deve importar quando se trata de velocidade.
#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);}
Bem, estou concordando totalmente com tudo o que foi dito nos comentários ao OP, para fins de exercício:
Se o compilador não for inteligente o suficiente (na verdade, você não deve usá-lo) ou a otimização estiver desabilitada, x == 0pode compilar para uma jump if zeroinstrução de montagem nativa , enquanto 0 == xpoderia ser uma comparação mais genérica (e cara) de valores numéricos.
Ainda assim, não gostaria de trabalhar para um chefe que pensa nestes termos ...
Acho que a melhor resposta é "em que idioma está esse exemplo"?
A pergunta não especifica o idioma e está marcada com 'C' e 'C ++'. Uma resposta precisa precisa de mais informações.
É uma péssima pergunta de programação, mas poderia ser uma boa no tortuoso departamento "vamos dar ao entrevistado corda suficiente para se enforcar ou construir um balanço de árvore". O problema com esse tipo de pergunta é que geralmente são escritas e passadas de entrevistador para entrevistador, até chegar a pessoas que não as entendem de todos os ângulos.
Apenas como um aparte (eu realmente acho que qualquer compilador decente tornará esta questão discutível, uma vez que irá otimizá-la) usando 0 == sinalizador sobre sinalizador == 0 evita o erro de digitação onde você esquece um dos = (ou seja, se você digitar acidentalmente flag = 0 ele irá compilar, mas 0 = flag não), o que eu acho que é um erro que todos cometeram em um ponto ou outro ...
Se houve alguma diferença, o que impede o compilador de escolher o mais rápido uma vez? Então, logicamente, não pode haver nenhuma diferença. Provavelmente é isso que o entrevistador espera. Na verdade, é uma pergunta brilhante.
if(flag = 0)
ao preço de um pouco de legibilidade.Respostas:
Ainda não vi nenhuma resposta correta (e já existem algumas) ressalva: Nawaz apontou a armadilha definida pelo usuário . E eu me arrependo de ter votado apressadamente na "pergunta mais estúpida" porque parece que muitos não acertaram e isso dá espaço para uma boa discussão sobre a otimização do compilador :)
A resposta é:
No caso em que
flag
realmente é um tipo definido pelo usuário. Então, depende de qual sobrecargaoperator==
está selecionada. Claro que pode parecer estúpido que eles não sejam simétricos, mas certamente é permitido, e eu já vi outros abusos.Se
flag
for embutido, ambos deverão ter a mesma velocidade.A partir do artigo da Wikipédia em
x86
, eu apostaria para umaJxx
instrução para aif
declaração: talvez umJNZ
(Salte Se não for Zero) ou algum equivalente.Eu duvido que o compilador perca uma otimização tão óbvia, mesmo com otimizações desativadas. Este é o tipo de coisa para a qual o Peephole Optimization foi projetado.
EDIT: Sprang up novamente, então vamos adicionar um pouco de montagem (LLVM 2.7 IR)
Mesmo que a pessoa não saiba ler o RI, acho que é autoexplicativo.
fonte
flag
deve ser inteiro ou booleano. OTOH, ter uma variável chamadaflag
de um tipo definido pelo usuário é bastante errado em si mesmo, IMHO#include
diretiva. Para simplificar, que normalmente equivale aint
,char
,bool
e similares. Todos os outros tipos são disse a ser definida pelo usuário, que é que eles existem, porque eles são o resultado de algum usuário declarando-os:typedef
,enum
,struct
,class
. Por exemplo,std::string
é definido pelo usuário, embora você certamente não o tenha definido :)Mesmo código para amd64 com GCC 4.1.2:
fonte
Não haverá diferença em suas versões.
Estou assumindo que o
type
sinalizador de não é um tipo definido pelo usuário, mas sim algum tipo integrado. Enum é exceção! . Você pode tratar enum como se estivesse embutido. Na verdade, seus valores são de um tipo embutido!No caso, se for do tipo definido pelo usuário (exceto
enum
), então a resposta depende inteiramente de como você sobrecarregou o operador==
. Observe que você deve sobrecarregar==
definindo duas funções, uma para cada uma de suas versões!fonte
Não há absolutamente nenhuma diferença.
Você pode ganhar pontos ao responder a essa pergunta da entrevista, referindo-se à eliminação de erros de atribuição / comparação, embora:
Embora seja verdade, que, por exemplo, um compilador C avisa no caso do anterior (
flag = 0
), não existem tais avisos em PHP, Perl ou Javascript ou<insert language here>
.fonte
Não haverá absolutamente nenhuma diferença em termos de velocidade. Por que deveria haver?
fonte
x == 0
pode ser usado, mas0 == x
pode usar uma comparação normal. Eu disse que teria que ser retardado.virtual operator==(int)
em um tipo definido pelo usuário?Bem, há uma diferença quando o sinalizador é um tipo definido pelo usuário
No primeiro caso (0 == s), o operador de conversão é chamado e, em seguida, o resultado retornado é comparado a 0. No segundo caso, o operador == é chamado.
fonte
Em caso de dúvida, faça um benchmark e aprenda a verdade.
fonte
Eles devem ser exatamente os mesmos em termos de velocidade.
Observe, entretanto, que algumas pessoas costumam colocar a constante à esquerda em comparações de igualdade (os chamados "condicionais de Yoda") para evitar todos os erros que podem surgir se você escrever
=
(operador de atribuição) em vez de==
(operador de comparação de igualdade); uma vez que a atribuição a um literal aciona um erro de compilação, esse tipo de erro é evitado.Por outro lado, a maioria das pessoas acha as "condicionais do Yoda" estranhas e irritantes, especialmente porque a classe de erros que elas evitam também pode ser detectada usando avisos do compilador adequados.
fonte
Como outros já disseram, não há diferença.
0
tem que ser avaliado.flag
tem que ser avaliado. Esse processo leva o mesmo tempo, não importa de que lado estão colocados.A resposta certa seria: ambos têm a mesma velocidade.
Mesmo as expressões
if(flag==0)
eif(0==flag)
têm a mesma quantidade de caracteres! Se um deles foi escrito comoif(flag== 0)
, o compilador teria um espaço extra para analisar, então você teria um motivo legítimo para apontar o tempo de compilação.Mas, como não existe tal coisa, não há absolutamente nenhuma razão para que um seja mais rápido do que o outro. Se houver um motivo, o compilador está fazendo coisas muito, muito estranhas no código gerado ...
fonte
Qual é o mais rápido depende de qual versão de == você está usando. Aqui está um snippet que usa 2 implementações possíveis de == e, dependendo se você escolhe chamar x == 0 ou 0 == x, um dos 2 é selecionado.
Se você estiver usando apenas um POD, isso realmente não deve importar quando se trata de velocidade.
fonte
Bem, estou concordando totalmente com tudo o que foi dito nos comentários ao OP, para fins de exercício:
Se o compilador não for inteligente o suficiente (na verdade, você não deve usá-lo) ou a otimização estiver desabilitada,
x == 0
pode compilar para umajump if zero
instrução de montagem nativa , enquanto0 == x
poderia ser uma comparação mais genérica (e cara) de valores numéricos.Ainda assim, não gostaria de trabalhar para um chefe que pensa nestes termos ...
fonte
Certamente nenhuma diferença em termos de velocidade de execução. A condição deve ser avaliada em ambos os casos da mesma maneira.
fonte
Acho que a melhor resposta é "em que idioma está esse exemplo"?
A pergunta não especifica o idioma e está marcada com 'C' e 'C ++'. Uma resposta precisa precisa de mais informações.
É uma péssima pergunta de programação, mas poderia ser uma boa no tortuoso departamento "vamos dar ao entrevistado corda suficiente para se enforcar ou construir um balanço de árvore". O problema com esse tipo de pergunta é que geralmente são escritas e passadas de entrevistador para entrevistador, até chegar a pessoas que não as entendem de todos os ângulos.
fonte
Construa dois programas simples usando as maneiras sugeridas.
Reúna os códigos. Olhe a assembléia e você pode julgar, mas duvido que haja diferença!
As entrevistas estão ficando mais baixas do que nunca.
fonte
Apenas como um aparte (eu realmente acho que qualquer compilador decente tornará esta questão discutível, uma vez que irá otimizá-la) usando 0 == sinalizador sobre sinalizador == 0 evita o erro de digitação onde você esquece um dos = (ou seja, se você digitar acidentalmente flag = 0 ele irá compilar, mas 0 = flag não), o que eu acho que é um erro que todos cometeram em um ponto ou outro ...
fonte
Se houve alguma diferença, o que impede o compilador de escolher o mais rápido uma vez? Então, logicamente, não pode haver nenhuma diferença. Provavelmente é isso que o entrevistador espera. Na verdade, é uma pergunta brilhante.
fonte