programing

C의 삼항 (조건부) 연산자

kingscode 2021. 1. 16. 10:02
반응형

C의 삼항 (조건부) 연산자


조건부 연산자의 필요성은 무엇입니까? if-else 구조를 구현하기 때문에 기능적으로 중복됩니다. 조건부 연산자가 동등한 if-else 할당보다 더 효율적인 경우 컴파일러에서 if-else를 더 효율적으로 해석 할 수없는 이유는 무엇입니까?


삼항 연산자는 성능 지름길이 아니라 구문 및 가독성의 편의입니다. 사람들은 다양한 복잡성의 조건부로 인해 그 장점에 대해 분할되지만 짧은 조건의 경우 한 줄로 표현하는 것이 유용 할 수 있습니다.

또한 Charlie Martin이 썼 듯이 표현이기 때문에 C로 된 문장의 오른쪽에 나타날 수 있습니다. 이것은 간결하기 때문에 가치가 있습니다.


C에서 그것의 진정한 유용성은 그것이 진술이 아니라 표현식이라는 것입니다. 즉, 명령문의 오른쪽 (RHS)에 표시 할 수 있습니다. 따라서 특정 내용을 더 간결하게 작성할 수 있습니다.


주어진 다른 답변 중 일부는 훌륭합니다. 그러나 누구도 const간결한 방식으로 정확성을 강화하는 데 사용할 수 있다고 언급하지 않은 것에 놀랐습니다 .

이 같은:

const int n = (x != 0) ? 10 : 20;

따라서 기본적으로 nconst초기 값이 조건문에 따라 달라집니다. 가장 쉬운 대안은를 만들지 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에는 다른 측면에서 어느 정도 쉽게 구현할 수 있기 때문에 기술적으로 필요하지 않은 많은 것들이 있습니다. 다음은 불완전한 목록입니다.

  1. 동안
  2. ...에 대한
  3. 기능
  4. 구조체

이것없이 코드가 어떻게 보일지 상상해보십시오. 그러면 답을 찾을 수 있습니다. 삼항 연산자는 "구문 적 설탕"의 한 형태로,주의 깊게 사용하면 코드를 쉽게 작성하고 이해할 수 있습니다.


때로는 삼항 연산자가 작업을 수행하는 가장 좋은 방법입니다. 특히 삼항의 결과가 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 often if...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 than if-else. That's some strange myth created by confused beginners. In case of optimized code, ?: gives identical performance as if-else in the vast majority of the cases.

    If anything, ?: can be slower than if-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 than if-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

반응형