programing

C와 C++에서 'constant static'은 무엇을 의미하는가?

prostudy 2022. 5. 16. 20:58
반응형

C와 C++에서 'constant static'은 무엇을 의미하는가?

const static int foo = 42;

StackOverflow의 코드에서 이걸 봤는데 그게 뭘 하는지 알 수가 없었어.그때 나는 다른 포럼에서 혼란스러운 대답을 보았다.내 추측으로는 C에서 상수를 숨기기 위해 사용하는 것 같아.foo다른 모듈에서.이거 맞는건가요?만약 그렇다면, 왜 C++ 컨텍스트에서 당신이 그냥 만들 수 있는 곳에서 그것을 사용하겠는가?private?

많은 사람들이 기본적인 답을 주었지만 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변하지 않는다는 뜻이야

그것은 C와 C++ 모두에서 사용된다.

짐작한 대로 그.static부분은 그 편찬 단위에 그 범위를 한정한다.정적 초기화도 제공한다. const일요일에 』 『아기도』 『 , 수 있다이 변수는 구조에 따라 데이터 또는 bss 세그먼트에 저장되며 읽기 전용으로 표시된 메모리에 있을 수 있다.

이 모든 것은 C가 이러한 변수들을 다루는 방법(또는 C++가 네임스페이스 변수를 처리하는 방법)이다.C++에서 멤버가 표시됨static주어진 클래스의 모든 인스턴스가 공유한다.비공개인지 아닌지는 한 변수가 여러 인스턴스에 의해 공유된다는 사실에 영향을 주지 않는다.하고 있다const어떤 코드가 그걸 수정하려 한다면 경고해줄거야

완전히 비공개인 경우 클래스의 각 인스턴스는 자체 버전을 얻을 것이다(최적화기는 그럼에도 불구하고).

작은 공간 최적화 입니다.

라고 말할 때

const int foo = 42;

상수를 정의하는 것이 아니라 읽기 전용 변수를 만드는 것이다.컴파일러는 foo를 볼 때마다 42개를 사용할 정도로 똑똑하지만, 이를 위해 초기화된 데이터 영역에 공간을 할당하기도 한다.이것은 정의된 대로 foo가 외부 연계를 가지기 때문에 이루어진다.다른 컴파일 유닛은 다음과 같이 말할 수 있다.

foo 내 건물 외부;

가치에 접근하기 위해.저 컴파일 유닛은 foo의 가치가 무엇인지 전혀 모르기 때문에 그것은 좋은 연습이 아니다.그것은 단지 그것이 항상 그렇다는 것을 알고 있고 그것이 사용될 때마다 기억에서 값을 다시 불러와야 한다.

이제, 정적임을 선언함으로써:

static const int foo = 42;

컴파일러는 평소의 최적화를 할 수 있지만, "이봐, 이 컴파일 유닛 밖에서는 아무도 foo를 볼 수 없고, 나는 그것이 항상 42라는 것을 알고 있기 때문에 그것을 위해 어떤 공간도 할당할 필요가 없다."라고 말할 수 있다.

또한 C++에서 이름이 현재 컴파일 단위에서 빠져나가지 못하도록 하는 선호되는 방법은 익명 네임스페이스를 사용하는 것이라는 점에 유의해야 한다.

namespace {
    const int foo = 42; // same as static definition above
}

C++17inline변수들

만약 당신이 "C++ constant static"을 검색했다면, 이것은 당신이 정말로 사용하고 싶은 C++17 인라인 변수일 가능성이 매우 높다.

이 놀라운 C++17 기능을 통해 다음을 실현하십시오.

  • 각 상수에 대해 하나의 메모리 주소만 편리하게 사용
  • 그것을 a로 저장하다.constexpr: Constexpr 외부 신고 방법?
  • 한 머리글에서 한 줄로 하다.

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);
}

주체가 아닌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;
}

컴파일 및 실행:

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다음을 허용한다.외부 신고는 어떻게 하지?
  • 헤더와 cpp 파일에서 변수를 별도로 선언하고 정의해야 하므로 품위가 떨어진다.

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.cpp

#include "notmain.hpp"

const int notmain_i = 42;

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

주체가 아닌hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

extern const int notmain_i;

const int* notmain_func();

#endif

기트허브 상류.

C++17 이전 헤더만 사용할 수 있음

이것들이 더 좋은 것 같지는 않다.extern솔루션, 그러나 단일 메모리 위치만 차지하며,

A constexpr함수를 의미하기 때문에inline 모든 번역 장치에 정의가 나타나도록 허용(수정):

constexpr int shared_inline_constexpr() { return 42; }

제대로 된 컴파일러라면 호출에 줄을 서겠지

또한 a를 사용할 수 있다.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;
}

그러나 주소를 가져가는 등의 작업을 수행할 수 없으며, 그렇지 않으면 더 이상 사용되지 않게 된다. 자세한 내용은 다음을 참조하십시오.constexpr 정적 데이터 멤버 정의

C

C에서 상황은 C++ 사전 C++ 17과 동일하며, 예를 업로드 했다: C에서 "정적"의미하는 것은 무엇인가?

유일한 차이점은 C++에서const함축적으로 말하다static지구본의 경우 정전기 대 정전기(static constitution)의 C++ 의미론에서는 그렇지 않다.

완전히 인라인으로 할 수 있는 방법은?

TODO: 메모리를 전혀 사용하지 않고 변수를 완전히 인라인으로 연결할 수 있는 방법이 없을까?

전처리가 하는 것과 비슷해

이 작업에는 어떻게든 다음과 같은 사항이 필요하다.

  • 변수의 주소를 사용하는지 여부를 탐지하거나 금지
  • 이 정보를 ELF 개체 파일에 추가하고 LTO가 이를 최적화하도록 하십시오.

관련:

Ubuntu 18.10, GCC 8.2.0에서 시험하였다.

그 코드 라인은 실제로 몇 가지 다른 맥락에서 나타날 수 있으며, 거의 동일하게 작용하지만, 작은 차이점이 있다.

네임스페이스 범위

// 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암묵적으로 정적이다.가 '이++라면 C++라면'을 const staticODR의 중단을 방지하기 위한 키워드.

클래스 범위

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

'int'를 놓치고 있다.다음과 같아야 한다.

const static int foo = 42;

C와 C++에서는 42의 로컬 파일 범위인 정수 상수를 선언한다.

왜 42? 만약 당신이 이미 알지 못한다면(그리고 믿지 않는다는 것이 믿기 어렵다면), 그것은 생명, 우주, 모든 것에 대한 해답을 가리키는 말이다.

C99/GNU99 사양에 따라:

  • static

    • 스토리지 클래스 지정자임

    • 기본적으로 파일 수준 범위의 개체에는 외부 링크가 있음

    • 정적 지정자가 있는 파일 수준 범위의 개체에는 내부 링크가 있음
  • const

    • 형식 지정(유형의 일부)

    • 즉시 왼쪽 인스턴스에 적용된 키워드 - 즉,

      • MyObj const * myVar;- 항상 정규화된 개체 유형에 대한 부적격 포인터

      • MyObj * const myVar;- 부적격 개체 유형에 대한 항상 정규화된 포인터

    • 가장 왼쪽 사용 - 변수가 아닌 개체 유형에 적용됨

      • const MyObj * myVar;- 항상 정규화된 개체 유형에 대한 부적격 포인터

따라서:

static NSString * const myVar;- 내부 링크가 있는 불변 문자열의 고정 포인터.

static키워드는 변수 이름을 전역으로 만들고 응용프로그램 내에서 이름 충돌로 이어질 수 있다.

모든 훌륭한 답변에 다음과 같은 작은 세부 사항을 추가하고자 한다.

플러그 인(예: CAD 시스템에서 로드할 DLL 또는 .so 라이브러리)을 작성하는 경우, 정적(static)은 다음과 같은 이름 충돌을 방지하는 수명 보호기입니다.

  1. CAD 시스템은 "const int foo = 42;"가 들어 있는 플러그인 A를 로드한다.
  2. 시스템은 "const int foo = 23;"가 들어 있는 플러그인 B를 로드한다.
  3. 결과적으로 플러그인 로더가 실현되기 때문에 플러그인 B는 foo에 42 값을 사용할 것이다. 외부 링크가 있는 "foo"가 이미 존재한다는 것을.

설상가상으로:3단계는 컴파일러 최적화, 플러그인 로드 메커니즘 등에 따라 다르게 동작할 수 있다.

나는 이 문제를 두 개의 플러그인에 있는 두 개의 도우미 기능(동일한 이름, 다른 동작)으로 해결한 적이 있다.그들에게 정적인 선언을 한 것이 문제를 해결했다.

C++에서는

static const int foo = 42;

상수를 정의하고 사용하는 선호하는 방법이다.즉, 이것보다 이것을 사용한다.

#define foo 42

왜냐하면 그것은 형식 안전 시스템을 전복시키지 않기 때문이다.

그래, 모듈 내 변수를 다른 모듈에서 숨긴다.C++에서는 다른 파일의 불필요한 재구축을 트리거하는 .h 파일을 변경하거나 변경할 필요가 없을 때 사용한다.그리고 정전기 먼저 넣었어.

static const int foo = 42;

또한, 컴파일러의 용도에 따라 컴파일러는 스토리지 할당도 하지 않고 단순히 사용 위치 값을 "인라인"으로 지정한다.정적이 없으면 컴파일러는 그것이 다른 곳에서 사용되고 있지 않다고 가정할 수 없고 인라인으로도 사용할 수 없다.

컴파일 모듈(.cpp 파일)에서만 액세스 가능한 글로벌 상수 표시/접근 가능.이 목적을 위해 정적을 사용하는 BTW는 더 이상 사용되지 않는다.익명 네임스페이스와 열거형을 더 잘 사용하십시오.

namespace
{
  enum
  {
     foo = 42
  };
}

비밀로 만든다는 것은 여전히 머리글에 나타난다는 것을 의미할 것이다.나는 "가장 약한" 방법을 사용하는 경향이 있다.Scott Meyers: http://www.ddj.com/cpp/184401197의 고전 기사를 참조하십시오.

참조URL: https://stackoverflow.com/questions/177437/what-does-const-static-mean-in-c-and-c

반응형