Nice programing

C ++에서 전역 상수 정의

nicepro 2020. 10. 21. 21:26
반응형

C ++에서 전역 상수 정의


여러 소스 파일에서 볼 수 있도록 C ++에서 상수를 정의하고 싶습니다. 다음과 같은 방법으로 헤더 파일에 정의 할 수 있습니다.

  1. #define GLOBAL_CONST_VAR 0xFF
  2. int GLOBAL_CONST_VAR = 0xFF;
  3. 값을 returing 일부 기능 (예를 들어 int get_GLOBAL_CONST_VAR())
  4. enum { GLOBAL_CONST_VAR = 0xFF; }
  5. const int GLOBAL_CONST_VAR = 0xFF;
  6. extern const int GLOBAL_CONST_VAR; 그리고 하나의 소스 파일에서 const int GLOBAL_CONST_VAR = 0xFF;

옵션 (1)-확실히 사용하고 싶은 옵션이 아닙니다.

옵션 (2)-헤더 파일을 사용하여 각 개체 파일의 변수 인스턴스 정의

옵션 (3)-IMO는 대부분의 경우 과잉 살인입니다.

옵션 (4)-enum에 구체적인 유형이 없기 때문에 많은 경우 좋지 않을 수 있습니다 (C ++ 0X는 유형을 정의 할 가능성을 추가합니다).

따라서 대부분의 경우 (5)와 (6) 중에서 선택해야합니다. 내 질문 :

  1. (5) 또는 (6) 무엇을 선호합니까?
  2. 왜 (5)는 괜찮고 (2)는 괜찮습니까?

(5) 당신이 말하고 싶은 것을 정확하게 말합니다. 또한 컴파일러가 대부분의 시간을 최적화 할 수 있습니다. (6) 반면에 컴파일러는 당신이 결국 그것을 변경할지 여부를 알지 못하기 때문에 컴파일러가 그것을 최적화하지 못하게 할 것입니다.


확실히 옵션 5로 가십시오-유형이 안전하고 컴파일러가 최적화 할 수 있도록합니다 (해당 변수의 주소를 사용하지 마십시오 :) 또한 헤더에있는 경우 전역 범위를 오염시키지 않도록 네임 스페이스에 붙여 넣으십시오.

// header.hpp
namespace constants
{
    const int GLOBAL_CONST_VAR = 0xFF;
    // ... other related constants

} // namespace constants

// source.cpp - use it
#include <header.hpp>
int value = constants::GLOBAL_CONST_VAR;

(5)는 GLOBAL_CONST_VAR모든 번역 단위에서 ICE (Integral Constant Expression)로 정의되기 때문에 (6)보다 "낫습니다" . 예를 들어, 모든 번역 단위에서 배열 크기 및 케이스 레이블로 사용할 수 있습니다. (6)의 경우 GLOBAL_CONST_VAR정의 된 번역 단위에서 정의 지점 이후에만 ICE가됩니다. 다른 번역 단위에서는 ICE로 작동하지 않습니다.

그러나 (5)는 GLOBAL_CONST_VAR내부 연결을 제공합니다 . 즉,의 "주소 ID"가 GLOBAL_CONST_VAR각 번역 단위에서 &GLOBAL_CONST_VAR다를 것입니다. 즉, 각 번역 단위에서 다른 포인터 값을 제공합니다. 대부분의 사용 사례에서 이것은 중요하지 않지만 일관된 전역 "주소 ID"를 가진 상수 개체가 필요한 경우 (6)으로 이동하여 상수의 ICE-ness를 희생해야합니다. 방법.

또한 상수의 ICE-ness가 문제가되지 않고 (정수 유형이 아님) 유형의 크기가 커지면 (스칼라 유형이 아님) 일반적으로 (6)이 (5)보다 더 나은 접근 방식이됩니다.

(2)는 GLOBAL_CONST_VAR기본적으로 (2)에 외부 연결이 있기 때문에 OK가 아닙니다 . 헤더 파일에 넣으면 일반적으로에 대한 여러 정의가 발생 GLOBAL_CONST_VAR하며 이는 오류입니다. constC ++의 객체는 기본적으로 내부 연결이 있습니다. 이것이 (5)가 작동하는 이유입니다 (그리고 위에서 말했듯 GLOBAL_CONST_VAR이 각 번역 단위에서 별도의 독립적 인 이유를 얻습니다 ).


C ++ 17부터 선언 할 수있는 옵션이 있습니다.

inline extern const int GLOBAL_CONST_VAR = 0xFF;

헤더 파일에서. 이렇게하면 모든 번역 단위 (방법 (5)와 마찬가지로)에서 ICE를 제공하는 동시에 전역 주소 ID를 유지합니다 GLOBAL_CONST_VAR. 모든 번역 단위에서 동일한 주소를 갖게됩니다.


상수가 되려면 상수로 표시해야합니다. 이것이 제 생각에 2가 나쁜 이유입니다.

컴파일러는 값의 const 특성을 사용하여 일부 수학 및 실제로 값을 사용하는 다른 연산을 확장 할 수 있습니다.

5에서 6 사이의 선택-흠; 5 기분이 나아졌습니다.

6)에서 값은 선언에서 불필요하게 분리됩니다.

나는 일반적으로 상수 등 만 정의하는 이러한 헤더 중 하나 이상을 가지고 있으며 다른 '영리한'것은 없습니다. 어디서나 쉽게 포함될 수있는 멋진 경량 헤더.


C ++ 11 이상을 사용하는 경우 컴파일 시간 상수를 사용해보십시오.

constexpr int GLOBAL_CONST_VAR{ 0xff };

두 번째 질문에 답하려면 :

(2) 단일 정의 규칙을 위반하여 불법입니다. GLOBAL_CONST_VAR포함 된 모든 파일, 즉 두 번 이상 정의합니다 . (5)는 하나의 정의 규칙이 적용되지 않기 때문에 합법적입니다. 각각 GLOBAL_CONST_VAR은 포함 된 해당 파일에 로컬 인 별도의 정의입니다. 이러한 모든 정의는 물론 동일한 이름과 값을 공유하지만 주소는 다를 수 있습니다.


const int GLOBAL_CONST_VAR = 0xFF;

상수이기 때문에!


귀하의 요구 사항에 따라 다릅니다. (5)는 대부분의 일반적인 사용에 가장 적합하지만 종종 모든 개체 파일에서 저장 공간을 계속 차지하게됩니다. (6) 중요한 상황에서이 문제를 해결할 수 있습니다.

(4) 또한 우선 순위가 저장 공간이 할당되지 않도록 보장하는 경우 적절한 선택이지만 물론 정수 상수에 대해서만 작동합니다.


#define GLOBAL_CONST_VAR 0xFF // this is C code not C++
int GLOBAL_CONST_VAR = 0xFF; // it is not constant and maybe not compilled
Some function returing the value (e.g. int get_LOBAL_CONST_VAR()) // maybe but exists better desision
enum { LOBAL_CONST_VAR = 0xFF; } // not needed, endeed, for only one constant (enum elms is a simple int, but with secial enumeration)
const int GLOBAL_CONST_VAR = 0xFF; // it is the best
extern const int GLOBAL_CONST_VAR; //some compiller doesn't understand this

C ++ 17 inline변수

This awesome C++17 feature allow us to:

  • conveniently use just a single memory address for each constant
  • store it as a constexpr: How to declare constexpr extern?
  • do it in a single line from one header

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline constexpr int notmain_i = 42;

const int* notmain_func();

#endif

notmain.cpp

#include "notmain.hpp"

const int* notmain_func() {
    return &notmain_i;
}

Compile and run:

g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main

GitHub upstream.

See also: How do inline variables work?

C++ standard on inline variables

The C++ standard guarantees that the addresses will be the same. C++17 N4659 standard draft 10.1.6 "The inline specifier":

6 An inline function or variable with external linkage shall have the same address in all translation units.

cppreference https://en.cppreference.com/w/cpp/language/inline explains that if static is not given, then it has external linkage.

Inline variable implementation

We can observe how it is implemented with:

nm main.o notmain.o

which contains:

main.o:
                 U _GLOBAL_OFFSET_TABLE_
                 U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
                 U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i

notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i

and man nm says about u:

"u" The symbol is a unique global symbol. This is a GNU extension to the standard set of ELF symbol bindings. For such a symbol the dynamic linker will make sure that in the entire process there is just one symbol with this name and type in use.

so we see that there is a dedicated ELF extension for this.

Tested on GCC 7.4.0, Ubuntu 18.04.

참고URL : https://stackoverflow.com/questions/2268749/defining-global-constant-in-c

반응형