programing

C에서 **를 사용할 때의 차이점

minimums 2023. 6. 12. 21:16
반응형

C에서 **를 사용할 때의 차이점

최근에 C를 배우기 시작했는데 포인터 구문을 이해하는 데 문제가 있습니다. 예를 들어 다음 줄을 쓸 때입니다.

int ** arr = NULL;

다음과 같은 경우 어떻게 알 수 있습니까?

  • ar는 정수의 포인터에 대한 포인터입니다.

  • ar는 정수에 대한 포인터 배열에 대한 포인터입니다.

  • ar는 정수 배열에 대한 포인터 배열에 대한 포인터입니다.

다 똑같지 않습니까?int **?


Another question for the same problem:

▁를 받는 이 있으면,char ** s매개 변수로, 나는 그것을 a라고 부르고 싶습니다.pointer 포인터, 문열배열대포터인한포, 에▁of▁의 배열에 합니다.chars하지만 그것은 또한 포인터에 대한 포인터입니다.char?

다 똑같지 않습니까?int **?

방금 유형 시스템의 결함으로 간주될 수 있는 내용을 발견했습니다.지정한 모든 옵션이 참일 수 있습니다.기본적으로 프로그램 메모리의 평면도에서 파생된 것으로, 단일 주소를 사용하여 다양한 논리 메모리 레이아웃을 참조할 수 있습니다.

C 프로그래머들이 C의 시작 이후로 이것을 다루는 방법은 규약을 마련하는 것입니다.예를 들어, 이러한 포인터를 허용하는 함수에 대한 크기 매개변수 요구 및 메모리 레이아웃에 대한 가정 문서화 등이 있습니다.또는 어레이를 특수 값으로 종료하도록 요구하여 버퍼에 대한 포인터의 "재깅된" 버퍼를 허용합니다.


나는 어느 정도의 해명이 필요하다고 생각합니다.여기서 다른 매우 좋은 답변을 참조할 때 알 수 있듯이 어레이는 포인터가 아닙니다.하지만 그것들은 그것들에 대해 가르치는 데 있어 수십 년 동안의 오류를 정당화할 수 있을 만큼 충분한 맥락에서 하나로 쇠퇴합니다(하지만 저는 탈선합니다).

제가 원래 작성한 내용은 다음과 같은 코드를 의미합니다.

void func(int **p_buff)
{
}

//...

int a = 0, *pa = &a;
func(&pa);

//...

int a[3][10];
int *a_pts[3] = { a[0], a[1], a[2] };
func(a_pts);

//...

int **a = malloc(10 * sizeof *a);
for(int i = 0; i < 10; ++i)
  a[i] = malloc(i * sizeof *a[i]);
func(a);

정다하추로 합니다.func각 코드 스니펫은 별도의 번역 단위로 컴파일됩니다.의 예(나에는한 C (에의한오제타입외고하다나유를예)입니다.배열은 인수로 전달될 때 "포인트 투 어 포인터"로 붕괴됩니다.의 정의는 어떻습니까?func매개 변수의 유형만으로 정확히 전달된 것이 무엇인지 알 수 있습니다!?답은 그럴 수 없다는 것입니다.의 의적 유형정p_buff이라int**하지만 여전히 그것은 허용합니다.func매우 다양한 효과적인 유형의 객체에 간접적으로 액세스(일부)할 수 있습니다.

언문선.int **arr ".", "interpointerar", "interpointerar".(유효한 경우) 단일 정수 개체를 가리키는 단일 포인터를 가리킵니다.포인터 은 어느 한 어한수즉방향의준으(느, 로니다때))으로도 할 수 입니다.*arr는 와동합다니와 .arr[0]그리고.**arr는 와동합다니와 .arr[0][0]), 객체는 당신의 질문에서 3에 접근하는 사용될 수 있습니다. (즉, 두 번째는 정수에 대한 포인터 배열에 접근하고 세 번째는 정수 배열의 첫 번째 요소에 대한 포인터 배열에 접근합니다.) 만약 포인터가 배열의 첫 번째 요소를 가리킨다면...


직아.arr는 여전히 단일 정수 개체에 대한 단일 포인터로 선언됩니다.정의된 차원배열에 대한 포인터를 선언할 수도 있습니다.여기서a됩니다: 10개의 정수 배열 10개의 10진수 배열에 대한 포인터입니다.

cdecl> declare a as pointer to array 10 of pointer to array 10 of int;
int (*(*a)[10])[10]

실제로 배열 포인터는 일정한 차원의 다차원 배열을 함수로 전달하거나 가변 길이 배열을 전달하는 데 가장 많이 사용됩니다.변수를 배열에 대한 포인터로 선언하는 구문은 함수에 전달될 때마다 "정의되지 않은 크기의 배열" 유형의 매개 변수를 사용하는 것이 다소 쉬우므로 선언하는 대신

void func(int (*a)[10]);

사용할 수 있는

void func(int a[][10])

10개의 정수 배열의 다차원 배열을 통과합니다.a 에신대, a.typedef두통을 완화시키는 데 사용될 수 있습니다.

다음의 경우 어떻게 알 수 있습니까?

  • ar는 정수의 포인터에 대한 포인터입니다.

항상 정수에 대한 포인터입니다.

  • ar는 정수에 대한 포인터 배열에 대한 포인터입니다.
  • ar는 정수 배열에 대한 포인터 배열에 대한 포인터입니다.

절대 그럴 수 없어요.정수에 대한 포인터 배열에 대한 포인터는 다음과 같이 선언됩니다.

int* (*arr)[n]

마치 당신이 사용하도록 속은 것처럼 들립니다.int**가난한 선생님들/책/학생들에 의해여기와 여기, 그리고 여기(어레이 포인터에 대한 자세한 설명 포함)에 설명된 것처럼 거의 항상 잘못된 관행입니다.

편집

마침내 어레이가 무엇인지, 조회 테이블은 무엇인지, 후자가 왜 나쁜지, 그리고 대신 사용해야 하는 것을 설명하는 자세한 게시물을 작성하게 되었습니다.다차원 배열을 올바르게 할당합니다.

변수 선언만 가지고 있기 때문에 세 가지 경우를 구분할 수 없습니다.다음과 같은 것을 사용하지 말아야 하는지에 대해서는 여전히 논의할 수 있습니다.int *x[10]것; int 다 개 것 대 10에 포 인 터 배 표 는 하 것 현 하 나 만 지 을 열 른 한 의 ▁to 하 만 지 것 나 ▁an ▁express ▁int ▁but ; s ▁pointers ▁of ▁toint **x- 산술적으로 잘못된 을 할 ( 확률을 세 으로 사용됩니다. can - 포좋인해산로술할 (좋은) 은확률로잘못된가다있니) 정는을있다메른모리웃레을아이수습가정하는사할용세가방로으지법수인터▁can▁layout▁way▁used▁in▁different▁a▁ar▁-,can▁three다▁be니▁to▁assuming▁-,▁due▁theith있습▁each▁waysmet▁pointericsption▁to수▁assumgood▁memory▁make▁with▁the▁wrong▁different▁(.

다음의 예를 생각해 보십시오.int **는 세 즉, 가방사용됩니다로으식지세다사니▁is▁different용.p2p2i_v1 (으) int, (으)로 가는 포인터로서,p2p2i_v2로서, 그리고 int대 서 포 대 포 로 터 그 고 리 인 한 에 한 에 열 배 인 터 ▁as 고리 그 ▁a 서 에 ▁to 로 , ▁to ▁intp2p2i_v3int 배열에 대한 포인터로 사용합니다. 세 는 없습니다. 즉 이 세 가 의 즉 유 점 없 십 하 의 시 오 에 는 지 다int** 경우 방법으로 각 수 없는 결과가 합니다.그러나 초기화가 서로 다르므로 각 초기화에 잘못된 방식으로 액세스하면 다음과 같은 첫 번째 요소에 액세스하는 것을 제외하고는 예측할 수 없는 결과를 초래됩니다.

int i1=1,i2=2,i3=3,i4=4;

int *p2i = &i1;
int **p2p2i_v1 = &p2i;  // pointer to a pointer to a single int

int *arrayOfp2i[4] = { &i1, &i2, &i3, &i4 };
int **p2p2i_v2 = arrayOfp2i; // pointer to an array of pointers to int

int arrayOfI[4] = { 5,6,7,8 };
int *p2arrayOfi = arrayOfI;
int **p2p2i_v3 = &p2arrayOfi; // pointer to a pointer to an array of ints

// assuming a pointer to a pointer to a single int:
int derefi1_v1 = *p2p2i_v1[0];  // correct; yields 1
int derefi1_v2 = *p2p2i_v2[0];  // correct; yields 1
int derefi1_v3 = *p2p2i_v3[0];  // correct; yields 5

// assuming a pointer to an array of pointers to int's
int derefi1_v1_at1 = *p2p2i_v1[1];  // incorrect, yields ? or seg fault
int derefi1_v2_at1 = *p2p2i_v2[1]; // correct; yields 2
int derefi1_v3_at1 = *p2p2i_v3[1]; // incorrect, yields ? or seg fault


// assuming a pointer to an array of pointers to an array of int's
int derefarray_at1_v1 = (*p2p2i_v1)[1]; // incorrect; yields ? or seg fault;
int derefarray_at1_v2 = (*p2p2i_v2)[1]; // incorrect; yields ? or seg fault;
int derefarray_at1_v3 = (*p2p2i_v3)[1]; // correct; yields 6;

다음의 경우 어떻게 알 수 있습니까?

ar는 정수의 포인터에 대한 포인터입니다.

ar는 정수에 대한 포인터 배열에 대한 포인터입니다.

ar는 정수 배열에 대한 포인터 배열에 대한 포인터입니다.

안 돼요.그것들 중 어느 것이든 될 수 있습니다.결과적으로 무엇이 되는지는 할당/사용 방법에 따라 달라집니다.

따라서 이것들을 사용하여 코드를 작성하는 경우, 그것들을 사용하여 수행하는 작업을 문서화하고, 크기 매개 변수를 함수에 전달하며, 일반적으로 사용하기 전에 무엇을 할당했는지 확인합니다.

포인터는 단일 개체를 가리킬 때나 배열의 요소인 개체를 가리킬 때나 정보를 유지하지 않습니다.또한 포인터 산술의 경우 단일 객체는 하나의 요소로 구성된 배열과 같이 간주됩니다.

이러한 선언을 고려합니다.

int a;
int a1[1];
int a2[10];

int *p;

p = &a;
//...
p = a1;
//...
p = a2;

는 " " " 입니다.p주소를 처리합니다.저장된 주소가 다음과 같은 단일 개체를 가리키는지 여부를 알 수 없습니다.a첫 요 배 이 동 합 니 다 로 소 번 첫 열 의a1첫 또는 배 의 요 첫 또 는 하 의 요 만 있 는 경a2열 개의 요소를 가지고 있습니다.

의 유형

int ** arr;

하나의 유효한 해석만 있습니다.다음과 같습니다.

arr is a pointer to a pointer to an integer

만약 당신이 위의 선언보다 더 많은 정보를 가지고 있지 않다면, 그것이 당신이 알 수 있는 전부입니다, 즉, 만약.arr이 초기화되어 있을 수 있습니다. 다른 포인터를 가리키며, 초기화되어 있을 경우 정수를 가리킵니다.

적절한 초기화를 가정할 때 이를 사용할 수 있는 유일한 보장된 유효한 방법은 다음과 같습니다.

**arr = 42;
int a = **arr;

그러나 C에서는 여러 가지 방법으로 사용할 수 있습니다.

ar는 정수(즉, 기본 대소문자)에 대한 포인터로 사용될 수 있습니다.

int a = **arr;

ar는 정수 배열에 대한 포인터로 사용될 수 있습니다.

int a = (*arr)[4];

ar는 정수에 대한 포인터 배열에 대한 포인터로 사용될 수 있습니다.

int a = *(arr[4]);

ar는 정수 배열에 대한 포인터 배열에 대한 포인터로 사용될 수 있습니다.

int a = arr[4][4];

마지막 세 가지 경우에는 배열이 있는 것처럼 보일 수 있습니다.그러나 형식이 배열이 아닙니다.유형은 항상 정당합니다.a pointer to a pointer to an integer참조 해제는 포인터 산술입니다.그것은 2D 어레이와는 전혀 다릅니다.

에 유효한지 는 코드 합니다.arr.

갱신하다

질문의 업데이트된 부분:

다음이 있는 경우:

void foo(char** x) { .... };

당신이 확실하게 알고 있는 유일한 것은**x차를 줄 것이고, 그리고*x 포인터 경우 )를 제공합니다.x가정).

당신이 경우용을 사용하고 .x다른 방법으로, 예를 들어.x[2]세 번째 문자 포인터를 가져오려면 호출자가 초기화되어 있어야 합니다.x최소 3개의 연속적인 문자 포인터가 있는 메모리 영역을 가리키도록 합니다.이는 통화 계약이라고 할 수 있습니다.foo.

C 구문은 논리적입니다.선언의 식별자 앞에 있는 별표는 변수 유형에 대한 포인터이며, 두 개의 별표는 변수 유형에 대한 포인터를 의미합니다.

이우경arr이라a pointer to a pointer to integer.

이중 포인터는 여러 가지 용도로 사용됩니다.예를 들어 포인터 벡터에 대한 포인터로 행렬을 나타낼 수 있습니다.이 벡터의 각 포인터는 행렬 자체의 행을 가리킵니다.

이것을 사용하여 2차원 배열을 만들 수도 있습니다.

 int **arr=(int**)malloc(row*(sizeof(int*)));  
 for(i=0;i<row;i++) {
 *(arr+i)=(int*)malloc(sizeof(int)*col); //You can use this also. Meaning of both is same. //
  arr[i]=(int*)malloc(sizeof(int)*col); }

포인터를 사용할 때 한 가지 요령이 있습니다. 오른쪽에서 왼쪽으로 읽으십시오.

int** arr = NULL;

무엇을 얻을 수 있습니까?arr,*,*,int따라서 배열은 정수에 대한 포인터입니다.

그리고.int **arr;는 와동합다니와 .int** arr;.

int ** arr = NULL;

컴파일러에게 말합니다.arr is a double pointer of an integer 할당된 및할됨.NULLvalue.value.value.

여기에는 이미 좋은 답변이 있지만 복잡한 선언을 위해 제 "이동" 사이트를 언급하고 싶습니다. http://cdecl.org/

사이트를 방문하여 신고서를 붙이면 영어로 번역됩니다.

위해서int ** arr;라고 씌어 있습니다declare arr as pointer to pointer to int.

이 사이트는 예제도 보여줍니다.여러분 자신을 시험해 본 다음, 정답을 보기 위해 커서를 놓습니다.

(double (^)(int , long long ))foo

foo를 블록에 던지다 (int, long long) 리턴 더블

int (*(*foo)(void ))[3]

foo를 함수에 대한 포인터로 선언(포인터), int의 배열 3에 대한 포인터 반환

그것은 또한 영어를 C 선언으로 번역할 것이며, 만약 당신이 설명을 정확하게 이해한다면 꽤 깔끔할 것입니다.

언급URL : https://stackoverflow.com/questions/42065293/differences-when-using-in-c

반응형