Programing

“정적 const”vs“#define”vs“enum”

lottogame 2020. 2. 9. 20:29
반응형

“정적 const”vs“#define”vs“enum”


C의 아래 진술 중 어느 것이 더 나은가요?

static const int var = 5;

또는

#define var 5

또는

enum { var = 5 };

일반적으로 말하면:

static const

범위를 존중하고 형식이 안전하기 때문입니다.

내가 볼 수있는 유일한주의 사항 : 변수를 명령 줄에 정의하려는 경우. 여전히 대안이 있습니다.

#ifdef VAR // Very bad name, not long enough, too general, etc..
  static int const var = VAR;
#else
  static int const var = 5; // default value
#endif

가능하면 매크로 / 줄임표 대신 유형이 안전한 대안을 사용하십시오.

매크로와 함께 가야하는 경우 (예 : __FILE__또는 __LINE__) 매크로 이름을 매우 신중하게 지정하는 것이 좋습니다. 명명 규칙에서 Boost 는 프로젝트 이름으로 시작하는 모든 대문자를 권장합니다 (여기서는 BOOST_ ), 라이브러리를 숙독하는 동안 (일반적으로) 그 뒤에 특정 영역 (라이브러리)의 이름이 붙은 다음 의미있는 이름이 붙습니다.

그것은 일반적으로 긴 이름을 만듭니다 :)


필요한 가치에 따라 다릅니다. 당신 (그리고 지금까지 다른 사람들)은 세 번째 대안을 생략했습니다.

  1. static const int var = 5;
  2. #define var 5
  3. enum { var = 5 };

이름 선택에 관한 문제를 무시하면 다음과 같습니다.

  • 포인터를 주위에 전달해야하는 경우 (1)을 사용해야합니다.
  • (2)는 분명히 옵션이므로 포인터를 전달할 필요가 없습니다.
  • (1)과 (3) 모두 디버거의 심볼 테이블에 심볼이있어 디버깅이 쉬워집니다. (2)에 기호가 없을 가능성이 높으며 그 의미가 무엇인지 궁금해합니다.
  • (1) 전역 범위에서 배열의 차원으로 사용할 수 없습니다. (2)와 (3) 모두 가능합니다.
  • (1) 함수 범위에서 정적 배열의 차원으로 사용할 수 없습니다. (2)와 (3) 모두 가능합니다.
  • C99에서는이 모든 것을 로컬 어레이에 사용할 수 있습니다. 기술적으로 (1)을 사용하는 것은 VLA (가변 길이 배열)를 사용한다는 것을 의미하지만 'var'이 참조하는 차원은 물론 크기 5로 고정됩니다.
  • (1) 스위치 문과 같은 장소에서는 사용할 수 없습니다. (2)와 (3) 모두 가능합니다.
  • (1) 정적 변수를 초기화하는 데 사용할 수 없습니다. (2)와 (3) 모두 가능합니다.
  • (2) 전 처리기에서 사용하기 때문에 변경하고 싶지 않은 코드를 변경할 수 있습니다. (1)과 (3)은 모두 예기치 않은 부작용이 없습니다.
  • 전처리기에 (2)가 설정되어 있는지 여부를 감지 할 수 있습니다. (1)도 (3)도 허용하지 않습니다.

따라서 대부분의 상황에서 대안보다 '열거'를 선호합니다. 그렇지 않으면 첫 번째 글 머리 기호와 마지막 글 머리 기호가 제어 요소가 될 수 있습니다. 한 번에 두 가지를 모두 만족시켜야하는 경우 더 열심히 생각해야합니다.

C ++에 대해 묻는다면 매번 옵션 (1) (정적 const)를 사용합니다.


C에서는 구체적으로? 사용 : C에서 정답은 #define(해당되는 경우, 또는 enum)

const객체 의 범위 지정 및 타이핑 속성을 갖는 것이 유리하지만 , 실제로 constC의 객체 (C ++와 반대)는 실제 상수가 아니므로 대부분의 실제 경우에는 쓸모가 없습니다.

따라서 C에서 선택은 상수를 어떻게 사용할 계획인지에 따라 결정되어야합니다. 예를 들어, const int개체를 case레이블 사용할 수 없습니다 (매크로가 작동하는 동안). const int매크로를 작동하는 동안 객체를 비트 필드 너비로 사용할 수 없습니다 . C89 / 90에서는 const객체를 사용하여 배열 크기를 지정할 수 없습니다 (매크로가 작동하는 동안). C99에서도 VLAconst 가 아닌 배열 이 필요할 때 객체를 사용하여 배열 크기를 지정할 수 없습니다 .

이것이 당신에게 중요하다면 그것은 당신의 선택을 결정할 것입니다. 대부분의 경우 #defineC 에서 사용할 수밖에 없습니다 enum. C-에서 상수를 생성하는 다른 대안을 잊지 마십시오 .

C ++ const객체는 진정한 상수이므로 C ++에서는 const변형 을 선호하는 것이 거의 항상 좋습니다 ( staticC ++ 에서는 명시 적으로 필요하지 않음 ).


차이 static const하고 #define있다는 이전의 메모리 사용 및 저장을 위해 나중에 사용하지 않는 메모리. 둘째,의 주소는 전달할 수 없지만의 주소 #define는 전달할 수 있습니다 static const. 실제로 그것은 우리가 어떤 환경에 있는지에 따라 다르며,이 두 가지 중에서 하나를 선택해야합니다. 둘 다 다른 환경에서 최선을 다하고 있습니다. 하나가 다른 것보다 낫다고 가정하지 마십시오 ... :-)

만약 그렇다면, Dennis Ritchie 는 최선을 다했을 것입니다 ... hahaha ... :-)


C #define에서는 훨씬 더 인기가 있습니다. 예를 들어 배열 크기를 선언하기 위해 해당 값을 사용할 수 있습니다.

#define MAXLEN 5

void foo(void) {
   int bar[MAXLEN];
}

ANSI C는 static const내가 아는 한이 문맥에서 s 를 사용할 수 없습니다 . C ++에서는 이러한 경우 매크로를 피해야합니다. 당신은 쓸 수 있습니다

const int maxlen = 5;

void foo() {
   int bar[maxlen];
}

static내부 연결은 const이미 [C ++에서만]에 의해 암시되기 때문에 제외하십시오 .


constC 의 또 다른 단점은 다른을 초기화 할 때 값을 사용할 수 없다는 것 const입니다.

static int const NUMBER_OF_FINGERS_PER_HAND = 5;
static int const NUMBER_OF_HANDS = 2;

// initializer element is not constant, this does not work.
static int const NUMBER_OF_FINGERS = NUMBER_OF_FINGERS_PER_HAND 
                                     * NUMBER_OF_HANDS;

컴파일러가 상수로 보지 않기 때문에 const에서도 작동하지 않습니다.

static uint8_t const ARRAY_SIZE = 16;
static int8_t const lookup_table[ARRAY_SIZE] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // ARRAY_SIZE not a constant!

const이 경우에는 타이핑을 사용하게되어 기쁩니다 . 그렇지 않으면 ...


당신이 그것을 벗어날 수 있다면 static const많은 장점이 있습니다. 일반적인 범위 원칙을 따르고 디버거에서 볼 수 있으며 일반적으로 변수가 따르는 규칙을 따릅니다.

그러나 최소한 원래 C 표준에서는 실제로 일정하지 않습니다. 당신이 사용하는 경우 #define var 5, 당신은 쓸 수 int foo[var];선언으로,하지만 당신과 함께 컴파일러 확장 "를 제외하고 (그렇게 할 수 없습니다 static const int var = 5;. 이것은 ++은 C의 경우가 아니라 static const버전은 어디서나 사용할 수있는 #define버전 캔, 그리고 믿을 C99의 경우도 마찬가지입니다.

그러나 #define소문자 이름 으로 상수 이름을 지정하지 마십시오 . 번역 단위가 끝날 때까지 해당 이름을 사용할 수 있습니다. 매크로 상수는 사실상 고유 이름 공간에 있어야하며, 일반적으로 접두사가있는 모든 대문자입니다.


#define 대신 const를 사용하는 것이 좋습니다. const가 컴파일러에 의해 처리되고 #define이 전처리기에 의해 처리되기 때문입니다. #define 자체는 코드의 일부가 아닙니다 (거의 말하기).

예:

#define PI 3.1416

기호 이름 PI는 컴파일러에서 볼 수 없습니다. 소스 코드가 컴파일러에 도달하기 전에 프리 프로세서에 의해 제거 될 수 있습니다. 결과적으로 이름 PI가 기호 테이블에 입력되지 않을 수 있습니다. 오류 메시지가 PI가 아닌 3.1416을 참조 할 수 있으므로 상수 사용과 관련된 컴파일 중에 오류가 발생하면 혼동 될 수 있습니다. PI가 작성하지 않은 헤더 파일에 정의되어 있으면 3.1416의 출처를 모를 것입니다.

프로그래밍하는 이름이 심볼 테이블에 없을 수도 있으므로이 문제는 심볼릭 디버거에서도 발생할 수 있습니다.

해결책:

const double PI = 3.1416; //or static const...

#define var 5당신이 같은 것을 가지고 있다면 문제를 일으킬 것 mystruct.var입니다.

예를 들어

struct mystruct {
    int var;
};

#define var 5

int main() {
    struct mystruct foo;
    foo.var = 1;
    return 0;
}

전처리 기가이를 대체하고 코드는 컴파일되지 않습니다. 이러한 이유로, 전통적인 코딩 스타일은 모든 상수가 #define충돌을 피하기 위해 대문자를 사용하도록 제안합니다 .


한 가지 차이점을 보여주기 위해 빠른 테스트 프로그램을 작성했습니다.

#include <stdio.h>

enum {ENUM_DEFINED=16};
enum {ENUM_DEFINED=32};

#define DEFINED_DEFINED 16
#define DEFINED_DEFINED 32

int main(int argc, char *argv[]) {

   printf("%d, %d\n", DEFINED_DEFINED, ENUM_DEFINED);

   return(0);
}

이것은 다음과 같은 오류 및 경고로 컴파일됩니다.

main.c:6:7: error: redefinition of enumerator 'ENUM_DEFINED'
enum {ENUM_DEFINED=32};
      ^
main.c:5:7: note: previous definition is here
enum {ENUM_DEFINED=16};
      ^
main.c:9:9: warning: 'DEFINED_DEFINED' macro redefined [-Wmacro-redefined]
#define DEFINED_DEFINED 32
        ^
main.c:8:9: note: previous definition is here
#define DEFINED_DEFINED 16
        ^

define에서 경고를 표시하면 enum에서 오류가 발생합니다.


정의

const int const_value = 5;

항상 상수 값을 정의하지는 않습니다. 일부 컴파일러 (예 : tcc 0.9.26 )는 "const_value"라는 이름으로 식별 된 메모리 만 할당합니다. 식별자 "const_value"를 사용하면이 메모리를 수정할 수 없습니다. 그러나 여전히 다른 식별자를 사용하여 메모리를 수정할 수 있습니다.

const int const_value = 5;
int *mutable_value = (int*) &const_value;
*mutable_value = 3;
printf("%i", const_value); // The output may be 5 or 3, depending on the compiler.

이것은 정의를 의미

#define CONST_VALUE 5

어떤 방법으로도 수정할 수없는 상수 값을 정의하는 유일한 방법입니다.


Matthieu가 말한 것처럼 "항상 최고"에 대한 답변이 있다고 생각하지 마십시오.

static const

안전합니다. 그러나 내 가장 큰 애완 동물 #defineVisual Studio 에서 디버깅 할 때 변수를 볼 수 없다는 것입니다. 기호를 찾을 수 없다는 오류가 발생합니다.


또한 #define적절한 범위 지정을 제공하지만 "실제"상수처럼 동작하는에 대한 대안 은 "열거"입니다. 예를 들면 다음과 같습니다.

enum {number_ten = 10;}

많은 경우 열거 된 유형을 정의하고 해당 유형의 변수를 작성하는 것이 유용합니다. 그렇게하면 디버거가 열거 이름에 따라 변수를 표시 할 수 있습니다.

그러나 C ++에서 열거 형은 정수와의 호환성이 제한적입니다. 예를 들어, 기본적으로 산술을 수행 할 수 없습니다. 열거 형에 대한 호기심이 많은 기본 동작이라는 것을 알았습니다. C ++이 C와 일반적으로 호환되기를 원한다면 "엄격한 열거 형"유형을 갖는 것이 좋았지 만 "enum"유형의 기본 동작은 정수와 호환 가능해야한다고 생각합니다.


문제는 정수에 관한 것이지만 상수 구조 또는 문자열이 필요한 경우 #define 및 enum은 쓸모가 없다는 점에 주목할 가치가 있습니다. 이것들은 보통 포인터로 함수에 전달됩니다. (문자열이 필요하고 구조가 훨씬 효율적입니다.)

정수의 경우 메모리가 매우 제한된 임베디드 환경에 있으면 상수가 저장되는 위치와 액세스가 컴파일되는 방법에 대해 걱정해야 할 수도 있습니다. 컴파일러는 런타임에 두 개의 const를 추가 할 수 있지만 컴파일시 두 개의 #defines를 추가 할 수 있습니다. #define 상수는 하나 이상의 MOV [즉시] 명령어로 변환 될 수 있으며, 이는 상수가 프로그램 메모리에 효과적으로 저장됨을 의미합니다. const 상수는 데이터 메모리의 .const 섹션에 저장됩니다. 하버드 아키텍처를 사용하는 시스템에서는 성능과 메모리 사용량에 차이가있을 수 있지만 크기는 작을 수 있습니다. 내부 루프의 하드 코어 최적화에 중요 할 수 있습니다.


간단한 차이점 :

사전 처리시 상수는 해당 값으로 대체됩니다. 따라서 역 참조 연산자를 정의에 적용 할 수 없지만 역 참조 연산자를 변수에 적용 할 수 있습니다.

가정 하듯이, 정적 const보다 define이 빠릅니다.

예를 들어,

#define mymax 100

당신은 할 수 없습니다 printf("address of constant is %p",&mymax);.

하지만

const int mymax_var=100

당신은 할 수 있습니다 printf("address of constant is %p",&mymax_var);.

더 명확하게하기 위해, 정의는 사전 처리 단계에서 값으로 대체되므로 프로그램에 변수가 저장되어 있지 않습니다. 정의가 사용 된 프로그램의 텍스트 세그먼트에있는 코드 만 있습니다.

그러나 정적 const의 경우 어딘가에 할당 된 변수가 있습니다. gcc의 경우 정적 const는 프로그램의 텍스트 세그먼트에 할당됩니다.

위의 참조 연산자에 대해 말하고 싶으므로 역 참조를 참조로 바꿉니다.


우리는 MBF16X에서 생성 된 어셈블러 코드를 살펴 보았습니다. 두 변형 모두 산술 연산 (예 : ADD Immediate)에 대해 동일한 코드가됩니다.

그래서 const int동안 유형 검사에 대한 선호 #define된 스타일입니다. 아마도 컴파일러마다 다를 수 있습니다. 따라서 생성 된 어셈블러 코드를 확인하십시오.


내가 옳은지는 확실하지 않지만 내 의견으로는 #defined 값을 호출하는 것이 일반적으로 선언 된 다른 변수 (또는 const 값)를 호출하는 것보다 훨씬 빠릅니다. 프로그램이 실행 중이고 정상적으로 선언 된 변수를 사용해야 할 때 해당 변수를 얻으려면 메모리의 정확한 위치로 이동해야하기 때문입니다.

반대로 #defined 값을 사용할 때 프로그램은 할당 된 메모리로 이동할 필요가 없으며 값을 가져옵니다. 경우 #define myValue 7프로그램을 호출하고 myValue, 그것이 바로 호출 할 때와 완전히 동일하게 동작합니다 7.

참고 URL : https://stackoverflow.com/questions/1674032/static-const-vs-define-vs-enum



반응형