답변:
그것은 C와 C ++ 모두에서 사용됩니다.
짐작했듯이 static
파트는 범위를 해당 컴파일 단위 로 제한합니다 . 또한 정적 초기화를 제공합니다. const
컴파일러에게 아무도 수정하지 못하도록 지시합니다. 이 변수는 아키텍처에 따라 데이터 또는 bss 세그먼트에 배치되며 읽기 전용으로 표시된 메모리에있을 수 있습니다.
이것이 C가 이러한 변수를 처리하는 방법 (또는 C ++이 네임 스페이스 변수를 처리하는 방법)입니다. C ++에서 표시된 멤버 static
는 지정된 클래스의 모든 인스턴스에서 공유됩니다. 비공개인지 여부는 하나의 변수가 여러 인스턴스에서 공유된다는 사실에 영향을주지 않습니다. 데 const
코드가를 수정하려고한다면 거기에 당신을 경고합니다.
엄격하게 비공개 인 경우 클래스의 각 인스턴스는 고유 한 버전을 얻습니다 (최적화에도 불구하고).
많은 사람들이 기본적인 대답을하는 듯했으나 아무도 C ++에서 지적하지 const
기본값 static
에서 namespace
레벨 (일부는 잘못된 정보를 주었다). C ++ 98 표준 섹션 3.5.3을 참조하십시오.
먼저 배경 :
번역 단위 : 전 처리기 (재귀 적으로)가 모든 포함 파일을 포함시킨 후의 소스 파일.
정적 연결 : 기호는 번역 단위 내에서만 사용할 수 있습니다.
외부 연결 : 다른 번역 단위에서 기호를 사용할 수 있습니다.
namespace
수준여기에는 전역 이름 공간 일명 전역 변수가 포함 됩니다.
static const int sci = 0; // sci is explicitly static
const int ci = 1; // ci is implicitly static
extern const int eci = 2; // eci is explicitly extern
extern int ei = 3; // ei is explicitly extern
int i = 4; // i is implicitly extern
static int si = 5; // si is explicitly static
static
함수 호출 사이에 값이 유지됨을 의미합니다.
함수 static
변수 의 의미는 프로그램의 데이터 세그먼트 (스택이나 힙이 아님) 에 있다는 점에서 전역 변수와 유사합니다 . 변수 수명 에 대한 자세한 내용 은 이 질문 을 참조하십시오 static
.
class
수준static
값이 클래스의 모든 인스턴스간에 공유되며 const
변경되지 않음을 의미합니다.
const int *foo(int x) {const int b=x;return &b};
대const int *foo(int x) {static const int b=x;return &b};
const
에만 암시하는 것에 대한 메모를 포함해야합니다 static
.
const
선언을 의미 static
뿐만 아니라이? 에서 const
와 같이 값을 버리고 수정하면 모든 값이 수정됩니까?
const
는 함수 수준에서 정적을 의미하지 않습니다. 그것은 동시성 악몽 (const! = constant expression)이 될 것이며, 함수 수준의 모든 것은 암시 적으로 auto
. 이 질문에는 [c] 태그도 지정되어 있으므로 전역 수준 const int
은 extern
C에서 암시 적 으로 사용 된다는 점을 언급해야합니다 . 그러나 여기에있는 규칙은 C ++를 완벽하게 설명합니다.
static
변수가 정적 기간 (프로그램 시작부터 끝까지 지속되는 복사본이 하나만 존재 함)이고, 달리 지정되지 않은 경우 내부 / 정적 연결이 있음을 나타냅니다 (이는 함수의 로컬 정적 변수에 대한 연결 또는 정적 멤버에 대한 클래스의 연결). 주요 차이점 static
은 유효한 각 상황에서 이것이 의미하는 바에 있습니다.
이 코드 줄은 실제로 여러 다른 컨텍스트에서 나타날 수 있으며 거의 동일하게 작동하지만 약간의 차이가 있습니다.
// foo.h
static const int i = 0;
' i
'은 헤더를 포함하는 모든 번역 단위에 표시됩니다. 그러나 실제로 객체의 주소 (예 : ' &i
')를 사용하지 않는 한 , 컴파일러는 ' i
'를 단순히 형식에 안전한 것으로 취급 할 것이라고 확신합니다 0
. 두 개 이상의 번역 단위가 ' &i
'를 사용하면 각 번역 단위에 대해 주소가 달라집니다.
// foo.cc
static const int i = 0;
' i
'에는 내부 연결이 있으므로이 번역 단위 외부에서 참조 할 수 없습니다. 그러나 주소를 사용하지 않는 한 다시 type-safe로 취급 될 가능성이 높습니다 0
.
지적 할 가치가있는 한 가지는 다음 선언입니다.
const int i1 = 0;
과 정확히 동일합니다 static const int i = 0
. 로 선언 const
되고 명시 적으로 선언되지 않은 네임 스페이스의 변수 extern
는 암시 적으로 정적입니다. 이것에 대해 생각해 보면 , ODR을 깨는 것을 피하기 위해 const
항상 static
키워드를 필요로하지 않고 헤더 파일에서 변수를 선언 할 수 있도록하는 것이 C ++위원회의 의도였습니다 .
class A {
public:
static const int i = 0;
};
위의 예에서 표준 i
은 주소가 필요하지 않은 경우 ' '를 정의 할 필요가 없음을 명시 적으로 지정 합니다. 즉, ' i
'를 유형 안전 0으로 만 사용 하면 컴파일러가이를 정의하지 않습니다. 클래스와 네임 스페이스 버전의 한 가지 차이점은 ' i
' 의 주소 (두 개 이상의 번역 단위에서 사용되는 경우)가 클래스 멤버에 대해 동일하다는 것입니다. 주소가 사용되는 경우 해당 주소에 대한 정의가 있어야합니다.
// a.h
class A {
public:
static const int i = 0;
};
// a.cc
#include "a.h"
const int A::i; // Definition so that we can take the address
작은 공간 최적화입니다.
당신이 말할 때
const int foo = 42;
상수를 정의하는 것이 아니라 읽기 전용 변수를 만듭니다. 컴파일러는 foo를 볼 때마다 42를 사용할만큼 똑똑하지만 초기화 된 데이터 영역에 공간을 할당하기도합니다. 이것은 정의 된대로 foo에 외부 연결이 있기 때문에 수행됩니다. 다른 컴파일 단위는 다음과 같이 말할 수 있습니다.
extern const int foo;
그 가치에 접근하기 위해. 그 컴파일 단위는 foo의 값이 무엇인지 전혀 모르기 때문에 좋은 습관이 아닙니다. 그것은 단지 그것이 const int라는 것을 알고 있고 그것이 사용될 때마다 메모리에서 값을 다시로드해야합니다.
이제 정적임을 선언하여 :
static const int foo = 42;
컴파일러는 일반적인 최적화를 수행 할 수 있지만 "야,이 컴파일 단위 외부의 누구도 foo를 볼 수 없으며 항상 42라는 것을 알고 있으므로 공간을 할당 할 필요가 없습니다."라고 말할 수도 있습니다.
또한 C ++에서 이름이 현재 컴파일 단위를 이스케이프하지 않도록 방지하는 가장 좋은 방법은 익명 네임 스페이스를 사용하는 것입니다.
namespace {
const int foo = 42; // same as static definition above
}
'int'가 없습니다. 그것은해야한다:
const static int foo = 42;
C 및 C ++에서는 로컬 파일 범위 값이 42 인 정수 상수를 선언합니다.
왜 42일까요? 당신이 아직 모른다면 (그리고 당신이 모른다는 것을 믿기 힘들다), 그것은 생명, 우주, 모든 것에 대한 답에 대한 언급 입니다.
모든 훌륭한 답변에 작은 세부 사항을 추가하고 싶습니다.
플러그인 (예 : CAD 시스템에서로드 할 DLL 또는 .so 라이브러리)을 작성하면 static 은 다음 과 같은 이름 충돌을 방지하는 생명의 은인입니다.
더 나쁜 점 : 3 단계는 컴파일러 최적화, 플러그인로드 메커니즘 등에 따라 다르게 작동 할 수 있습니다.
두 개의 플러그인에서 두 개의 도우미 함수 (같은 이름, 다른 동작)로이 문제가 한 번 발생했습니다. 정적으로 선언하면 문제가 해결되었습니다.
C99 / GNU99 사양에 따르면 :
static
스토리지 클래스 지정자
기본적으로 파일 수준 범위의 개체에는 외부 연결이 있습니다.
const
유형 한정자 (유형의 일부)
바로 왼쪽 인스턴스에 적용된 키워드-예
MyObj const * myVar;
-const 한정된 개체 유형에 대한 한정되지 않은 포인터
MyObj * const myVar;
-규정되지 않은 객체 유형에 대한 const 규정 된 포인터
가장 왼쪽 사용-변수가 아닌 개체 유형에 적용
const MyObj * myVar;
-const 한정된 개체 유형에 대한 한정되지 않은 포인터그러므로:
static NSString * const myVar;
-내부 연결이있는 불변 문자열에 대한 상수 포인터.
static
키워드가 없으면 변수 이름이 전역으로 만들어지고 응용 프로그램 내에서 이름 충돌이 발생할 수 있습니다.
C ++ 17 inline
변수
Google에서 "C ++ const static"을 검색했다면 실제로 사용하려는 것이 C ++ 17 인라인 변수 일 가능성이 높습니다. .
이 멋진 C ++ 17 기능을 통해 다음을 수행 할 수 있습니다.
constexpr
. constexpr extern을 선언하는 방법?main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_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 ¬main_i;
}
컴파일 및 실행 :
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
참고 항목 : 인라인 변수는 어떻게 작동합니까?
인라인 변수에 대한 C ++ 표준
C ++ 표준은 주소가 동일하다는 것을 보장합니다. C ++ 17 N4659 표준 초안 10.1.6 "인라인 지정자":
6 외부 연결이있는 인라인 함수 또는 변수는 모든 변환 단위에서 동일한 주소를 가져야합니다.
cppreference https://en.cppreference.com/w/cpp/language/inline는 경우라고 설명 static
지정되어 있지 않은 경우, 그것은 외부 링크가 있습니다.
GCC 인라인 변수 구현
다음과 같이 구현되는 방법을 관찰 할 수 있습니다.
nm main.o notmain.o
포함하는:
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
그리고 man nm
에 대해 말한다 u
:
"u"기호는 고유 한 글로벌 기호입니다. 이것은 ELF 심볼 바인딩의 표준 세트에 대한 GNU 확장입니다. 이러한 심볼의 경우 동적 링커는 전체 프로세스에서이 이름과 유형이 사용중인 심볼이 하나만 있는지 확인합니다.
이를위한 전용 ELF 확장이 있음을 알 수 있습니다.
C ++ 이전 17 : extern const
C ++ 17 이전과 C에서는를 사용하여 매우 유사한 효과를 얻을 수 있으며 extern const
,이 경우 단일 메모리 위치가 사용됩니다.
단점 inline
은 다음과 같습니다.
constexpr
이 기술로 변수를 만드는 것은 불가능하며 , 다음 만 inline
허용합니다. constexpr extern을 선언하는 방법?main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
notmain.cpp
#include "notmain.hpp"
const int notmain_i = 42;
const int* notmain_func() {
return ¬main_i;
}
notmain.hpp
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
extern const int notmain_i;
const int* notmain_func();
#endif
Pre-C ++ 17 헤더 전용 대안
이들은 extern
솔루션 만큼 좋지는 않지만 작동하며 단일 메모리 위치 만 차지합니다.
constexpr
때문에 기능, constexpr
의미inline
와 inline
수 (힘) 정의는 모든 번역 단위에 표시합니다 :
constexpr int shared_inline_constexpr() { return 42; }
괜찮은 컴파일러가 호출을 인라인 할 것이라고 장담합니다.
다음 과 같이 const
또는 constexpr
정적 변수를 사용할 수도 있습니다 .
#include <iostream>
struct MyClass {
static constexpr int i = 42;
};
int main() {
std::cout << MyClass::i << std::endl;
// undefined reference to `MyClass::i'
//std::cout << &MyClass::i << std::endl;
}
그러나 주소를 가져 오는 것과 같은 작업을 수행 할 수 없습니다. 그렇지 않으면 odr이 사용됩니다. 참조 : constexpr 정적 데이터 멤버 정의
씨
C에서 상황은 C ++ 이전 C ++ 17과 동일합니다. 다음 위치에 예제를 업로드했습니다. C에서 "정적"은 무엇을 의미합니까?
유일한 차이점은 C ++에서는 전역을 const
의미 static
하지만 C에서는 그렇지 않다는 것입니다 .`static const` 대`const`의 C ++ 의미 체계
완전히 인라인 할 방법이 있습니까?
TODO : 메모리를 전혀 사용하지 않고 변수를 완전히 인라인 할 수있는 방법이 있습니까?
전처리 기가하는 것과 매우 비슷합니다.
이것은 어떻게 든 필요합니다.
관련 :
Ubuntu 18.10, GCC 8.2.0에서 테스트되었습니다.
이것은 컴파일 모듈 (.cpp 파일)에서만 표시 / 접근 할 수있는 전역 상수입니다. 이 목적으로 정적을 사용하는 BTW는 더 이상 사용되지 않습니다. 익명 네임 스페이스와 열거 형을 사용하는 것이 좋습니다.
namespace
{
enum
{
foo = 42
};
}
enum
이 맥락에서 어떤 이점이 있는지 명확하지 않습니다 . 자세히 설명 하시겠습니까? 이러한는 enums
보통 (현대 컴파일러이 필요하지 않습니다하지만 값에 어떤 공간을 할당에서 컴파일러를 방지하기 위해 사용되는 enum
그것을 위해 해킹) 및 값에 대한 포인터의 생성을 방지 할 수 있습니다.
비공개로 설정하면 여전히 헤더에 표시됩니다. 나는 작동하는 "가장 약한"방식을 사용하는 경향이 있습니다. Scott Meyers의이 고전적인 기사를 참조하십시오 : http://www.ddj.com/cpp/184401197 (기능에 관한 것이지만 여기에서도 적용 할 수 있습니다).