development

우리는 얼마나 많은 레벨의 포인터를 가질 수 있습니까?

big-blog 2020. 2. 18. 22:50
반응형

우리는 얼마나 많은 레벨의 포인터를 가질 수 있습니까?


*단일 변수에 몇 개의 포인터 ( )가 허용됩니까?

다음 예제를 살펴 보겠습니다.

int a = 10;
int *p = &a;

마찬가지로 우리는 할 수 있습니다

int **q = &p;
int ***r = &q;

등등.

예를 들어

int ****************zz;

C표준 지정 하한 :

번역 한계

276 구현은 다음 한계 중 하나 이상의 인스턴스를 하나 이상 포함하는 하나 이상의 프로그램을 번역하고 실행할 수 있어야한다. [...]

279 — 12 선언에서 산술, 구조, 공용체 또는 공백 유형을 수정하는 포인터, 배열 및 함수 선언자 (모든 조합)

상한은 구현에 따라 다릅니다.


실제로, C 프로그램은 일반적으로 무한 포인터 간접을 사용합니다. 하나 또는 두 개의 정적 레벨이 일반적입니다. 트리플 간접 지향은 드물다. 그러나 무한은 매우 일반적입니다.

무한 포인터 간접 지시는 물론 직접적인 선언자가 아닌 구조체의 도움으로 얻을 수 있으며 불가능합니다. 그리고이 구조에 다른 데이터를 포함 할 수있는 다른 레벨을 포함 할 수 있도록 구조체가 필요합니다.

struct list { struct list *next; ... };

지금 당신은 가질 수 있습니다 list->next->next->next->...->next. 이것은 실제로 여러 포인터 간접 참조 *(*(..(*(*(*list).next).next).next...).next).next입니다. 그리고 .next이것은 구조의 첫 번째 구성원 일 때 기본적으로 noop입니다. 따라서 이것을로 상상할 수 있습니다 ***..***ptr.

링크는 이와 같은 거대한 표현이 아닌 루프로 순회 할 수 있으며 구조를 쉽게 원형으로 만들 수 있기 때문에 이에 제한이 없습니다.

다시 말해, 링크 된 목록은 문제를 해결하기 위해 다른 수준의 간접 성을 추가하는 궁극적 인 예일 수 있습니다. 모든 푸시 작업에서 동적으로 수행하기 때문입니다. :)


이론적으로 :

원하는만큼의 간접적 인 수준을 가질 수 있습니다.

거의:

물론 메모리를 소비하는 것은 무기 한일 수 없으며 호스트 환경에서 사용 가능한 리소스로 인해 제한이 있습니다. 따라서 실제로 구현이 지원할 수있는 것에는 최대 한계가 있으며 구현시이를 적절하게 문서화해야합니다. 따라서 이러한 모든 아티팩트에서 표준은 최대 한계를 지정하지 않지만 하한을 지정합니다.

참조는 다음과 같습니다.

C99 표준 5.2.4.1 번역 한계 :

— 12 선언, 산술, 구조, 공용체 또는 공극 유형을 수정하는 포인터, 배열 및 함수 선언자 (모든 조합).

이것은 모든 구현 지원 해야하는 하한을 지정합니다 . 각주에서 표준은 다음과 같이 말합니다.

18) 구현시 가능한 한 고정 된 번역 제한을 적용하지 않아야합니다.


사람들이 말했듯이, "이론적으로"제한은 없습니다. 그러나 흥미롭게도 g ++ 4.1.2로 이것을 실행했으며 최대 20,000 크기로 작동했습니다. 컴파일은 꽤 느렸으므로 더 높이려고하지 않았습니다. 따라서 g ++도 제한을 두지 않는다고 생각합니다. ( size = 10ptr.cpp가 즉시 명확하지 않으면 설정 하고 살펴 보십시오 .)

g++ create.cpp -o create ; ./create > ptr.cpp ; g++ ptr.cpp -o ptr ; ./ptr

create.cpp

#include <iostream>

int main()
{
    const int size = 200;
    std::cout << "#include <iostream>\n\n";
    std::cout << "int main()\n{\n";
    std::cout << "    int i0 = " << size << ";";
    for (int i = 1; i < size; ++i)
    {
        std::cout << "    int ";
        for (int j = 0; j < i; ++j) std::cout << "*";
        std::cout << " i" << i << " = &i" << i-1 << ";\n";
    }
    std::cout << "    std::cout << ";
    for (int i = 1; i < size; ++i) std::cout << "*";
    std::cout << "i" << size-1 << " << \"\\n\";\n";
    std::cout << "    return 0;\n}\n";
    return 0;
}

확인하기 재미있다.

  • Visual Studio 2010 (Windows 7)에서이 오류가 발생하기 전에 1011 수준을 가질 수 있습니다.

    치명적인 오류 C1026 : 파서 ​​스택 오버플로, 프로그램이 너무 복잡

  • *충돌없이 gcc (우분투), 100k + ! 하드웨어가 한계라고 생각합니다.

(변수 선언만으로 테스트)


제한이 없습니다 . 여기에서 예제를 확인 하십시오 .

대답은 "포인터 수준"의 의미에 따라 다릅니다. "한 번의 선언으로 몇 개의 간접 수준을 가질 수 있습니까?" 답은 "최소한 12"입니다.

int i = 0;

int *ip01 = & i;

int **ip02 = & ip01;

int ***ip03 = & ip02;

int ****ip04 = & ip03;

int *****ip05 = & ip04;

int ******ip06 = & ip05;

int *******ip07 = & ip06;

int ********ip08 = & ip07;

int *********ip09 = & ip08;

int **********ip10 = & ip09;

int ***********ip11 = & ip10;

int ************ip12 = & ip11;

************ip12 = 1; /* i = 1 */

"프로그램을 읽기가 어려워지기 전에 얼마나 많은 수준의 포인터를 사용할 수 있는지"를 의미한다면 그것은 맛의 문제이지만 한계가 있습니다. 간접적 인 두 수준의 수준 (무엇에 대한 포인터)이있는 것이 일반적입니다. 그 이상은 쉽게 생각하기가 조금 더 어려워집니다. 대안이 더 나 빠지지 않는 한 그렇게하지 마십시오.

"런타임에 얼마나 많은 수준의 포인터 간접 설정을 가질 수 있는지"라는 의미라면 제한이 없습니다. 이 점은 각 노드가 다음 노드를 가리키는 순환 목록에 특히 중요합니다. 프로그램은 포인터를 영원히 따라갈 수 있습니다.


실제로 함수에 대한 포인터로 더 재미 있습니다.

#include <cstdio>

typedef void (*FuncType)();

static void Print() { std::printf("%s", "Hello, World!\n"); }

int main() {
  FuncType const ft = &Print;
  ft();
  (*ft)();
  (**ft)();
  /* ... */
}

여기에 설명 된대로 이것은 다음을 제공합니다.

안녕, 월드!
안녕, 월드!
안녕, 월드!

그리고 런타임 오버 헤드가 필요하지 않으므로 컴파일러가 파일을 질식시킬 때까지 원하는만큼 스택 할 수 있습니다.


제한없습니다 . 포인터는 내용이 주소 인 메모리 덩어리입니다.
당신이 말했듯이

int a = 10;
int *p = &a;

포인터에 대한 포인터는 다른 포인터의 주소를 포함하는 변수이기도합니다.

int **q = &p;

다음 q은 주소를 보유하고있는 포인터를 가리키는 포인터 p입니다 a.

포인터에 대한 포인터에는 특별한 것이 없습니다.
따라서 다른 포인터의 주소를 보유하는 poniters 체인에는 제한이 없습니다.
즉.

 int **************************************************************************z;

허용됩니다.


여기에는 두 가지 가능한 질문이 있습니다. C 유형에서 달성 할 수있는 포인터 간접 수준의 수준과 단일 선언자에 넣을 수있는 포인터 간접 수준의 수준.

C 표준은 전자에 최대가 부과되도록 허용합니다 (최소한 값 제공). 그러나 여러 typedef 선언을 통해 우회 할 수 있습니다.

typedef int *type0;
typedef type0 *type1;
typedef type1 *type2; /* etc */

따라서 궁극적으로 이것은 거부되기 전에 C 프로그램을 얼마나 크게 / 복잡하게 만들 수 있는지에 관한 구현 문제이며, 이는 컴파일러마다 매우 다릅니다.


모든 C ++ 개발자는 유명한 3 성 프로그래머 에 대해 들어야 합니다.

그리고 위장해야 할 마법의 "포인터 장벽"이있는 것 같습니다.

C2에서 인용 :

3 성 프로그래머

C 프로그래머를위한 등급 시스템. 포인터가 간접적 일수록 (즉, 변수 앞에 "*"가 많을수록) 평판이 높아집니다. 사실상 모든 비 사소한 프로그램은 포인터를 사용해야하므로 별이없는 C 프로그래머는 거의 존재하지 않습니다. 대부분은 1 성급 프로그래머입니다. 예전에는 (어릴 때 나는 젊었 기 때문에 이것들은 나에게는 예전의 것처럼 보였습니다), 때로는 3 성급 프로그래머가 수행 한 코드 조각을 찾아 경외감에 떨립니다. 어떤 사람들은 하나 이상의 간접적 인 수준에서 함수 포인터가 포함 된 3 성 코드를 보았다고 주장하기도했습니다. 나에게 UFO만큼이나 들렸다.


임의의 수의 *로 유형을 생성하는 것은 템플릿 메타 프로그래밍으로 발생할 수있는 것임을 지적하고 싶습니다. 나는 내가 정확히하고있는 일을 잊었지만 재귀 T * 유형 을 사용하여 그들 사이에 어떤 종류의 메타 조작이있는 새로운 고유 유형을 생성 할 수 있다고 제안했습니다 .

템플릿 메타 프로그래밍은 광기의 속도가 느리기 때문에 수천 수준의 간접적 인 유형을 생성 할 때 변명 할 필요가 없습니다. 예를 들어, 기능 언어로서 템플릿 확장에 peano 정수를 매핑하는 편리한 방법입니다.


2004 MISRA C 표준 규칙 17.52 단계 이상의 포인터 간접 지정을 금지합니다.


실제 한계 와 같은 것은 없지만 한계가 있습니다. 모든 포인터는 일반적으로 힙이 아닌 스택에 저장되는 변수입니다 . 스택은 일반적으로 작습니다 (일부 연결 중에 크기를 변경할 수 있음). 따라서 4MB 스택이 있다고 가정 해 봅시다. 정상 크기입니다. 그리고 4 바이트 크기의 포인터가 있다고 가정 해 봅시다 (포인터 크기는 아키텍처, 대상 및 컴파일러 설정에 따라 다릅니다).

이 경우 4 MB / 4 b = 1024가능한 최대 수는 1048576이지만 다른 것들이 스택되어 있다는 사실을 무시해서는 안됩니다.

그러나 일부 컴파일러에는 최대 개수의 포인터 체인이있을 수 있지만 한계는 스택 크기입니다. 따라서 무한대와 연결하는 동안 스택 크기를 늘리고 해당 메모리를 처리하는 OS를 실행하는 무한대 메모리가있는 기계를 사용하면 포인터 체인이 무제한이됩니다.

당신이 사용하는 경우 int *ptr = new int;와 그렇지 않다 힙에 포인터 넣어 일반적인 방법으로 제한 힙 크기가 아닌 스택 될 것입니다.

편집 그냥 깨달으십시오 infinity / 2 = infinity. 기계에 메모리가 더 있으면 포인터 크기가 증가합니다. 따라서 메모리가 무한대이고 포인터의 크기가 무한대이면 나쁜 소식입니다 ... :)


포인터를 저장하는 위치에 따라 다릅니다. 그것들이 스택에 있다면 당신은 상당히 낮은 한계 를 가지고 있습니다 . 힙에 저장하면 제한이 훨씬 높습니다.

이 프로그램을보십시오 :

#include <iostream>

const int CBlockSize = 1048576;

int main() 
{
    int number = 0;
    int** ptr = new int*[CBlockSize];

    ptr[0] = &number;

    for (int i = 1; i < CBlockSize; ++i)
        ptr[i] = reinterpret_cast<int *> (&ptr[i - 1]);

    for (int i = CBlockSize-1; i >= 0; --i)
        std::cout << i << " " << (int)ptr[i] << "->" << *ptr[i] << std::endl;

    return 0;
}

그것은 1M 포인터를 생성하고 체인에서 첫 번째 변수로가는 것이 무엇인지 쉽게 알 수있는 지점을 보여줍니다 number.

BTW. 92KRAM을 사용 하므로 얼마나 깊이 갈 수 있는지 상상하십시오.

참고 URL : https://stackoverflow.com/questions/10087113/how-many-levels-of-pointers-can-we-have



도와주세요.
반응형