인터뷰 질문 : 한 빨리 실행됩니다 if (flag==0)
또는 if (0==flag)
? 왜?
if(flag = 0)
약간의 가독성을 제공하는 대가로 버그에 대해 보험을들 수 있다는 것입니다.
인터뷰 질문 : 한 빨리 실행됩니다 if (flag==0)
또는 if (0==flag)
? 왜?
if(flag = 0)
약간의 가독성을 제공하는 대가로 버그에 대해 보험을들 수 있다는 것입니다.
답변:
나는 아직 정답을 보지 못했습니다 (그리고 이미 일부가 있습니다) 경고 : Nawaz는 사용자 정의 트랩을 지적했습니다 . 그리고 "가장 어리석은 질문"에 대해 서둘러 찬성 투표를 한 것을 후회합니다. 많은 사람들이 제대로 이해하지 못한 것 같고 컴파일러 최적화에 대한 좋은 토론의 여지를 제공합니다. :)
정답은:
flag
유형 은 무엇입니까 ?
경우 여기서 flag
실제로 사용자 정의 형식이다. 그런 다음 어떤 과부하 operator==
가 선택 되었는지에 따라 다릅니다 . 물론 그것들이 대칭 적이 지 않다는 것은 어리석은 것처럼 보일 수 있지만, 그것은 확실히 허용되며, 이미 다른 학대를 보았습니다.
flag
가 내장 된 경우 둘 다 동일한 속도를 가져야합니다.
에 대한 Wikipedia 기사 에서 x86
나는 진술 에 대한 Jxx
지침을 if
기대할 것 JNZ
입니다.
최적화가 꺼져 있어도 컴파일러가 그러한 명백한 최적화를 놓치는 것은 의심 스럽습니다. 이것은 Peephole Optimization 이 설계된 유형입니다 .
편집 : 다시 시작되었으므로 어셈블리를 추가해 보겠습니다 (LLVM 2.7 IR).
int regular(int c) {
if (c == 0) { return 0; }
return 1;
}
int yoda(int c) {
if (0 == c) { return 0; }
return 1;
}
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
}
IR을 읽는 방법을 모르더라도 자명하다고 생각합니다.
flag
정수 또는 부울이어야 한다고 가정한다고 생각한다 . OTOH,라는 변수를 갖는 flag
사용자 정의 유형은, 이럴 자체에 매우 잘못된 것입니다
#include
지시어 없이도 사용할 수 있습니다 . 단순화하기 위해, 보통 금액 int
, char
, bool
등이있다. 다른 모든 유형이 그들을 선언 유저의 결과이기 때문에 그들이 존재 즉, 사용자 정의로 말한다 : typedef
, enum
, struct
, class
. 예를 들어, std::string
사용자가 정의한 것입니다. 확실히 직접 정의하지는 않았지만 :)
GCC 4.1.2를 사용하는 amd64에 대한 동일한 코드 :
.loc 1 4 0 # int f = argc;
movl -20(%rbp), %eax
movl %eax, -4(%rbp)
.loc 1 6 0 # if( f == 0 ) {
cmpl $0, -4(%rbp)
jne .L2
.loc 1 7 0 # return 0;
movl $0, -36(%rbp)
jmp .L4
.loc 1 8 0 # }
.L2:
.loc 1 10 0 # if( 0 == f ) {
cmpl $0, -4(%rbp)
jne .L5
.loc 1 11 0 # return 1;
movl $1, -36(%rbp)
jmp .L4
.loc 1 12 0 # }
.L5:
.loc 1 14 0 # return 2;
movl $2, -36(%rbp)
.L4:
movl -36(%rbp), %eax
.loc 1 15 0 # }
leave
ret
버전에는 차이가 없습니다.
type
of 플래그가 사용자 정의 유형이 아니라 일부 내장 유형 이라고 가정하고 있습니다. Enum은 예외입니다! . enum을 내장 된 것처럼 취급 할 수 있습니다. 사실, 그것의 값은 내장형 중 하나입니다!
사용자 정의 유형 (제외 enum
) 인 경우 대답은 전적으로 연산자를 오버로드 한 방법에 따라 달라집니다 ==
. ==
각 버전에 대해 하나씩 두 개의 함수를 정의 하여 오버로드 해야합니다!
전혀 차이가 없습니다.
하지만 다음과 같이 할당 / 비교 오타 제거를 참조하여 인터뷰 질문에 답할 때 점수를 얻을 수 있습니다.
if (flag = 0) // typo here
{
// code never executes
}
if (0 = flag) // typo and syntactic error -> compiler complains
{
// ...
}
예를 들어 C 컴파일러가 전자 ( flag = 0
)의 경우 경고하는 것은 사실이지만 PHP, Perl 또는 Javascript 또는 <insert language here>
.
속도면에서 전혀 차이가 없습니다. 왜 있어야합니까?
x == 0
이를 사용할 수 있지만 0 == x
일반 비교를 사용할 수 있습니다. 나는 그것이 지연되어야한다고 말했었다.
virtual operator==(int)
사용자 정의 유형에?
플래그가 사용자 정의 유형일 때 차이가 있습니다.
struct sInt
{
sInt( int i ) : wrappedInt(i)
{
std::cout << "ctor called" << std::endl;
}
operator int()
{
std::cout << "operator int()" << std::endl;
return wrappedInt;
}
bool operator==(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 faster
if ( 0 == s )
{
std::cout << "equal" << std::endl;
}
if ( s == 0 )
{
std::cout << "equal" << std::endl;
}
}
첫 번째 경우 (0 == s)에서 변환 연산자가 호출되고 반환 된 결과가 0과 비교됩니다. 두 번째 경우에는 == 연산자가 호출됩니다.
의심 스러울 때 그것을 벤치마킹하고 진실을 배우십시오.
속도면에서 정확히 동일해야합니다.
그러나 어떤 사람들은 (등등 비교 =
연산자) 대신 (할당 연산자) 를 쓰면 발생할 수있는 모든 오류를 피하기 위해 등식 비교 (소위 "Yoda conditionals")에서 상수를 왼쪽에 두는 데 사용합니다 ==
. 리터럴에 할당하면 컴파일 오류가 발생하므로 이러한 종류의 실수를 방지 할 수 있습니다.
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
{
}
반면에, 대부분의 사람들은 "Yoda conditional"이 이상하고 성가 시다고 생각합니다. 특히 적절한 컴파일러 경고를 사용하여 예방하는 오류 클래스를 발견 할 수 있기 때문입니다.
if(flag=0) // <--- warning: assignment in conditional expression
{
}
다른 사람들이 말했듯이 차이가 없습니다.
0
평가해야합니다. flag
평가해야합니다. 이 프로세스는 어느쪽에 배치 되든 동일한 시간이 걸립니다.
정답은 둘 다 같은 속도입니다.
심지어 표현 if(flag==0)
과 if(0==flag)
문자 같은 양의있다! 그들 중 하나가로 작성 되었다면 if(flag== 0)
컴파일러는 파싱 할 추가 공간을 하나 갖게되므로 컴파일 시간을 지적 할 합법적 인 이유가있을 것입니다.
그러나 그런 것이 없기 때문에 어떤 사람이 다른 사람보다 더 빨라야 할 이유가 전혀 없습니다. 이유가 있다면 컴파일러는 생성 된 코드에 매우 이상한 일을하고 있습니다.
어느 것이 빠른지 사용하는 == 버전에 따라 다릅니다. 다음은 ==의 두 가지 가능한 구현을 사용하는 스 니펫이며 x == 0 또는 0 == x 호출 여부에 따라 2 개 중 하나가 선택됩니다.
POD를 사용하는 경우 속도와 관련하여 정말 중요하지 않습니다.
#include <iostream>
using namespace std;
class x {
public:
bool operator==(int x) { cout << "hello\n"; return 0; }
friend bool operator==(int x, const x& a) { cout << "world\n"; return 0; }
};
int main()
{
x x1;
//int m = 0;
int k = (x1 == 0);
int j = (0 == x1);
}