IT이야기

포인터에 대한 포인터와 일반 포인터

cyworld 2022. 6. 10. 21:32
반응형

포인터에 대한 포인터와 일반 포인터

포인터의 목적은 특정 변수의 주소를 저장하는 것입니다.다음으로 다음 코드의 메모리 구조는 다음과 같습니다.

int a = 5;
int *b = &a;

......값 ........모리주 ............ .................. 。
a... 0x000002 ....................
... 0x000010 ............0x000002

그래, 좋아.그런 다음 포인터 *b의 주소를 저장한다고 가정합니다.다음으로 일반적으로 더블 포인터 **c를 다음과 같이 정의합니다.

int a = 5;
int *b = &a;
int **c = &b;

다음으로 메모리 구조는 다음과 같습니다.

......값 ........모리주 ............ .................. 。
a... 0x000002 ....................
b... 0x000010 ............0x000002
... 0x000020 ............0x000010

그래서 **c는 *b의 주소를 말합니다.

제 질문은, 왜 이런 종류의 코드가

int a = 5;
int *b = &a;
int *c = &b;

경고를 발생시킬까요?

포인터의 목적이 단지 메모리 주소의 보존이라면, 보존하는 주소가 변수, 포인터, 더블 포인터등을 참조하고 있는 경우는 계층이 존재하지 않는 것이 좋다고 생각하기 때문에, 이하의 타입의 코드가 유효합니다.

int a = 5;
int *b = &a;
int *c = &b;
int *d = &c;
int *e = &d;
int *f = &e;

int a = 5;
int *b = &a;   
int *c = &b;

가 뜨는 는 '경고'가 있기 때문입니다.&b은 「」입니다.int ** 이 때, 이 유형의 int * 이 두 유형 사이에는 암묵적인 변환이 없으므로 경고가 발생합니다.

보다 긴, 「 」를하려고 하면, 「 」가 됩니다.f컴파일러가 우리에게 줄 것이다.int더 이상 참조할 수 있는 포인터가 아닙니다.

, 많은 에서는, 「」가 되는 해 주세요.int ★★★★★★★★★★★★★★★★★」int*, 는 64비트일 수 있습니다).int32개).「」를 참조 해제했을 .f을 받다int값이 반감되면 유효한 포인터에 던질 수도 없습니다.

포인터의 목적이 단순히 메모리 주소를 저장하는 것이라면, 저장할 주소가 변수, 포인터, 더블 포인터 등을 참조한다면 계층은 없어야 한다고 생각합니다.기타

실행 시 포인터는 주소만 유지합니다.그러나 컴파일 시에는 모든 변수와 관련된 유형도 있습니다.int* ★★★★★★★★★★★★★★★★★」int**는 호환되지 않는 두 가지 유형입니다.

한 가지 이 있어요.void*이데올로기 때문에주소만 저장되므로 임의의 주소를 할당할 수 있습니다.

int a = 5;
int *b = &a;
void *c = &b;

, 하고 경우void* '' 유형 정보를 직접 입력해야 합니다.

int a2 = **((int**)c);

제 질문은, 왜 이런 종류의 코드가

int a = 5; 
int *b = &a; 
int *c = &b; 

경고를 발생시킬까요?

당신은 다시 원리로 돌아가야 합니다.

  • 변수에는 유형이 있습니다.
  • 변수 고정 값
  • 포인터는 값이다.
  • 포인터는 변수를 참조한다
  • p.*p입니다.
  • v입니다.&v입니다.

그리고 이제 우리는 당신의 게시물에 있는 모든 실수를 찾을 수 있습니다.

포인터의 .*b

안 돼요.*b인트라포인터가 아닙니다. b이 포인터인 변수입니다. *b값은 정수 변수입니다.

**c를 .*b.

아뇨, 아뇨 절대 아니에요포인터를 이해하려면 이것을 정확하게 이해해야 합니다.

*b변수입니다.입니다.이치노a .a입니다.b**c에는, ,, 의, does, does, , 。a오히려 변수의 별칭변수입니다.a...)*b . )

올바른 스테이트먼트는 다음과 같습니다.value of variablec주소입니다.b""의c입니다.b.

걸걸어 떻???리로돌돌 돌돌돌다다이 것은 ★★★★★★★★★★★★★★★★★★★★」c = &b 럼,, 치는 of of of of of의 가치는 c★★★★★★★★★★★★★★★★★★?b

기본 규칙을 완전히 이해하도록 하세요.

이제 변수와 포인터의 올바른 관계를 이해하셨으면 합니다.코드에 오류가 발생하는 이유에 대한 질문에 답할 수 있을 것입니다.

올바른 경고를 받고 코드를 컴파일하려면 C 유형 시스템이 필요합니다.한 수준의 포인터만 있으면 포인터가 포인터를 가리키는지 실제 정수를 가리키는지 알 수 없습니다.

int**은 '아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 네.int*로 "Dereference"를하면 "Dereference"가 됩니다.int*은 「」입니다.int당신의 제안대로라면 유형이 애매할 것입니다.

당신의 예를 들어 보면, 그것이 사실인지 아닌지는 알 수 없다.cint ★★★★★★★★★★★★★★★★★」int*:

c = rand() % 2 == 0 ? &a : &b;

c는 어떤 타입을 가리키고 있습니까?컴파일러는 그것을 모르기 때문에 다음 행을 실행할 수 없습니다.

*c;

C에서는 컴파일 시 모든 유형이 체크되어 더 이상 필요하지 않으므로 컴파일 후 모든 유형 정보가 손실됩니다.모든 포인터가 포인터에 포함된 유형에 대한 추가 런타임 정보를 가져야 하므로 제안서는 실제로 메모리와 시간을 낭비합니다.

포인터는 추가 타입 시멘틱스가 있는 메모리주소의 추상화이며, C 타입이 중요한 언어입니다.

,int * ★★★★★★★★★★★★★★★★★」int **(일본어판/영어)

둘째, 포인터 산술은 유형이 중요합니다.p입입 of T *식 , 。p + 1타입의 다음 오브젝트의 주소를 나타냅니다.T하다

char  *cp     = 0x1000;
short *sp     = 0x1000;  // assume 16-bit short
int   *ip     = 0x1000;  // assume 32-bit int
long  *lp     = 0x1000;  // assume 64-bit long

.cp + 1를 알려 .char 「」, 「」)0x1001.표현sp + 1를 알려 .short 「」, 「」)0x1002ip + 1 에게 주다0x1004 , , , , 입니다.lp + 1 에게 주다0x1008

그래서, 주어진다면...

int a = 5;
int *b = &a;
int **c = &b;

b + 1를 알려 .int , , , , 입니다.c + 1에 대한 다음 포인터의 주소를 우리에게 알려준다.int

포인터 타입의 파라미터에 함수를 기입하려면 포인터 투 포인터가 필요합니다.다음 코드를 사용합니다.

void foo( T *p )    
{
  *p = new_value(); // write new value to whatever p points to
}

void bar( void )
{
  T val;
  foo( &val );     // update contents of val
}

이는 모든 유형에 해당됩니다.T 타입의 「」를 .P * 코드가 '비밀번호가 되다'가 됩니다

void foo( P **p )    
{
  *p = new_value(); // write new value to whatever p points to
}

void bar( void )
{
  P *val;
  foo( &val );     // update contents of val
}

시멘틱스는 완전히 동일하며 타입만 다를 뿐 형식 파라미터입니다.p보다 한 더 입니다.val

저장할 주소가 변수, 포인터, 더블 포인터를 참조하면 계층이 없어야 한다고 생각합니다.

계층 구조가 없다면 경고 없이 UB를 생성하는 것은 매우 쉬울 것입니다.그것은 끔찍할 것입니다.

다음 사항을 고려하십시오.

char c = 'a';
char* pc = &c;
char** ppc = &pc;
printf("%c\n", **ppc);   // compiles ok and is valid
printf("%c\n", **pc);    // error: invalid type argument of unary ‘*’

컴파일러에 의해 에러가 표시되기 때문에, 내가 잘못하고 있는 것을 알 수 있어 버그를 수정할 수 있습니다.

하지만 '계층제'가 없다면, 다음과 같은 것이 있습니다.

char c = 'a';
char* pc = &c;
char* ppc = &pc;
printf("%c\n", **ppc);   // compiles ok and is valid
printf("%c\n", **pc);    // compiles ok but is invalid

"계층 구조"가 없기 때문에 컴파일러는 어떠한 오류도 줄 수 없습니다.

하지만 선이:

printf("%c\n", **pc);

UB(정의되지 않은 동작)가 실행됩니다.

번째 번째*pcchar예를 들어, 1바이트만 예약했는데도 4 또는 8바이트를 읽을 수 있습니다.UB로 하다

위의 UB로 인해 프로그램이 크래쉬하지 않고 일부 garish 값만 반환한 경우 두 번째 단계는 garish 값을 참조하는 것입니다.다시 한 번 UB.

결론

타입 시스템은 int*, int**, int*** 등을 다른 타입으로 보고 버그를 검출하는 데 도움이 됩니다.

포인터의 목적이 단순히 메모리 주소를 저장하는 것이라면, 저장하는 주소가 변수, 포인터, 더블 포인터 등을 참조하고 있기 때문에, 아래의 코드 타입은 유효하다고 생각합니다.

여기 당신의 오해가 있다고 생각합니다.포인터의 목적은 메모리 주소를 저장하는 것이지만, 포인터에는 보통 타입이 있기 때문에 포인터가 가리키는 장소에서 무엇을 기대할 수 있는지 알 수 있습니다.

특히, 당신과 달리, 다른 사람들은 포인터가 가리키는 메모리 내용을 어떻게 해야 하는지 알 수 있도록 이러한 계층을 가지고 싶어합니다.

C의 포인터 시스템에 타입 정보가 첨부되어 있는 것이 포인트입니다.

네가 한다면.

int a = 5;

&a는, 「」가 것을 있습니다.int *하면 '해당'이 .int

다음 단계로 넘어가면

int *b = &a;
int **c = &b;

&b포인터이기도 합니다.나타나기 포인터를 역참조하면 원래 타입이 나타납니다.*(&b)는 입니다.int * , , , , 입니다.**(&b) 원조입니다.int츠미야

는 안 든지 '계급이 없다'로 할 수 .void *다만, 직접적인 조작성은 매우 제한적입니다.

포인터의 목적이 단순히 메모리 주소를 저장하는 것이라면, 저장하는 주소가 변수, 포인터, 더블 포인터 등을 참조하고 있기 때문에, 아래의 코드 타입은 유효하다고 생각합니다.

기계도 마찬가지입니다(결국 모든 것은 숫자입니다).그러나 많은 언어에서 변수가 입력되어 컴파일러가 사용자가 변수를 올바르게 사용할 수 있음을 확인할 수 있습니다(타입은 변수에 올바른 컨텍스트를 부여합니다).

포인터 및 포인터(아마도)가 같은 양의 메모리를 사용하여 값을 저장하는 것은 사실입니다(int 및 int에 대한 포인터의 경우 이는 사실이 아니며 주소의 크기는 주택의 크기와 관련이 없습니다).

따라서 단순한 포인터로서 포인터에 액세스 하면, int 의 주소를 조작할 수 있습니다(다른 것은 없고, int 의 주소를 치환해 위험을 확인할 수 있습니다).당신은 이 모든 것이 숫자이기 때문에 혼란스러울지도 모르지만, 일상생활에서는 그렇지 않다: 나는 개인적으로 개 1달러와 개 1달러는 큰 차이를 만든다. 개와 개 1달러는 유형이다. 당신은 그들과 함께 무엇을 할 수 있는지 알고 있다.

조립하여 원하는 것을 만들 수 있지만, 얼마나 위험한지 알게 될 것입니다. 왜냐하면 당신은 당신이 원하는 거의 모든 것, 특히 이상한 것들을 할 수 있기 때문입니다.네, 주소 값을 변경하는 것은 위험합니다.예를 들어, 1200 메모리 거리(주소)로 표시된 주소에서 무언가를 전달할 필요가 있는 Autonomous Car가 있다고 가정하고, 거리 주택이 100피트(121은 유효하지 않은 주소)로 분리되어 있다고 가정합니다.만약 당신이 정수로써 원하는 대로 주소를 조작할 수 있다면, 당신은 다음과 같이 할 수 있습니다.1223에 전달하여 패킷이 포장도로 한가운데에 있도록 합니다.

또 다른 예로는 집, 집 주소, 주소록의 엔트리 번호 등이 있습니다.이 세 가지는 모두 다른 개념, 다른 유형입니다.

종류도 다양합니다.여기에는 그럴 만한 이유가 있습니다.

가지고 있는 것...

int a = 5;
int *b = &a;
int **c = &b;

…라는 표현

*b * 5

…은 유효하지만, 그 표현은 …입니다.

*c * 5

말이 되지 않는.

중요한 것은 포인터나 포인터를 어떻게 저장하느냐아니라 그들이 무엇을 언급하느냐이다.

C언어는 강하게 입력되어 있습니다.이것은, 모든 주소에 대해서, 그 주소의 값을 해석하는 방법을 컴파일러에게 알려주는 타입이 있는 것을 의미합니다.

이 예에서는 다음과 같습니다.

int a = 5;
int *b = &a;

aint 의 " " " "bint *로 읽다'로.int에는 :)가 포함되어 . 예를 들어 메모리에는 다음과 같은 것이 포함되어 있습니다.

..... memory address ...... value ........ type
a ... 0x00000002 .......... 5 ............ int
b ... 0x00000010 .......... 0x00000002 ... int*

타입은 실제로 메모리에 저장되는 것이 아니라 컴파일러가 읽을 때 알고 있을 뿐입니다.a 게 있어요.int 이 때,b라고 하는 것은 '주소지'를 수 int.

두 번째 예시는 다음과 같습니다.

int a = 5;
int *b = &a;
int **c = &b;

cint **투 포인터, 포인터 투 포인터, 포인터 투 포인터, 포인터 투 포인터, 포인터 투 포인터, 포인터 투 포인터, 포인터 투 포인터, int 를 합니다. 컴파일러의 경우 다음과 같습니다.

  • c는 포인터입니다.
  • 읽을 때c, 다른 포인터의 주소를 취득합니다.
  • 당신이 다른 포인터를 읽을 때, 당신은 그 포인터의 주소를 알 수 있습니다.int.

그것은,

  • c는 포인터입니다(int **);
  • *c는 포인터이기도 합니다(int *);
  • **c는 입니다.int.

메모리에는 다음이 포함됩니다.

..... memory address ...... value ........ type
a ... 0x00000002 .......... 5 ............ int
b ... 0x00000010 .......... 0x00000002 ... int*
c ... 0x00000020 .......... 0x00000010 ... int**

"type"은 값과 함께 저장되지 않으며 포인터는 어떤 메모리주소를 가리킬 수 있기 때문에 컴파일러는 기본적으로 포인터의 타입을 선택하여 오른쪽 끝의 타입을 삭제합니다.*.


참고로 일반적인 32비트 아키텍처용입니다.대부분의 64비트 아키텍처에서는 다음과 같은 이점을 얻을 수 있습니다.

..... memory address .............. value ................ type
a ... 0x0000000000000002 .......... 5 .................... int
b ... 0x0000000000000010 .......... 0x0000000000000002 ... int*
c ... 0x0000000000000020 .......... 0x0000000000000010 ... int**

주소는 각각8 바이트가 되었습니다만,int아직 4바이트밖에 안 돼요컴파일러는 각 변수의 유형을 알고 있기 때문에 이 차이를 쉽게 처리할 수 있으며 포인터의 경우 8바이트, 포인터의 경우 4바이트를 읽을 수 있습니다.int.

이 유형의 코드가 경고를 생성하는 이유는 무엇입니까?

int a = 5;
int *b = &a;   
int *c = &b;

&연산자는 오브젝트에 대한 포인터를 생성합니다.&a종류int *따라서 (초기화를 통해) 할당한다.b그것 역시 종류이다.int *유효합니다. &b오브젝트에 대한 포인터를 생성합니다.b,그것은&b에 대한 포인터 유형입니다.int *,예.,int **.

C는 (초기화를 위해 유지되는) 할당 연산자의 제약조건에서 (C11, 6.5.16.1p1) "양쪽 오퍼랜드가 호환 가능한 유형의 정규 버전 또는 정규화되지 않은 버전에 대한 포인터"라고 말합니다.그러나 C의 정의에서는 호환성이 있는 타입은int **그리고.int *호환되지 않는 유형입니다.

즉, 에 제약 위반이 있습니다.int *c = &b;initialization(초기화). 즉 컴파일러에 진단이 필요합니다.

여기서 규칙의 근거 중 하나는 두 가지 다른 포인터 유형이 동일한 크기라는 보장이 기준서에 없다는 것이다(단,void *및 문자 포인터 유형) 즉,sizeof (int *)그리고.sizeof (int **)값은 다를 수 있습니다.

그 이유는 어떤 포인터든T*실제로 같은 종류이다pointer to a T(또는address of a T), 여기서T가 가리키는 타입입니다.이 경우,*라고 읽을 수 있다pointer to a(n),그리고.T가 가리키는 타입입니다.

int     x; // Holds an integer.
           // Is type "int".
           // Not a pointer; T is nonexistent.
int   *px; // Holds the address of an integer.
           // Is type "pointer to an int".
           // T is: int
int **pxx; // Holds the address of a pointer to an integer.
           // Is type "pointer to a pointer to an int".
           // T is: int*

이것은 비참조 목적으로 사용되며, 여기서 참조 해제 오퍼레이터는 다음 명령을 수행합니다.T*타입이 다음과 같은 값을 반환한다.T. 반환 유형은 맨 왼쪽의 "point to a(n)"를 잘라내고 남은 모든 것으로 간주할 수 있습니다.

  *x; // Invalid: x isn't a pointer.
      // Even if a compiler allows it, this is a bad idea.
 *px; // Valid: px is "pointer to int".
      // Return type is: int
      // Truncates leftmost "pointer to" part, and returns an "int".
*pxx; // Valid: pxx is "pointer to pointer to int".
      // Return type is: int*
      // Truncates leftmost "pointer to" part, and returns a "pointer to int".

위의 각 작업에 대해 참조 해제 연산자의 반환 유형이 원본과 일치하는지 확인하십시오.T*선언서T유형.

이는 원시 컴파일러와 프로그래머 모두 포인터의 유형을 해석하는 데 큰 도움이 됩니다.컴파일러의 경우 Address-of-operator에 의해*에 대해 '비참조 연산자'를 합니다.*이치노, ★★★★★★★★★★★★의 수,* 나타냅니다( 「 」 「 」 「 」 「 」 「 」 「 」 。int* int,float** float* 「」를 가리키고 있습니다.float등 ) 。


.*간접 수준 수에 관계없이 다음을 수행합니다.

  1. 포인터는 가장 최근의 할당을 참조하여 간접 수준을 결정하고 반환 유형을 적절하게 결정해야 하기 때문에 컴파일러가 참조 해제하는 것은 훨씬 더 어렵습니다.
  2. 포인터는 프로그래머가 이해하기 어렵습니다.왜냐하면 간접 레이어가 몇 개 있는지 추적하지 못할 수 있기 때문입니다.

두 경우 모두 값의 실제 유형을 판별하는 유일한 방법은 값을 역추적하는 것이므로 값을 찾기 위해 다른 곳을 찾아야 합니다.

void f(int* pi);

int main() {
    int x;
    int *px = &x;
    int *ppx = &px;
    int *pppx = &ppx;

    f(pppx);
}

// Ten million lines later...

void f(int* pi) {
    int i = *pi; // Well, we're boned.
    // To see what's wrong, see main().
}

이며, 건...의 쉽게 할 수 있는 입니다. 매우 위험한 문제이고, 그 수가 많으면 쉽게 해결할 수 있는 문제죠.*는 직접 간접 수준을 나타냅니다.

언급URL : https://stackoverflow.com/questions/38076981/pointers-to-pointers-vs-normal-pointers

반응형