C의 아래 진술 중에서 어느 것이 더 나은가요?
static const int var = 5;
또는
#define var 5
또는
enum { var = 5 };
C의 아래 진술 중에서 어느 것이 더 나은가요?
static const int var = 5;
또는
#define var 5
또는
enum { var = 5 };
답변:
필요한 가치에 따라 다릅니다. 당신 (그리고 지금까지 다른 사람들)은 세 번째 대안을 생략했습니다.
static const int var = 5;
#define var 5
enum { var = 5 };
이름 선택에 관한 문제를 무시하면 다음과 같습니다.
따라서 대부분의 상황에서 대안보다 '열거'를 선호합니다. 그렇지 않으면 첫 번째 글 머리 기호와 마지막 글 머리 기호가 제어 요소가 될 가능성이 높으며 한 번에 두 가지를 모두 만족시켜야하는 경우 더 열심히 생각해야합니다.
C ++에 대해 묻는다면 매번 옵션 (1) (정적 const)를 사용합니다.
enum
은 int
([C99] 6.7.2.2/3) 로 구현된다는 것 입니다. A를 #define
사용하면 부호없는 문자 와 긴 문자 U
및 L
접미사 를 지정할 const
수 있으며 유형 을 지정할 수 있습니다. enum
일반적인 유형 변환에 문제가 발생할 수 있습니다.
enum
도 #define
사용 하지도 않습니다 . 값은 데이터 세그먼트 또는 힙 또는 스택에 스토리지가 할당되지 않고 명령의 일부로 오브젝트 코드에 나타납니다. 에 대해 약간의 공간이 할당 static const int
되지만 주소를 사용하지 않으면 컴파일러가 공간을 최적화 할 수 있습니다.
enum
의 (및 static const
)에 대한 또 다른 '투표' : 변경할 수 없습니다. define
될 수 #undefine
D '여기서 enum
및 static const
소정 값으로 고정된다.
일반적으로 말하면:
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_ ), 라이브러리를 숙독하는 동안 (일반적으로) 그 뒤에 특정 영역 (라이브러리)의 이름과 그 뒤에 의미있는 이름이 붙는 것을 알 수 있습니다.
그것은 일반적으로 긴 이름을 만듭니다 :)
static
주소를 가진 사람 만 남아 있어야합니다. 주소를 가져 가면 #define
또는 enum
주소를 사용할 수 없었습니다 ... 그래서 실제로 어떤 대안을 사용할 수 있는지 보지 못했습니다. "컴파일 시간 평가"를 없앨 수 있다면 extern const
대신 찾으실 수 있습니다 .
#if
이상 바람직 수 있습니다 #ifdef
부울 플래그 있지만,이 경우에는 불가능 정의 할 것 var
같은 0
명령 줄에서. 따라서이 경우에 대한 법적 가치가있는 한 #ifdef
더 의미 0
가 var
있습니다.
C에서는 구체적으로? 사용 : C에서 정답은 #define
(해당되는 경우, 또는 enum
)
const
객체 의 범위 지정 및 타이핑 속성을 갖는 것이 유리하지만 , 실제로 const
C의 객체 (C ++와 반대)는 실제 상수가 아니므로 대부분의 실제 경우에는 쓸모가 없습니다.
따라서 C에서 선택은 상수를 어떻게 사용할 계획인지에 따라 결정되어야합니다. 예를 들어, const int
개체를 case
레이블 로 사용할 수 없습니다 (매크로가 작동하는 동안). const int
매크로를 작동하는 동안 객체를 비트 필드 너비로 사용할 수 없습니다 . C89 / 90에서는 const
객체를 사용하여 배열 크기를 지정할 수 없습니다 (매크로가 작동하는 동안). C99에서도 VLAconst
가 아닌 배열 이 필요할 때 객체를 사용하여 배열 크기를 지정할 수 없습니다 .
이것이 당신에게 중요하다면 그것은 당신의 선택을 결정할 것입니다. 대부분의 경우 #define
C 에서 사용하는 것 외에는 선택의 여지가 없습니다 enum
. C-에서 진정한 상수를 생성하는 다른 대안을 잊지 마십시오 .
C ++ const
객체는 진정한 상수이므로 C ++에서는 const
변형 을 선호하는 것이 거의 항상 좋습니다 ( static
C ++ 에서는 명시 적으로 필요하지 않음 ).
const int
모든 버전의 C 언어에서 대소 문자 레이블에 객체를 사용하는 것은 불법입니다. (물론 컴파일러는 비표준 C ++와 같은 언어 확장으로 자유롭게 지원할 수 있습니다.)
const
읽기 전용을 의미합니다. const int r = rand();
완벽하게 합법적입니다.
constexpr
에 비해 const
특별히와 stl
같은 용기 array
나 bitset
.
switch()
은 case
하나가 아니라 진술에서 테스트해야 합니다. 방금 이것에 붙잡 혔습니다 ☺
차이 static const
하고 #define
있다는 이전의 메모리 사용 및 저장을 위해 나중에 사용하지 않는 메모리. 둘째,의 주소는 전달할 수 없지만의 주소 #define
는 전달할 수 있습니다 static const
. 실제로 그것은 우리가 어떤 환경에 있는지에 따라 다르며,이 두 가지 중에서 하나를 선택해야합니다. 둘 다 다른 상황에서 최선을 다하고 있습니다. 하나가 다른 것보다 낫다고 가정하지 마십시오 ... :-)
만약 그렇다면, Dennis Ritchie 는 최선을 다했을 것입니다 ... hahaha ... :-)
const
가 메모리를 사용 한다는 것은 사실이 아닙니다 . GCC (4.5.3 및 몇 가지 최신 버전으로 테스트)는 const int
-O3을 사용할 때 코드에서 직접 리터럴로 쉽게 최적화합니다 . 따라서 RAM 내장 개발 (예 : AVR)을 수행하지 않으면 GCC 또는 다른 호환되는 컴파일러를 사용하는 경우 C const를 안전하게 사용할 수 있습니다. 나는 그것을 테스트하지는 않았지만 Clang이 btw와 같은 일을 할 것으로 기대합니다.
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 ++에서만]에 의해 암시되기 때문에 제외하십시오 .
const int MY_CONSTANT = 5;
하나의 파일을 가지고 다른 파일로 액세스 할 수 있습니다 extern const int MY_CONSTANT;
. const
기본 동작 변경 에 대한 표준 (최소한 C99) 정보를 찾을 수 없습니다 "6.2.2 : 5 개체에 대한 식별자 선언에 파일 범위가 있고 스토리지 클래스 지정자가없는 경우 연결은 외부입니다".
bar
VLA (가변 길이 배열)입니다. 컴파일러는 길이가 일정한 것처럼 코드를 생성 할 수 있습니다.
const
C 의 또 다른 단점은 다른를 초기화 할 때 값을 사용할 수 없다는 것 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 uint8_t const ARRAY_SIZE = 16;
갑자기 모든 컴파일이 더 이상 수행되지 않는 이유를 추적 하는 #define ARRAY_SIZE 256
것은 특히 어려운 일 입니다. 모든 대문자 이름 ARRAY_SIZE
이 문제를 요구하고 있습니다. 매크로를 위해 ALL_CAPS를 예약하고 ALL_CAPS 형식이 아닌 매크로를 정의하지 마십시오.
const
. 이것은 더 많이 upvoted 할 수 있습니다!
도망 갈 수 있다면 static const
많은 장점이 있습니다. 일반적인 범위 원칙을 준수하고 디버거에서 볼 수 있으며 일반적으로 변수가 따르는 규칙을 따릅니다.
그러나 최소한 원래 C 표준에서는 실제로 일정하지 않습니다. 당신이 사용하는 경우 #define var 5
, 당신은 쓸 수 int foo[var];
선언으로,하지만 당신과 함께 컴파일러 확장 "를 제외하고 (그렇게 할 수 없습니다 static const int var = 5;
. 이것은 ++은 C의 경우가 아니라 static const
버전 어디서나 사용할 수 있습니다#define
버전 캔, 그리고 믿을 C99의 경우도 마찬가지입니다.
그러나 #define
소문자 이름 으로 상수 이름을 지정하지 마십시오 . 번역 단위가 끝날 때까지 해당 이름을 사용할 수 있습니다. 매크로 상수는 사실상 고유의 네임 스페이스에 있어야하며, 일반적으로 접두사가있는 모든 대문자입니다.
const
C99에서는 여전히 실제 상수가 아닙니다. const
C99에서 a 를 사용하여 배열 크기를 선언 할 수 있지만 C99는 가변 길이 배열을 지원하므로 가능합니다. 이러한 이유로 VLA가 허용되는 경우에만 작동합니다. 예를 들어, C99에서도을 사용하여 const
의 멤버 배열 크기를 선언 할 수는 없습니다 struct
.
const int
마치 마치 C ++ const 나 매크로 인 것처럼 크기로 배열을 완벽하게 초기화 할 수있게 해줍니다. 표준에서 GCC의 편차에 의존하고 싶을지는 물론 당신이 선택하는 것입니다 .GCC 또는 Clang 이외의 다른 컴파일러를 사용하여 실제로 볼 수 없다면, 후자는 동일한 기능을 가지고 있습니다 (Clang으로 테스트 됨) 3.7).
#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
어떤 방법으로도 수정할 수없는 상수 값을 정의하는 유일한 방법입니다.
#define
기계 코드를 편집하여 수정할 수도 있습니다.
5
됩니다. 그러나 #define
전 처리기 매크로이기 때문에 수정할 수 없습니다 . 바이너리 프로그램에는 존재하지 않습니다. CONST_VALUE
사용 된 모든 장소를 수정 하려면 하나씩 수행해야합니다.
#define CONST 5
다음 if (CONST == 5) { do_this(); } else { do_that(); }
컴파일러가 else
분기를 제거 한다고 가정 해 봅시다 . CONST
6 으로 변경 하기 위해 기계어 코드 편집을 어떻게 제안 합니까?
#define
방탄 없습니다.
#define
입니다. 그렇게하는 유일한 방법은 소스 코드를 편집하고 다시 컴파일하는 것입니다.
문제는 정수에 관한 것이지만 상수 구조 또는 문자열이 필요한 경우 #define 및 enum은 쓸모가 없다는 점에 주목할 가치가 있습니다. 이것들은 보통 포인터로 함수에 전달됩니다. (문자열이 필요하고 구조가 훨씬 효율적입니다.)
정수의 경우 메모리가 매우 제한된 임베디드 환경에 있으면 상수가 저장되는 위치와 액세스가 컴파일되는 방법에 대해 걱정해야 할 수도 있습니다. 컴파일러는 런타임에 두 개의 const를 추가 할 수 있지만 컴파일시 두 개의 #defines를 추가 할 수 있습니다. #define 상수는 하나 이상의 MOV [즉시] 명령어로 변환 될 수 있으며, 이는 상수가 프로그램 메모리에 효과적으로 저장됨을 의미합니다. const 상수는 데이터 메모리의 .const 섹션에 저장됩니다. 하버드 아키텍처를 사용하는 시스템에서는 성능과 메모리 사용량에 차이가있을 수 있지만 크기는 작을 수 있습니다. 내부 루프의 하드 코어 최적화에 중요 할 수 있습니다.
Matthieu가 말한 것처럼 "항상 최고"에 대한 답이 있다고 생각하지 마십시오.
static const
안전합니다. 그러나 내 가장 큰 애완 동물 #define
은 Visual Studio 에서 디버깅 할 때 변수를 볼 수 없다는 것입니다. 기호를 찾을 수 없다는 오류가 발생합니다.
또한 #define
적절한 범위 지정을 제공하지만 "실제"상수처럼 동작하는에 대한 대안 은 "enum"입니다. 예를 들면 다음과 같습니다.
enum {number_ten = 10;}
많은 경우 열거 된 유형을 정의하고 해당 유형의 변수를 작성하는 것이 유용합니다. 그렇게하면 디버거가 열거 이름에 따라 변수를 표시 할 수 있습니다.
그러나 C ++에서 열거 형은 정수와의 호환성이 제한적입니다. 예를 들어, 기본적으로 산술을 수행 할 수 없습니다. 열거 형에 대한 호기심이 많은 기본 동작이라는 것을 알았습니다. C ++이 일반적으로 C와 호환되도록하려면 "엄격 열거 형"형식을 사용하는 것이 좋았지 만 "enum"형식의 기본 동작은 정수와 호환 가능해야한다고 생각합니다.
int
이므로 "enum hack"은 다른 정수 유형과 함께 사용할 수 없습니다. 열거 형 은 구현 정의 정수형과 반드시 호환 될 필요 int
는 없지만,이 경우에는 익명이므로 아무리 중요하지도 않습니다.
int
가 열거 형 변수 이외의 유형 (컴파일러가 허용) 이외의 유형 을 할당하고 그러한 변수에 할당하려고 하면 MISRA-C가 스 쿼크된다는 것을 읽었습니다. 자체 열거 형의 멤버 표준위원회가 지정된 의미론으로 정수 유형을 선언하는 이식 가능한 방법을 추가하기를 바랍니다. 크기에 상관없이 모든 플랫폼 char
은 컴파일러가 AND R0,#0xFFFF
명령을 많이 추가 하거나 이에 상응하는 명령 을 추가해야하더라도 mod 65536을 래핑 할 유형을 선언 할 수 있어야합니다 .
uint16_t
물론 열거 형이 아닌을 사용할 수 있습니다 . 사용자가 주어진 열거 유형을 나타내는 데 사용되는 정수 유형을 지정하게하는 것이 좋지만 개별 값에 대해 typedef
for uint16_t
및 일련의 #define
s를 사용 하여 동일한 효과를 얻을 수 있습니다 .
2U < -1L
참으로 평가 되고 다른 플랫폼은 거짓으로 평가 된다는 사실에 고착하고 있으며, 이제 일부 플랫폼이 사이에 uint32_t
그리고 int32_t
서명 된 것과 의 비교를 구현한다는 사실에 고착 하고 있습니다 그리고 일부는 서명되지 않았지만위원회가 의미가 모든 컴파일러에서 일관된 유형을 포함하는 C의 상위 호환 호환 후속 작업을 정의 할 수 없음을 의미하지는 않습니다.
간단한 차이점 :
사전 처리시 상수는 해당 값으로 대체됩니다. 따라서 역 참조 연산자를 정의에 적용 할 수 없지만 역 참조 연산자를 변수에 적용 할 수 있습니다.
가정 하듯이, 정적 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가 프로그램의 텍스트 세그먼트에 할당됩니다.
위의 참조 연산자에 대해 말하고 싶기 때문에 역 참조를 참조로 바꿉니다.
const
한정자에 대한 의미가 매우 다릅니다 . C에는 열거 상수 이외의 기호 상수가 없습니다 . A const int
는 변수입니다. 또한 언어와 특정 구현을 혼동합니다. 물체를 어디에 둘 필요가 없습니다. 그리고 gcc의 경우에도 마찬가지입니다. 일반적으로 섹션에 const
한정된 변수를 배치 .rodata
합니다. 그러나 그것은 목표 플랫폼에 달려 있습니다. 그리고 당신은 주소 연산자를 의미합니다 &
.
내가 옳은지는 확실하지 않지만 내 의견으로는 #define
d 값을 호출하는 것이 일반적으로 선언 된 다른 변수 (또는 const 값)를 호출하는 것보다 훨씬 빠릅니다. 프로그램이 실행 중이고 정상적으로 선언 된 변수를 사용해야 할 때 해당 변수를 얻으려면 메모리의 정확한 위치로 이동해야하기 때문입니다.
#define
d 값을 사용할 때 반대로 프로그램은 할당 된 메모리로 이동할 필요가 없으며 값을 가져옵니다. 경우 #define myValue 7
프로그램을 호출하고 myValue
, 그것이 바로 호출 할 때와 완전히 동일하게 동작합니다 7
.