[c] 이중 간접 지정을 사용하는 이유는 무엇입니까? 또는 왜 포인터를 포인터로 사용합니까?

C에서 이중 간접을 사용해야하는 시점은 언제입니까? 누구나 예를 들어 설명 할 수 있습니까?

내가 아는 것은 이중 간접 지시가 포인터에 대한 포인터라는 것입니다. 포인터에 대한 포인터가 필요한 이유는 무엇입니까?



답변

문자 목록 (단어)을 원한다면 char *word

단어 목록 (문장)을 원한다면 char **sentence

문장 목록 (독백)을 원한다면 char ***monologue

독백 (전기) 목록을 원한다면 char ****biography

전기 목록 (바이오 라이브러리)을 원한다면 char *****biolibrary

바이오 라이브러리 목록을 원한다면 (??? lol) char ******lol

… …

예, 이것이 최선의 데이터 구조가 아닐 수도 있음을 알고 있습니다


매우 지루한 lol을 사용한 사용 예

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int wordsinsentence(char **x) {
    int w = 0;
    while (*x) {
        w += 1;
        x++;
    }
    return w;
}

int wordsinmono(char ***x) {
    int w = 0;
    while (*x) {
        w += wordsinsentence(*x);
        x++;
    }
    return w;
}

int wordsinbio(char ****x) {
    int w = 0;
    while (*x) {
        w += wordsinmono(*x);
        x++;
    }
    return w;
}

int wordsinlib(char *****x) {
    int w = 0;
    while (*x) {
        w += wordsinbio(*x);
        x++;
    }
    return w;
}

int wordsinlol(char ******x) {
    int w = 0;
    while (*x) {
        w += wordsinlib(*x);
        x++;
    }
    return w;
}

int main(void) {
    char *word;
    char **sentence;
    char ***monologue;
    char ****biography;
    char *****biolibrary;
    char ******lol;

    //fill data structure
    word = malloc(4 * sizeof *word); // assume it worked
    strcpy(word, "foo");

    sentence = malloc(4 * sizeof *sentence); // assume it worked
    sentence[0] = word;
    sentence[1] = word;
    sentence[2] = word;
    sentence[3] = NULL;

    monologue = malloc(4 * sizeof *monologue); // assume it worked
    monologue[0] = sentence;
    monologue[1] = sentence;
    monologue[2] = sentence;
    monologue[3] = NULL;

    biography = malloc(4 * sizeof *biography); // assume it worked
    biography[0] = monologue;
    biography[1] = monologue;
    biography[2] = monologue;
    biography[3] = NULL;

    biolibrary = malloc(4 * sizeof *biolibrary); // assume it worked
    biolibrary[0] = biography;
    biolibrary[1] = biography;
    biolibrary[2] = biography;
    biolibrary[3] = NULL;

    lol = malloc(4 * sizeof *lol); // assume it worked
    lol[0] = biolibrary;
    lol[1] = biolibrary;
    lol[2] = biolibrary;
    lol[3] = NULL;

    printf("total words in my lol: %d\n", wordsinlol(lol));

    free(lol);
    free(biolibrary);
    free(biography);
    free(monologue);
    free(sentence);
    free(word);
}

산출:

내 lol에있는 총 단어 : 243


답변

함수 인수로 함수에 전달 된 포인터의 값을 변경하려면 포인터에 대한 포인터가 필요합니다.

간단히 말해서 함수 호출 외부에서도 메모리 할당 또는 할당을 유지 (또는 변경 유지) 할 때 사용하십시오 **. (따라서 더블 포인터 인수로 그러한 함수를 전달하십시오.)

이것은 좋은 예는 아니지만 기본 사용법을 보여줍니다.

void allocate(int** p)
{
  *p = (int*)malloc(sizeof(int));
}

int main()
{
  int* p = NULL;
  allocate(&p);
  *p = 42;
  free(p);
}


답변

  • 포인터가 있다고 가정 해 봅시다. 그 값은 주소입니다.
  • 하지만 이제 그 주소를 변경하고 싶습니다.
  • 당신은 할 수 있습니다. 을 수행 pointer1 = pointer2하면 pointer1에 pointer2의 주소를 제공합니다.
  • 그러나! 함수 내에서이를 수행하고 함수가 완료된 후에도 결과를 유지하려면 추가 작업이 필요합니다. pointer1을 가리 키기 위해 새로운 pointer3이 필요합니다. pointer3를 함수에 전달하십시오.

  • 여기 예가 있습니다. 아래 출력을 먼저 이해하십시오.

#include <stdio.h>

int main()
{

    int c = 1;
    int d = 2;
    int e = 3;
    int * a = &c;
    int * b = &d;
    int * f = &e;
    int ** pp = &a;  // pointer to pointer 'a'

    printf("\n a's value: %x \n", a);
    printf("\n b's value: %x \n", b);
    printf("\n f's value: %x \n", f);
    printf("\n can we change a?, lets see \n");
    printf("\n a = b \n");
    a = b;
    printf("\n a's value is now: %x, same as 'b'... it seems we can, but can we do it in a function? lets see... \n", a);
    printf("\n cant_change(a, f); \n");
    cant_change(a, f);
    printf("\n a's value is now: %x, Doh! same as 'b'...  that function tricked us. \n", a);

    printf("\n NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a' \n");
     printf("\n change(pp, f); \n");
    change(pp, f);
    printf("\n a's value is now: %x, YEAH! same as 'f'...  that function ROCKS!!!. \n", a);
    return 0;
}

void cant_change(int * x, int * z){
    x = z;
    printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n", x);
}

void change(int ** x, int * z){
    *x = z;
    printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n", *x);
}

출력은 다음과 같습니다 ( 이를 먼저 읽으십시오 ).

 a's value: bf94c204

 b's value: bf94c208

 f's value: bf94c20c

 can we change a?, lets see

 a = b

 a's value is now: bf94c208, same as 'b'... it seems we can, but can we do it in a function? lets see...

 cant_change(a, f);

 ----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see

 a's value is now: bf94c208, Doh! same as 'b'...  that function tricked us.

 NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a'

 change(pp, f);

 ----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see

 a's value is now: bf94c20c, YEAH! same as 'f'...  that function ROCKS!!!. 


답변

다음 예제에 대한 단일 포인터 (예 : alloc1 ())를 사용하는 경우 Asha의 응답에 추가 하면 함수 내에 할당 된 메모리에 대한 참조가 손실됩니다.

void alloc2(int** p) {
   *p = (int*)malloc(sizeof(int));
   **p = 10;
}

void alloc1(int* p) {
   p = (int*)malloc(sizeof(int));
   *p = 10;
}

int main(){
   int *p = NULL;
   alloc1(p);
   //printf("%d ",*p);//undefined
   alloc2(&p);
   printf("%d ",*p);//will print 10
   free(p);
   return 0;
}

이것이 발생하는 이유 alloc1는 포인터가 값으로 전달되기 때문입니다. 따라서 malloc내부 호출 결과에 다시 할당 alloc1되면 변경 내용이 다른 범위의 코드와 관련이 없습니다.


답변

아래에 요약 된 것처럼 이 블로그 게시물 에서 오늘 좋은 예를 보았습니다 .

링크 된 목록에 노드 구조가 있다고 가정하십시오.

typedef struct node
{
    struct node * next;
    ....
} node;

이제 remove_if제거 기준 rm을 인수 중 하나로 받아들이고 링크 된 목록을 순회 하는 함수 를 구현하려고 합니다. 항목이 기준 ()을 만족하면 rm(entry)==true해당 노드가 목록에서 제거됩니다. 결국, remove_if연결된 목록의 헤드 (원래 헤드와 다를 수 있음)를 반환합니다.

당신은 쓸 수 있습니다

for (node * prev = NULL, * curr = head; curr != NULL; )
{
    node * const next = curr->next;
    if (rm(curr))
    {
        if (prev)  // the node to be removed is not the head
            prev->next = next;
        else       // remove the head
            head = next;
        free(curr);
    }
    else
        prev = curr;
    curr = next;
}

당신의 for루프로. 이중 포인터prev없는 메시지는 포인터 를 재구성 하기 위해 변수를 유지 하고 두 가지 경우를 처리해야한다는 것입니다.

그러나 이중 포인터를 사용하면 실제로 쓸 수 있습니다.

// now head is a double pointer
for (node** curr = head; *curr; )
{
    node * entry = *curr;
    if (rm(entry))
    {
        *curr = entry->next;
        free(entry);
    }
    else
        curr = &entry->next;
}

가리키는 항목을 직접 수정할 수 있으므로 prev지금 필요하지 않습니다 .prev->next

더 명확하게하기 위해 코드를 조금만 따라 봅시다. 제거하는 동안 :

  1. if entry == *head: 이제 *head (==*curr) = *head->nexthead새로운 표제 노드의 포인터를 가리 킵니다. 님 head의 콘텐츠를 새 포인터 로 직접 변경하면됩니다 .
  2. 경우 entry != *head: 유사하게, *curr무엇 prev->next에 지금 점에 지적하고 entry->next.

어떤 경우 든 더블 포인터를 사용하여 포인터를 통합 된 방식으로 재구성 할 수 있습니다.


답변

1. 기본 개념-

다음과 같이 선언하면 :-

1. char * ch-(문자 포인터라고 함)
-ch는 단일 문자의 주소를 포함합니다.
-(* ch)는 ​​문자 값을 참조하지 않습니다.

2. char ** ch-
‘ch’는 문자 포인터 배열의 주소를 포함합니다. (1에서와 같이)
‘* ch’는 단일 문자의 주소를 포함합니다. (선언의 차이로 인해 1과 다릅니다.)
(** ch)는 ​​문자의 정확한 값을 참조하지 않습니다.

포인터를 더 추가하면 데이터 유형의 차원이 문자에서 문자열, 문자열 배열 등으로 확장됩니다. 1d, 2d, 3d 행렬과 연관시킬 수 있습니다.

따라서 포인터 사용법은 선언 방법에 따라 다릅니다.

다음은 간단한 코드입니다.

int main()
{
    char **p;
    p = (char **)malloc(100);
    p[0] = (char *)"Apple";      // or write *p, points to location of 'A'
    p[1] = (char *)"Banana";     // or write *(p+1), points to location of 'B'

    cout << *p << endl;          //Prints the first pointer location until it finds '\0'
    cout << **p << endl;         //Prints the exact character which is being pointed
    *p++;                        //Increments for the next string
    cout << *p;
}

2. 더블 포인터의 또 다른 응용-
(이것은 참조로 패스를 커버 할 것입니다)

함수에서 문자를 업데이트한다고 가정하십시오. 다음을 시도하면 :-

void func(char ch)
{
    ch = 'B';
}

int main()
{
    char ptr;
    ptr = 'A';
    printf("%c", ptr);

    func(ptr);
    printf("%c\n", ptr);
}

출력은 AA가됩니다. 함수에 “Passed By Value”가 있으므로 작동하지 않습니다.

올바른 방법은-

void func( char *ptr)        //Passed by Reference
{
    *ptr = 'B';
}

int main()
{
    char *ptr;
    ptr = (char *)malloc(sizeof(char) * 1);
    *ptr = 'A';
    printf("%c\n", *ptr);

    func(ptr);
    printf("%c\n", *ptr);
}

이제 문자 대신 문자열을 업데이트하기 위해이 요구 사항을 확장하십시오.
이를 위해 함수에서 매개 변수를 이중 포인터로 받아야합니다.

void func(char **str)
{
    strcpy(str, "Second");
}

int main()
{
    char **str;
    // printf("%d\n", sizeof(char));
    *str = (char **)malloc(sizeof(char) * 10);          //Can hold 10 character pointers
    int i = 0;
    for(i=0;i<10;i++)
    {
        str = (char *)malloc(sizeof(char) * 1);         //Each pointer can point to a memory of 1 character.
    }

    strcpy(str, "First");
    printf("%s\n", str);
    func(str);
    printf("%s\n", str);
}

이 예제에서 메소드는 문자열의 값을 업데이트하기 위해 매개 변수로 이중 포인터를 예상합니다.


답변

포인터에 대한 포인터는 또한 메모리 사이의 “핸들”로서 유용하며, 함수들 사이의 “핸들”주위를 이동하여 재배치 가능한 메모리로 전달하려고합니다. 이는 기본적으로 함수가 핸들 변수 내부의 포인터가 가리키는 메모리를 변경할 수 있으며 핸들을 사용하는 모든 함수 또는 객체가 새로 재배치 된 (또는 할당 된) 메모리를 올바르게 가리 킵니다. 라이브러리는 “불투명 한”데이터 형식으로이 작업을 수행하는 것과 같습니다. 즉, 데이터 형식은 메모리가 지적한 작업에 대해 걱정할 필요가 없었으며, 단순히 “핸들”을 해당 메모리에서 일부 작업을 수행하는 라이브러리 기능 …

예를 들어 :

#include <stdlib.h>

typedef unsigned char** handle_type;

//some data_structure that the library functions would work with
typedef struct
{
    int data_a;
    int data_b;
    int data_c;
} LIB_OBJECT;

handle_type lib_create_handle()
{
    //initialize the handle with some memory that points to and array of 10 LIB_OBJECTs
    handle_type handle = malloc(sizeof(handle_type));
    *handle = malloc(sizeof(LIB_OBJECT) * 10);

    return handle;
}

void lib_func_a(handle_type handle) { /*does something with array of LIB_OBJECTs*/ }

void lib_func_b(handle_type handle)
{
    //does something that takes input LIB_OBJECTs and makes more of them, so has to
    //reallocate memory for the new objects that will be created

    //first re-allocate the memory somewhere else with more slots, but don't destroy the
    //currently allocated slots
    *handle = realloc(*handle, sizeof(LIB_OBJECT) * 20);

    //...do some operation on the new memory and return
}

void lib_func_c(handle_type handle) { /*does something else to array of LIB_OBJECTs*/ }

void lib_free_handle(handle_type handle)
{
    free(*handle);
    free(handle);
}


int main()
{
    //create a "handle" to some memory that the library functions can use
    handle_type my_handle = lib_create_handle();

    //do something with that memory
    lib_func_a(my_handle);

    //do something else with the handle that will make it point somewhere else
    //but that's invisible to us from the standpoint of the calling the function and
    //working with the handle
    lib_func_b(my_handle);

    //do something with new memory chunk, but you don't have to think about the fact
    //that the memory has moved under the hood ... it's still pointed to by the "handle"
    lib_func_c(my_handle);

    //deallocate the handle
    lib_free_handle(my_handle);

    return 0;
}

도움이 되었기를 바랍니다,

제이슨