C의 삼항 (조건부) 연산자
조건부 연산자의 필요성은 무엇입니까? if-else 구조를 구현하기 때문에 기능적으로 중복됩니다. 조건부 연산자가 동등한 if-else 할당보다 더 효율적인 경우 컴파일러에서 if-else를 더 효율적으로 해석 할 수없는 이유는 무엇입니까?
삼항 연산자는 성능 지름길이 아니라 구문 및 가독성의 편의입니다. 사람들은 다양한 복잡성의 조건부로 인해 그 장점에 대해 분할되지만 짧은 조건의 경우 한 줄로 표현하는 것이 유용 할 수 있습니다.
또한 Charlie Martin이 썼 듯이 표현이기 때문에 C로 된 문장의 오른쪽에 나타날 수 있습니다. 이것은 간결하기 때문에 가치가 있습니다.
C에서 그것의 진정한 유용성은 그것이 진술이 아니라 표현식이라는 것입니다. 즉, 명령문의 오른쪽 (RHS)에 표시 할 수 있습니다. 따라서 특정 내용을 더 간결하게 작성할 수 있습니다.
주어진 다른 답변 중 일부는 훌륭합니다. 그러나 누구도 const
간결한 방식으로 정확성을 강화하는 데 사용할 수 있다고 언급하지 않은 것에 놀랐습니다 .
이 같은:
const int n = (x != 0) ? 10 : 20;
따라서 기본적으로 n
는 const
초기 값이 조건문에 따라 달라집니다. 가장 쉬운 대안은를 만들지 n
않는 것입니다 const
. 이렇게하면 일반 사용자 if
가 초기화 할 수 있습니다. 그러나 당신이 그것을 원한다면 const
평범한 것으로 할 수 없습니다 if
. 가장 좋은 대안은 다음과 같은 도우미 함수를 사용하는 것입니다.
int f(int x) {
if(x != 0) { return 10; } else { return 20; }
}
const int n = f(x);
그러나 버전이 훨씬 더 작고 틀림없이 더 읽기 쉬운 경우 삼항.
다음과 같이 코드 난독 화에 중요합니다.
Look-> See?!
No
:(
Oh, well
);
간결함과 if-then-else 구문을 식에 인라인하는 기능.
C에는 다른 측면에서 어느 정도 쉽게 구현할 수 있기 때문에 기술적으로 필요하지 않은 많은 것들이 있습니다. 다음은 불완전한 목록입니다.
- 동안
- ...에 대한
- 기능
- 구조체
이것없이 코드가 어떻게 보일지 상상해보십시오. 그러면 답을 찾을 수 있습니다. 삼항 연산자는 "구문 적 설탕"의 한 형태로,주의 깊게 사용하면 코드를 쉽게 작성하고 이해할 수 있습니다.
때로는 삼항 연산자가 작업을 수행하는 가장 좋은 방법입니다. 특히 삼항의 결과가 l- 값이되기를 원할 때.
이것은 좋은 예는 아니지만, 나는 더 나은 것에 대해 빈칸을 그리고있다. 한 가지는 certian입니다. 나는 여전히 꽤 많이 사용하지만 실제로 삼항을 사용해야 할 때가 아닙니다.
const char* appTitle = amDebugging ? "DEBUG App 1.0" : "App v 1.0";
내가 경고 할 한 가지는 삼진을 함께 묶는 것입니다. 유지
보수시 실제 문제가됩니다.
int myVal = aIsTrue ? aVal : bIsTrue ? bVal : cIsTrue ? cVal : dVal;
편집 : 잠재적으로 더 나은 예가 있습니다. 삼항 연산자를 사용하여 처리 할 함수를 작성해야하는 경우 참조 및 const 값을 할당 할 수 있습니다.
int getMyValue()
{
if( myCondition )
return 42;
else
return 314;
}
const int myValue = getMyValue();
... 될 수 있습니다 :
const int myValue = myCondition ? 42 : 314;
어느 쪽이 더 낫다는 것은 내가 논쟁하지 않기로 선택할 논쟁의 여지가있는 질문입니다.
아직 아무도 이것을 언급하지 않았으므로 현명한 printf
명령문 을 얻는 유일한 방법 은 삼항 연산자를 사용하는 것입니다.
printf("%d item%s", count, count > 1 ? "s\n" : "\n");
주의 사항 : C에서 C ++로 이동할 때 연산자 우선 순위에 약간의 차이가 있으며 그로 인해 발생하는 미묘한 버그에 놀랄 수 있습니다.
삼항 연산자는 명령문이 아니라 표현식이므로 표현식의 일부로 사용되는 함수와 유사한 매크로에 대한 매크로 확장에 사용할 수 있습니다. Const는 원래 C의 일부가 아닐 수도 있지만 매크로 전처리 기는 이전으로 돌아갑니다.
내가 사용한 곳 중 하나는 바운드 검사 배열 액세스에 매크로를 사용하는 배열 패키지에 있습니다. 확인 된 참조의 구문은 다음과 같았습니다 aref(arrayname, type, index)
. 여기서 arrayname은 실제로 배열 경계와 데이터에 대한 부호없는 char 배열을 포함하는 구조체에 대한 포인터이고, 유형은 데이터의 실제 유형이고, 인덱스는 인덱스입니다. 이것의 확장은 꽤나 털이 많았지 만 (그리고 저는 메모리에서 그것을하지 않을 것입니다), 바운드 검사를하기 위해 삼항 연산자를 사용했습니다.
반환 된 객체의 다형성이 필요하기 때문에 C에서 함수 호출로이를 수행 할 수 없습니다. 따라서 표현식에서 유형 캐스팅을 수행하려면 매크로가 필요했습니다. C ++에서는이를 템플릿 오버로드 된 함수 호출 (아마 operator []의 경우)으로 수행 할 수 있지만 C에는 이러한 기능이 없습니다.
편집 : Berkeley CAD 어레이 패키지 (glu 1.4 에디션)에서 제가 이야기 한 예입니다. array_fetch 사용법에 대한 문서는 다음과 같습니다.
type
array_fetch(type, array, position)
typeof type;
array_t *array;
int position;
배열에서 요소를 가져옵니다. 배열 경계 외부를 참조하려고하면 런타임 오류가 발생합니다. 주어진 위치의 값이 실제로 배열을 역 참조 할 때 사용 된 유형인지 확인하는 유형 검사는 없습니다.
여기에 array_fetch의 매크로 정의가 있습니다 (단일 표현식의 일부로 올바른 값을 가진 모든 하위 표현식을 올바른 순서로 실행하기 위해 삼항 연산자와 쉼표 시퀀싱 연산자를 사용합니다).
#define array_fetch(type, a, i) \
(array_global_index = (i), \
(array_global_index >= (a)->num) ? array_abort((a),1) : 0,\
*((type *) ((a)->space + array_global_index * (a)->obj_size)))
array_insert의 확장 (필요한 경우 C ++ 벡터처럼 배열 확장)은 중첩 된 여러 삼항 연산자를 포함하여 훨씬 더 복잡합니다.
구문 적 설탕이며 하나의 명령문 만 포함하는 간단한 if / else 블록에 대한 편리한 속기입니다. 기능적으로 두 구성은 동일하게 수행되어야합니다.
삼항 연산자는 일반 if else 절보다 성능이 더 좋을 수 있습니다. 이것은 임베디드 애플리케이션에서 중요 할 수 있지만 컴파일러 최적화도이 차이를 축소 할 수 있습니다.
dwn이 말했듯이, 성능은 복잡한 프로세서의 부상 중 이점 중 하나였습니다. MSDN 블로그 비고 전적인 프로세서 동작 : 작업을 수행하지 않는 것보다 빠를 수있는 방법 은 삼항 (조건부) 연산자와 if / else 문.
다음 코드를 제공하십시오.
#include <windows.h>
#include <stdlib.h>
#include <stdlib.h>
#include <stdio.h>
int array[10000];
int countthem(int boundary)
{
int count = 0;
for (int i = 0; i < 10000; i++) {
if (array[i] < boundary) count++;
}
return count;
}
int __cdecl wmain(int, wchar_t **)
{
for (int i = 0; i < 10000; i++) array[i] = rand() % 10;
for (int boundary = 0; boundary <= 10; boundary++) {
LARGE_INTEGER liStart, liEnd;
QueryPerformanceCounter(&liStart);
int count = 0;
for (int iterations = 0; iterations < 100; iterations++) {
count += countthem(boundary);
}
QueryPerformanceCounter(&liEnd);
printf("count=%7d, time = %I64d\n",
count, liEnd.QuadPart - liStart.QuadPart);
}
return 0;
}
다른 경계에 대한 비용은 훨씬 다르고 이상합니다 (원래 자료 참조). 변경하는 동안 :
if (array[i] < boundary) count++;
...에
count += (array[i] < boundary) ? 1 : 0;
실행 시간은 이제 다음과 같은 이유로 경계 값과 무관합니다.
최적화 프로그램은 삼항 표현식에서 분기를 제거 할 수있었습니다.
하지만 내 데스크탑 인텔 i5 cpu / windows 10 / vs2015에서 내 테스트 결과는 msdn 블로그와 상당히 다릅니다.
디버그 모드를 사용할 때 , if / else 비용 :
count= 0, time = 6434
count= 100000, time = 7652
count= 200800, time = 10124
count= 300200, time = 12820
count= 403100, time = 15566
count= 497400, time = 16911
count= 602900, time = 15999
count= 700700, time = 12997
count= 797500, time = 11465
count= 902500, time = 7619
count=1000000, time = 6429
삼항 연산자 비용 :
count= 0, time = 7045
count= 100000, time = 10194
count= 200800, time = 12080
count= 300200, time = 15007
count= 403100, time = 18519
count= 497400, time = 20957
count= 602900, time = 17851
count= 700700, time = 14593
count= 797500, time = 12390
count= 902500, time = 9283
count=1000000, time = 7020
릴리스 모드를 사용할 때 , if / else 비용 :
count= 0, time = 7
count= 100000, time = 9
count= 200800, time = 9
count= 300200, time = 9
count= 403100, time = 9
count= 497400, time = 8
count= 602900, time = 7
count= 700700, time = 7
count= 797500, time = 10
count= 902500, time = 7
count=1000000, time = 7
삼항 연산자 비용 :
count= 0, time = 16
count= 100000, time = 17
count= 200800, time = 18
count= 300200, time = 16
count= 403100, time = 22
count= 497400, time = 16
count= 602900, time = 16
count= 700700, time = 15
count= 797500, time = 15
count= 902500, time = 16
count=1000000, time = 16
삼항 연산자가 내 컴퓨터의 if / else 문보다 느립니다!
따라서 다른 컴파일러 최적화 기술에 따라 내부 연산자와 if / else는 훨씬 다르게 동작 할 수 있습니다.
삼항 = 간단한 형태의 if-else. 대부분 가독성을 위해 사용할 수 있습니다.
Some of the more obscure operators in C exist solely because they allow implementation of various function-like macros as a single expression that returns a result. I would say that this is the main purpose why the
?:
and,
operators are allowed to exist, even though their functionality is otherwise redundant.Lets say we wish to implement a function-like macro that returns the largest of two parameters. It would then be called as for example:
int x = LARGEST(1,2);
The only way to implement this as a function-like macro would be
#define LARGEST(x,y) ((x) > (y) ? (x) : (y))
It wouldn't be possible with an
if ... else
statement, since it does not return a result value. Note)The other purpose of
?:
is that it in some cases actually increases readability. Most oftenif...else
is more readable, but not always. Take for example long, repetitive switch statements:switch(something) { case A: if(x == A) { array[i] = x; } else { array[i] = y; } break; case B: if(x == B) { array[i] = x; } else { array[i] = y; } break; ... }
This can be replaced with the far more readable
switch(something) { case A: array[i] = (x == A) ? x : y; break; case B: array[i] = (x == B) ? x : y; break; ... }
Please note that
?:
does never result in faster code thanif-else
. That's some strange myth created by confused beginners. In case of optimized code,?:
gives identical performance asif-else
in the vast majority of the cases.If anything,
?:
can be slower thanif-else
, because it comes with mandatory implicit type promotions, even of the operand which is not going to be used. But?:
can never be faster thanif-else
.
Note) Now of course someone will argue and wonder why not use a function. Indeed if you can use a function, it is always preferable over a function-like macro. But sometimes you can't use functions. Suppose for example that x
in the example above is declared at file scope. The initializer must then be a constant expression, so it cannot contain a function call. Other practical examples of where you have to use function-like macros involve type safe programming with _Generic
or "X macros".
The same as
if(0)
do();
if(0)
{
do();
}
ReferenceURL : https://stackoverflow.com/questions/758849/the-ternary-conditional-operator-in-c
'programing' 카테고리의 다른 글
.NET을 사용하여 동일한 프로세스로 여러 명령 줄 실행 (0) | 2021.01.17 |
---|---|
프로그래머가 아닌 사람에게 프로젝트 복잡성을 설명하는 좋은 은유가 있습니까? (0) | 2021.01.16 |
Java 8보다 Java 11에서 현저하게 느린 스택 추적 소비 (0) | 2021.01.16 |
MPVolumeView를 사용한 후 시스템 볼륨 오버레이를 다시 켜려면 어떻게합니까? (0) | 2021.01.16 |
RApacheOutputErrors로 출력 제어 (0) | 2021.01.16 |