답변:
행정상 개요 : 아니다.
i++
++i
의 이전 값 때문에 잠재적으로 느릴 수 있습니다.i
나중에 사용하기 위해 저장해야 할 수도 있지만 실제로는 모든 최신 컴파일러가이를 최적화합니다.
우리는이 기능의 코드를 보면이 설명과 함께 모두 수 ++i
와 i++
.
$ cat i++.c
extern void g(int i);
void f()
{
int i;
for (i = 0; i < 100; i++)
g(i);
}
파일은 제외하고, 동일 ++i
및 i++
:
$ diff i++.c ++i.c
6c6
< for (i = 0; i < 100; i++)
---
> for (i = 0; i < 100; ++i)
컴파일하고 생성 된 어셈블러를 얻습니다.
$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c
그리고 생성 된 객체와 어셈블러 파일이 동일하다는 것을 알 수 있습니다.
$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e
$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22
++i
대신 대신 것이 좋습니다 i++
. 하지 말아야 할 이유가 전혀 없으며, 소프트웨어가 툴체인을 통해 최적화하지 않는 소프트웨어를 통과하면 소프트웨어가 더 효율적입니다. 이를 감안하면 그냥 쉽게 입력 할 수 있습니다 ++i
가 입력이기 때문에 i++
, 사용하지에 대한 변명 정말 없다 ++i
처음에이.
에서 의도 대비 효율성 앤드류 코에 닉의 :
첫째, 적어도 정수 변수가 관련된 경우
++i
보다 더 효율적인 것은 분명하지i++
않습니다.
그리고 :
따라서 두 가지 작업 중 어느 것이 더 빠르지 않은지, 두 가지 작업 중 어떤 것이 달성하려는 것을 더 정확하게 표현하는지에 대한 질문이 있습니다. 변수 값을 복사하고 변수를 증가시킨 다음 복사본을 버릴 이유가 없기 때문에 표현식 값을 사용하지 않는 경우
i++
대신 대신 사용할 이유++i
가 없다고 제출합니다.
따라서 결과 값을 사용하지 않으면을 사용합니다 ++i
. 그러나 그것이 더 효율적이기 때문에가 아닙니다 : 그것은 내 의도를 올바르게 진술하기 때문입니다.
i++
같은 방법으로 내가 코드 거라고 i += n
또는 i = i + n
양식에, 즉, 목표 동사 객체 으로, 대상 피연산자하여 왼쪽에 동사 연산자. 의 경우 i++
올바른 객체 가 없지만 동사 연산자 의 왼쪽에 대상 을 유지하면서 규칙이 계속 적용됩니다 .
더 나은 대답은 ++i
때로는 빠르지 만 결코 느리지 않을 것입니다.
모두가 그것을 가정하고있는 것 같습니다 i
가 같은 기본 제공 유형int
. 이 경우 측정 가능한 차이가 없습니다.
그러나 i
복잡한 유형이라면 측정 가능한 차이를 찾을 수 있습니다. 들어 i++
당신을 증가하기 전에 클래스의 사본을해야합니다. 사본에 포함 된 내용에 따라 ++it
최종 값을 반환 할 수 있기 때문에 실제로 느려질 수 있습니다.
Foo Foo::operator++()
{
Foo oldFoo = *this; // copy existing value - could be slow
// yadda yadda, do increment
return oldFoo;
}
또 다른 차이점은 ++i
값 대신 참조를 반환하는 옵션 것입니다. 다시 말하지만, 객체의 사본을 만드는 데 관련된 내용에 따라 속도가 느려질 수 있습니다.
이것이 발생할 수있는 실제 예는 반복자를 사용하는 것입니다. 반복자를 복사하면 응용 프로그램에서 병목 수 없을 수도 있습니다,하지만 여전히 사용하는 습관을 좋은 습관이다 ++i
대신에 i++
결과가 영향을받지된다.
짧은 답변:
속도 i++
와 ++i
는 차이가 없습니다 . 좋은 컴파일러는 두 경우에 다른 코드를 생성해서는 안됩니다.
긴 대답 :
다른 모든 대답에서 언급하지 못한 것은 발견 된 표현 내에서만 ++i
대 차이 i++
가 의미가 있다는 것입니다.
의 경우 for(i=0; i<n; i++)
는은 i++
자신의 표현에 혼자 : 전과 시퀀스 포인트가 i++
그것을 하나씩있다. 따라서 생성 된 유일한 기계 코드는 "증가한 i
로는 1
"이 프로그램의 나머지 부분과 관련하여 염기 서열을 얼마나 잘 정의되어있다. 그래서 당신은 접두사로 변경한다면 ++
, 그것은 문제가 조금, 당신은 여전히 머신 코드 "증가 얻을 것없는 것 i
에 의해를 1
".
사이의 차이 ++i
와 i++
같은 표현 만 문제 array[i++] = x;
대 array[++i] = x;
. 어떤 사람들은 그 위치 i
에있는 레지스터를 나중에 다시로드해야하기 때문에 이러한 작업에서 접미사가 느려질 것이라고 주장하고 말할 수 있습니다. 그러나 C 표준이 호출 할 때 "추상 기계의 동작을 중단하지"않는 한 컴파일러는 원하는 방식으로 명령을 자유롭게 주문할 수 있습니다.
따라서 다음 array[i++] = x;
과 같이 기계 코드로 변환 된다고 가정 할 수 있습니다 .
i
레지스터 A에 값을 저장합니다 .i
// 레지스터 A의 // 비효율적으로 저장하기 때문에 이미 한 번 수행했습니다.i
.컴파일러는 다음과 같이 코드를보다 효율적으로 생성 할 수 있습니다.
i
레지스터 A에 값을 저장합니다 .i
.C 프로그래머로서 postfix ++
가 마지막에 발생 한다고 생각하도록 훈련을 받았다고 해서 기계 코드를 그런 식으로 주문할 필요는 없습니다.
따라서 ++
C 에서는 접두사와 접미사 사이에 차이가 없습니다 . 이제 C 프로그래머는 다양해야합니다. 이유에 관계없이 접두사를 일관성없이 사용하고 어떤 경우에는 접두사를 사용하는 사람들이 있습니다. 이는 C의 작동 방식에 대해 확신이 없거나 언어에 대한 지식이 잘못되었음을 나타냅니다. 이것은 항상 나쁜 징조이며, 결과적으로 그들이 미신이나 "종교적 교리"에 근거하여 프로그램에서 다른 의심스러운 결정을 내린다고 제안합니다.
"접두사 ++
가 항상 빠릅니다"는 C 프로그래머들 사이에서 흔히 볼 수있는 잘못된 교리 중 하나입니다.
Scott Meyers에서 보다 효과적인 c ++ 항목 6 : 접두사와 접미사 형태의 증분 및 감소 연산 구분 .
접두사 버전은 객체, 특히 반복자와 관련하여 항상 접미사보다 우선합니다.
연산자의 호출 패턴을 보면이 이유가 있습니다.
// Prefix
Integer& Integer::operator++()
{
*this += 1;
return *this;
}
// Postfix
const Integer Integer::operator++(int)
{
Integer oldValue = *this;
++(*this);
return oldValue;
}
이 예제를 보면 접두사 연산자가 항상 접미사보다 어떻게 효율적인지 쉽게 알 수 있습니다. 접미사를 사용할 때 임시 개체가 필요하기 때문입니다.
이것이 반복자를 사용하는 예제를 볼 때 항상 접두사 버전을 사용하는 이유입니다.
그러나 int가 지적한 것처럼 컴파일러 최적화로 인해 실제로 차이가 없습니다.
마이크로 최적화가 걱정되는 경우 추가 관찰 사항이 있습니다. 다음과 같은 경우 감소 루프는 증가하는 루프 (명령 세트 아키텍처에 따라 ARM과 같은) 증가 루프보다 '효율적'일 수 있습니다.
for (i = 0; i < 100; i++)
각 루프마다 다음에 대한 명령이 하나씩 있습니다.
1
에 i
. i
보다 작은 지 비교하십시오 100
.i
보다는 더 적은이다 100
.감소 루프 반면 :
for (i = 100; i != 0; i--)
루프에는 다음에 대한 지침이 있습니다.
i
, CPU 레지스터 상태 플래그 설정Z==0
) 에 따른 조건부 분기 입니다.물론 이것은 0으로 감소 할 때만 작동합니다!
ARM 시스템 개발자 안내서에서 기억합니다.
"어느 쪽이 빠를까"라는 질문이 어떤 결정을 내릴 것인지 결정하지 마십시오. 그다지 신경 쓰지 않을 것입니다. 게다가 프로그래머의 읽기 시간이 기계 시간보다 훨씬 비쌉니다.
사람이 코드를 읽는 데 가장 적합한 것을 사용하십시오.
우선 : C의 차이점 i++
과 ++i
C에서는 무시할 수 있습니다.
세부 사항에.
++i
더 빠름C ++에서는 ++i
오버플 i
로 증분 연산자가있는 일종의 객체입니다.
왜?
에서는 ++i
, 물체를 먼저 증가시키고,이어서 임의의 다른 함수에 CONST 기준으로 통과 할 수있다. 표현식 이 호출 foo(i++)
되기 전에 증분을 수행해야 foo()
하지만 이전 값을 전달해야하기 때문에 표현식이 가능한 경우에는 불가능합니다.foo()
. 결과적으로 컴파일러는 i
원본에서 증가 연산자를 실행하기 전에 사본을 작성 해야합니다. 추가적인 생성자 / 소멸자 호출은 나쁜 부분입니다.
위에서 언급했듯이 이것은 기본 유형에는 적용되지 않습니다.
i++
may 더 빠를 있습니다생성자 / 소멸자 C의 경우는 항상, 어떤 호출 할 필요가 없을 경우 ++i
와i++
오른쪽 똑같이 빠른해야 하는가? 아닙니다. 거의 똑같이 빠르지 만 작은 차이가있을 수 있습니다. 대부분의 다른 응답자들은 잘못된 길을 가고 있습니다.
어떻게 i++
더 빠를 수 있습니까?
요점은 데이터 의존성입니다. 메모리에서 값을로드해야하는 경우 두 개의 후속 조작을 수행하여이를 증가시키고 사용해야합니다. 을 사용 하면 값을 사용 하기 전에++i
증분을 수행 해야 합니다. 를 사용하면 i++
증분에 의존하지 않고 CPU는 증분 작업 과 병행 하여 사용 작업 을 수행 할 수 있습니다 . 차이는 최대 하나의 CPU주기이므로 실제로 무시할 수는 있지만 실제로는 있습니다. 그리고 그것은 많은 사람들이 기대하는 다른 방법입니다.
++i
또는 i++
다른 식에서 또는를 사용하는 경우 식 사이를 변경하면 식의 의미가 변경되므로 가능한 성능 향상 / 손실은 의심의 여지가 없습니다. 그것들이 독립형이라면, 즉 연산의 결과가 즉시 사용되지 않는다면, 괜찮은 컴파일러는 그것을 동일한 것으로, 예를 들어 INC
어셈블리 명령으로 컴파일 할 것 입니다.
i++
및 ++i
그들이 프로그래머가 무엇에 가까운 당량 그래서, 하나의 루프 상수를 조정함으로써 거의 모든 가능한 상황에서 상호 교환 적으로 사용될 수있다. 2) 둘 다 동일한 명령어로 컴파일하더라도 CPU마다 실행이 다릅니다. 의 경우 i++
CPU는 동일한 값을 사용하는 다른 명령어 (CPU는 실제로이 작업을 수행함)와 병렬 로 증분 을 계산할 수 있지만 ++i
, CPU는 증분 후 다른 명령어를 예약해야합니다 .
if(++foo == 7) bar();
과 if(foo++ == 6) bar();
기능적으로 동일하다. 그러나 비교와 증분은 CPU에 의해 병렬로 계산 될 수 있기 때문에 두 번째는 한 사이클 더 빠를 수 있습니다. 이 단일 사이클이 중요하지는 않지만 차이점이 있습니다.
<
예 : vs <=
) ++
가 많이 나타나므로 종종 사이의 변환이 쉽게 가능합니다.
@Mark 컴파일러가 변수의 (스택 기반) 임시 복사본을 최적화하고 gcc (최근 버전)가 그렇게한다고하더라도 모든 컴파일러가 항상 그렇게하는 것은 아닙니다.
방금 현재 프로젝트에서 사용하는 컴파일러로 테스트했으며 4 개 중 3 개가 최적화하지 않습니다.
더 빠르지 만 느리지 않은 코드가 읽기 쉬운 경우 컴파일러가 올바르게 처리한다고 가정하지 마십시오.
코드에서 연산자 중 하나를 실제로 어리석게 구현하지 않은 경우 :
Alwas는 i ++보다 ++ i를 선호합니다.
postfix가 prefix 증가보다 느린 상황을 생각할 수 있습니다.
레지스터 A
가 있는 프로세서가 누산기로 사용되며 많은 명령어에서 사용되는 유일한 레지스터라고 상상해보십시오 (일부 소형 마이크로 컨트롤러는 실제로 이와 유사 함).
이제 다음 프로그램과 그 가상의 번역으로의 번역을 상상해보십시오.
접두사 증가 :
a = ++b + c;
; increment b
LD A, [&b]
INC A
ST A, [&b]
; add with c
ADD A, [&c]
; store in a
ST A, [&a]
접미사 증가 :
a = b++ + c;
; load b
LD A, [&b]
; add with c
ADD A, [&c]
; store in a
ST A, [&a]
; increment b
LD A, [&b]
INC A
ST A, [&b]
의 값을 b
다시로드 하는 방법에 유의하십시오 . 접두사 증분을 사용하면 컴파일러는 값을 증분하고 계속 사용할 수 있습니다. 증분 후 원하는 값이 이미 레지스터에 있으므로 재로드를 피할 수 있습니다. 그러나 postfix 증가로 컴파일러는 두 가지 값을 처리해야합니다. 하나는 이전 값이고 다른 하나는 증가 된 값입니다. 위에 표시된 것처럼 하나 이상의 메모리 액세스가 발생합니다.
물론 단일 i++;
명령문 과 같이 증분 값을 사용하지 않으면 컴파일러는 접미사 또는 접두사 사용법에 관계없이 증분 명령을 생성 할 수 있습니다.
부수적으로, 나는 노력 b++
이 ++b
없다면 (예를 들어을 추가하여 - 1
) 단순히 표현으로 변환 할 수 없다는 언급을하고 싶습니다 . 따라서 두 표현이 표현의 일부인 경우 비교하는 것은 실제로 유효하지 않습니다. 종종 b++
표현식 안에서 사용 하는 곳에서는을 사용할 수 없으므로 잠재적으로 더 효율적 ++b
이더라도 ++b
잘못되었을 수 있습니다. 물론식이 구걸하는 경우는 예외입니다 (예 : a = b++ + 1;
로 변경 가능 a = ++b;
).
여기 답변의 대부분 의견의 대부분을 읽고 있고, 나는 모든 참조를 참조하지 않은 한 내가 어디 생각할 수 있다는 예를 i++
보다 효율적이다 ++i
(그리고 놀랍게도 --i
이었다 보다 더 효율적 i--
). DEC PDP-11의 C 컴파일러를위한 것입니다!
PDP-11에는 레지스터의 사전 감소 및 사후 증가에 대한 조립 지침이 있었지만 다른 방법은 없습니다. 이 명령을 통해 "일반용"레지스터를 스택 포인터로 사용할 수 있습니다. 따라서 비슷한 *(i++)
것을 사용 하면 단일 어셈블리 명령으로 컴파일 할 수는 있지만 그렇게 할 수 *(++i)
는 없었습니다.
(또는 내가 말을해야 이것은 분명히 매우 비의 예이지만, 사후 증가가 더 효율적입니다 예외를 제공한다 이었다 요즘 PDP-11 C 코드에 대한 많은 수요가 아니기 때문에,).
--i
와 i++
.
나는 항상 사전 증가를 선호합니다 ...
나는 operator ++ 함수를 호출하는 경우에도 함수가 인라인되면 컴파일러가 임시를 최적화 할 수 있다고 지적하고 싶었다. operator ++는 일반적으로 짧고 종종 헤더에서 구현되므로 인라인 될 수 있습니다.
따라서 실제적인 목적으로 두 형식의 성능에는 큰 차이가 없습니다. 그러나, 나는 항상 사전 증분을 선호합니다. 왜냐하면 내가 말하고자하는 것을 직접적으로 표현하는 것보다 직접 표현하는 것이 낫기 때문입니다.
또한 옵티마이 저의 가능성을 줄이면 컴파일러가 더 빨리 실행됩니다.
내 C는 조금 녹슬 었으므로 미리 사과드립니다. 결과적으로 결과를 이해할 수 있습니다. 그러나 두 파일이 동일한 MD5 해시에 어떻게 나오는지 혼란 스럽습니다. 아마도 for 루프가 동일하게 실행되지만 다음 두 줄의 코드가 다른 어셈블리를 생성하지 않습니까?
myArray[i++] = "hello";
vs
myArray[++i] = "hello";
첫 번째 값은 배열에 값을 쓴 다음 i를 증가시킵니다. 그런 다음 두 번째 증분 i는 배열에 씁니다. 나는 어셈블리 전문가가 아니지만이 두 가지 코드 줄에서 동일한 실행 파일이 어떻게 생성되는지 알지 못합니다.
내 두 센트.
foo[i++]
에 foo[++i]
분명히 프로그램의 의미를 변경할 것 아무 것도 변경하지 않고 있지만, 일부 프로세서 루프 게양 최적화 로직없이 컴파일러를 사용하는 경우, 증가 p
및 q
예를 들어이 수행하는 루프를 실행 한 후 한 번 *(p++)=*(q++);
더 빨리하는 수행 루프를 사용하는 것보다 것을 *(++pp)=*(++q);
. 일부 프로세서에서 매우 엄격한 루프의 경우 속도 차이가 클 수 있지만 (10 % 이상) 사후 증분이 사전 증분보다 훨씬 빠른 C의 경우 일 수 있습니다.