답변:
문자 목록 (단어)을 원한다면 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
arr[a][b][c]
가 아닌 것을 지적하고 싶었 습니다 ***arr
. 포인터의 포인터는 참조의 참조를 사용하지만 arr[a][b][c]
행의 주요 순서로 일반적인 배열로 저장됩니다.
함수 인수로 함수에 전달 된 포인터의 값을 변경하려면 포인터에 대한 포인터가 필요합니다.
간단히 말해서 함수 호출 외부에서도 메모리 할당 또는 할당을 유지 (또는 변경 유지) 할 때 사용하십시오 **
. (따라서 더블 포인터 인수로 그러한 함수를 전달하십시오.)
이것은 좋은 예는 아니지만 기본 사용법을 보여줍니다.
void allocate(int** p)
{
*p = (int*)malloc(sizeof(int));
}
int main()
{
int* p = NULL;
allocate(&p);
*p = 42;
free(p);
}
void allocate(int *p)
있고 당신이 그것을 불렀다 면 무엇이 다를까요 allocate(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
되면 변경 내용이 다른 범위의 코드와 관련이 없습니다.
free(p)
충분하지 않습니다, 당신 if(p) free(*p)
은뿐만 아니라
*p
가로 평가 int
, 10의 값을 보관 유지하는이 통과 int
() 확보는`나쁜 생각이다.
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
더 명확하게하기 위해 코드를 조금만 따라 봅시다. 제거하는 동안 :
entry == *head
: 이제 *head (==*curr) = *head->next
- head
새로운 표제 노드의 포인터를 가리 킵니다. 님 head
의 콘텐츠를 새 포인터 로 직접 변경하면됩니다 .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 <stdio.h> int main() { char *ptr = 0; ptr = malloc(255); // allocate some memory strcpy( ptr, "Stack Overflow Rocks..!!"); printf("%s\n", ptr); printf("%d\n",strlen(ptr)); free(ptr); return 0; }
그러나 이중 포인터를 사용하지 않고도 할 수 있습니다.
char
포인터 배열의 첫 번째 요소 주소를 포함합니다 . 의 배열에 대한 포인터 char*
는 예를 들어 다음과 같이 입력 됩니다 .에 대한 42 배열의 포인터를 char(*(*p)[42])
정의 p
합니다 char
.
*str = ...
str
정의되지 않은 동작을 호출하는 초기화되지 않은 참조가 있습니다.
malloc(sizeof(char) * 10);
10 포인터를위한 공간을 할당하지 않고 char
10 char
만을 위한 공간을 할당 합니다.
for(i=0;i<10;i++) { str = ...
는 인덱스를 사용하지 못합니다 i
.
포인터에 대한 포인터는 또한 메모리 사이의 "핸들"로서 유용하며, 함수들 사이의 "핸들"주위를 이동하여 재배치 가능한 메모리로 전달하려고합니다. 이는 기본적으로 함수가 핸들 변수 내부의 포인터가 가리키는 메모리를 변경할 수 있으며 핸들을 사용하는 모든 함수 또는 객체가 새로 재배치 된 (또는 할당 된) 메모리를 올바르게 가리 킵니다. 라이브러리는 "불투명 한"데이터 형식으로이 작업을 수행하는 것과 같습니다. 즉, 데이터 형식은 메모리가 지적한 작업에 대해 걱정할 필요가 없었으며, 단순히 "핸들"을 해당 메모리에서 일부 작업을 수행하는 라이브러리 기능 ...
예를 들어 :
#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;
}
도움이 되었기를 바랍니다,
제이슨
unsigned char
원시 바이트로 표시되는 이진 데이터에 대한 포인터를 저장하기 때문에 특히 사용됩니다. 사용 void
하려면 어느 시점에서 캐스트가 필요하며 일반적으로 수행중인 작업의 의도만큼 읽을 수 없습니다.
int main(int argc, char **argv)
두 번째 매개 변수에는 char에 대한 포인터를 가리키는 포인터가 있습니다.
포인터 표기법 ( char* c
)과 배열 표기법 ( char c[]
)은 함수 인수에서 서로 바꿔 사용할 수 있습니다. 그래서 당신은 또한 쓸 수 있습니다 char *argv[]
. 즉 char *argv[]
와 char **argv
교환 할 수있다.
위의 내용은 실제로 문자 시퀀스 배열 (시작시 프로그램에 제공되는 명령 행 인수)입니다.
참조 이 답변 위의 함수 서명에 대한 자세한 내용을.
char* c
) 어레이 표기법 ( char c[]
) 상호 교환" (그리고 정확히 같은 의미를 갖는다) 함수 인수하여 . 그러나 외부 함수 인수와는 다릅니다.
예를 들어, 메모리를 확보 할 때 포인터를 널로 설정했는지 확인하고 싶을 수 있습니다.
void safeFree(void** memory) {
if (*memory) {
free(*memory);
*memory = NULL;
}
}
이 함수를 호출하면 포인터의 주소로 호출됩니다
void* myMemory = someCrazyFunctionThatAllocatesMemory();
safeFree(&myMemory);
이제 myMemory
NULL로 설정되어 있으며 재사용하려는 시도는 매우 잘못되었습니다.
if(*memory)
하고free(*memory);
예를 들어 비 연속 데이터에 무작위로 액세스하려는 경우.
p -> [p0, p1, p2, ...]
p0 -> data1
p1 -> data2
-C에서
T ** p = (T **) malloc(sizeof(T*) * n);
p[0] = (T*) malloc(sizeof(T));
p[1] = (T*) malloc(sizeof(T));
포인터 p
배열을 가리키는 포인터를 저장합니다. 각 포인터는 데이터 조각을 가리 킵니다.
경우 sizeof(T)
큰 그것의 (즉, malloc을 사용) 연속 블록을 할당하는 것이 가능하지 않을 수 있습니다 sizeof(T) * n
바이트.
내가 지속적으로 사용하는 것 중 하나는 객체 배열이 있고 다른 필드별로 조회 (이진 검색)를 수행해야 할 때입니다.
원래 배열을 유지합니다 ...
int num_objects;
OBJECT *original_array = malloc(sizeof(OBJECT)*num_objects);
그런 다음 객체에 대한 정렬 된 포인터 배열을 만듭니다.
int compare_object_by_name( const void *v1, const void *v2 ) {
OBJECT *o1 = *(OBJECT **)v1;
OBJECT *o2 = *(OBJECT **)v2;
return (strcmp(o1->name, o2->name);
}
OBJECT **object_ptrs_by_name = malloc(sizeof(OBJECT *)*num_objects);
int i = 0;
for( ; i<num_objects; i++)
object_ptrs_by_name[i] = original_array+i;
qsort(object_ptrs_by_name, num_objects, sizeof(OBJECT *), compare_object_by_name);
필요한만큼 정렬 된 포인터 배열을 만든 다음 정렬 된 포인터 배열에서 이진 검색을 사용하여 필요한 데이터로 필요한 개체에 액세스 할 수 있습니다. 객체의 원래 배열은 정렬되지 않은 채로있을 수 있지만 각 포인터 배열은 지정된 필드를 기준으로 정렬됩니다.
왜 더블 포인터인가?
목적은 함수를 사용하여 studentA가 가리키는 것을 변경하는 것입니다.
#include <stdio.h>
#include <stdlib.h>
typedef struct Person{
char * name;
} Person;
/**
* we need a ponter to a pointer, example: &studentA
*/
void change(Person ** x, Person * y){
*x = y; // since x is a pointer to a pointer, we access its value: a pointer to a Person struct.
}
void dontChange(Person * x, Person * y){
x = y;
}
int main()
{
Person * studentA = (Person *)malloc(sizeof(Person));
studentA->name = "brian";
Person * studentB = (Person *)malloc(sizeof(Person));
studentB->name = "erich";
/**
* we could have done the job as simple as this!
* but we need more work if we want to use a function to do the job!
*/
// studentA = studentB;
printf("1. studentA = %s (not changed)\n", studentA->name);
dontChange(studentA, studentB);
printf("2. studentA = %s (not changed)\n", studentA->name);
change(&studentA, studentB);
printf("3. studentA = %s (changed!)\n", studentA->name);
return 0;
}
/**
* OUTPUT:
* 1. studentA = brian (not changed)
* 2. studentA = brian (not changed)
* 3. studentA = erich (changed!)
*/
다음은 함수를 사용하여 포인터가 객체를 가리 키도록 설정 하려면 포인터에 대한 포인터가 필요 하다는 것을 보여주는 매우 간단한 C ++ 예제입니다 . 그렇지 않으면 포인터가 계속 null로 되돌아갑니다 .
(C ++ 답변이지만 C와 동일하다고 생각합니다.)
(또한 참조 : Google ( "pass by value c ++") = "기본적으로 C ++의 인수는 값으로 전달됩니다. 인수가 값으로 전달되면 인수의 값이 함수의 매개 변수에 복사됩니다.")
따라서 포인터 b
를 문자열과 동일하게 설정하려고 합니다 a
.
#include <iostream>
#include <string>
void Function_1(std::string* a, std::string* b) {
b = a;
std::cout << (b == nullptr); // False
}
void Function_2(std::string* a, std::string** b) {
*b = a;
std::cout << (b == nullptr); // False
}
int main() {
std::string a("Hello!");
std::string* b(nullptr);
std::cout << (b == nullptr); // True
Function_1(&a, b);
std::cout << (b == nullptr); // True
Function_2(&a, &b);
std::cout << (b == nullptr); // False
}
// Output: 10100
라인에서 무슨 일이 Function_1(&a, b);
?
&main::a
(주소) 의 "값"이 매개 변수에 복사됩니다 std::string* Function_1::a
. 따라서 Function_1::a
문자열에 대한 포인터 (즉, 메모리 주소) main::a
입니다.
main::b
(메모리의 주소) 의 "값"이 매개 변수에 복사됩니다 std::string* Function_1::b
. 따라서 메모리에이 주소 중 2 개가 있으며 모두 널 포인터입니다. 라인 b = a;
에서 로컬 변수 Function_1::b
는 동일 Function_1::a
(= &main::a
) main::b
으로 변경 되지만 변수 는 변경되지 않습니다. 호출 한 후 Function_1
, main::b
여전히 널 포인터이다.
라인에서 무슨 일이 Function_2(&a, &b);
?
a
변수 처리는 동일합니다. 함수 내 Function_2::a
에서 문자열의 주소입니다 main::a
.
그러나 변수 b
는 이제 포인터에 대한 포인터로 전달됩니다. &main::b
( 포인터 의 주소)의 "값"이에 main::b
복사됩니다 std::string** Function_2::b
. 따라서 Function_2 내에서 *Function_2::b
액세스 및 수정하는 것처럼 이것을 역 참조 합니다 main::b
. 따라서 라인 *b = a;
은 실제로 main::b
(주소)를 Function_2::a
(= address of main::a
)와 동일하게 설정 하고 있습니다.
객체를 수정하기 위해 함수를 사용하려면 객체 또는 주소 (포인터), 해당 객체에 대한 포인터를 전달해야합니다. 로컬 사본이 작성되므로 실제로 전달 하는 것은 호출 범위에서 수정할 수 없습니다.
(매개 변수가 참조와 같은 참조 인 경우는 예외입니다 std::string& a
. 그러나 일반적으로 이것들은 const
입니다. 일반적으로 호출하는 f(x)
경우 x
, 개체 인 경우 수정 f
하지 않을 것이라고 가정 할 수 있습니다 x
. 그러나 x
포인터 인 경우 에는 에서 가리키는 객체를 수정할 f
수 있다고 가정합니다 x
.)
파티에 조금 늦었지만 희망적으로 이것은 누군가를 도울 것입니다.
C 배열에서는 항상 스택에 메모리를 할당하므로 실행이 현재 블록의 끝에 도달하면 스택에 할당 된 메모리가 자동으로 해제되므로 함수는 (정적이 아닌) 배열을 반환 할 수 없습니다. 2 차원 배열 (예 : 행렬)을 처리하고 행렬을 변경하고 반환 할 수있는 몇 가지 함수를 구현하려는 경우 실제로 성가시다. 이를 위해 포인터 대 포인터를 사용하여 동적으로 할당 된 메모리가있는 행렬을 구현할 수 있습니다.
/* Initializes a matrix */
double** init_matrix(int num_rows, int num_cols){
// Allocate memory for num_rows float-pointers
double** A = calloc(num_rows, sizeof(double*));
// return NULL if the memory couldn't allocated
if(A == NULL) return NULL;
// For each double-pointer (row) allocate memory for num_cols floats
for(int i = 0; i < num_rows; i++){
A[i] = calloc(num_cols, sizeof(double));
// return NULL if the memory couldn't allocated
// and free the already allocated memory
if(A[i] == NULL){
for(int j = 0; j < i; j++){
free(A[j]);
}
free(A);
return NULL;
}
}
return A;
}
그림은 다음과 같습니다.
double** double* double
------------- ---------------------------------------------------------
A ------> | A[0] | ----> | A[0][0] | A[0][1] | A[0][2] | ........ | A[0][cols-1] |
| --------- | ---------------------------------------------------------
| A[1] | ----> | A[1][0] | A[1][1] | A[1][2] | ........ | A[1][cols-1] |
| --------- | ---------------------------------------------------------
| . | .
| . | .
| . | .
| --------- | ---------------------------------------------------------
| A[i] | ----> | A[i][0] | A[i][1] | A[i][2] | ........ | A[i][cols-1] |
| --------- | ---------------------------------------------------------
| . | .
| . | .
| . | .
| --------- | ---------------------------------------------------------
| A[rows-1] | ----> | A[rows-1][0] | A[rows-1][1] | ... | A[rows-1][cols-1] |
------------- ---------------------------------------------------------
이중 포인터 대 이중 포인터 A는 요소가 이중 포인터 자체 인 메모리 블록의 첫 번째 요소 A [0]을 가리 킵니다. 이 이중 포인터를 행렬의 행으로 상상할 수 있습니다. 이것이 모든 이중 포인터가 double 유형의 num_cols 요소에 메모리를 할당하는 이유입니다. 또한 A [i]는 i 번째 행을 가리키고, 즉 A [i]는 A [i] [0]을 가리키며 i 번째 행에 대한 메모리 블록의 첫 번째 이중 요소입니다. 마지막으로 A [i] [j]를 사용하여 i 번째 행과 j 번째 열의 요소에 쉽게 액세스 할 수 있습니다.
사용법을 보여주는 완전한 예는 다음과 같습니다.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* Initializes a matrix */
double** init_matrix(int num_rows, int num_cols){
// Allocate memory for num_rows double-pointers
double** matrix = calloc(num_rows, sizeof(double*));
// return NULL if the memory couldn't allocated
if(matrix == NULL) return NULL;
// For each double-pointer (row) allocate memory for num_cols
// doubles
for(int i = 0; i < num_rows; i++){
matrix[i] = calloc(num_cols, sizeof(double));
// return NULL if the memory couldn't allocated
// and free the already allocated memory
if(matrix[i] == NULL){
for(int j = 0; j < i; j++){
free(matrix[j]);
}
free(matrix);
return NULL;
}
}
return matrix;
}
/* Fills the matrix with random double-numbers between -1 and 1 */
void randn_fill_matrix(double** matrix, int rows, int cols){
for (int i = 0; i < rows; ++i){
for (int j = 0; j < cols; ++j){
matrix[i][j] = (double) rand()/RAND_MAX*2.0-1.0;
}
}
}
/* Frees the memory allocated by the matrix */
void free_matrix(double** matrix, int rows, int cols){
for(int i = 0; i < rows; i++){
free(matrix[i]);
}
free(matrix);
}
/* Outputs the matrix to the console */
void print_matrix(double** matrix, int rows, int cols){
for(int i = 0; i < rows; i++){
for(int j = 0; j < cols; j++){
printf(" %- f ", matrix[i][j]);
}
printf("\n");
}
}
int main(){
srand(time(NULL));
int m = 3, n = 3;
double** A = init_matrix(m, n);
randn_fill_matrix(A, m, n);
print_matrix(A, m, n);
free_matrix(A, m, n);
return 0;
}
나는 일을 위해 무언가를 프로그래밍하는 동안 오늘 이중 포인터를 사용 했으므로 왜 우리가 그것들을 사용해야했는지 대답 할 수 있습니다 (실제로 이중 포인터를 사용해야했던 것은 처음입니다). 일부 구조의 멤버 인 버퍼에 포함 된 프레임의 실시간 인코딩을 처리해야했습니다. 엔코더에서는 이러한 구조 중 하나에 대한 포인터를 사용해야했습니다. 문제는 포인터가 다른 스레드의 다른 구조를 가리 키도록 변경되었다는 것입니다. 인코더에서 현재 구조를 사용하려면 다른 스레드에서 수정 된 포인터를 가리 키기 위해 이중 포인터를 사용해야했습니다. 적어도 우리에게는이 접근법을 취해야한다는 것이 처음에는 분명하지 않았습니다. 프로세스에서 많은 주소가 인쇄되었습니다 :)).
응용 프로그램의 다른 위치에서 변경된 포인터를 작업 할 때는 이중 포인터를 사용해야합니다. 또한 반환 및 주소를 지정하는 하드웨어를 처리 할 때 이중 포인터가 필수임을 알 수 있습니다.
변수의 수정 값과 포인터 의 수정 값을 비교하십시오 .
#include <stdio.h>
#include <stdlib.h>
void changeA(int (*a))
{
(*a) = 10;
}
void changeP(int *(*P))
{
(*P) = malloc(sizeof((*P)));
}
int main(void)
{
int A = 0;
printf("orig. A = %d\n", A);
changeA(&A);
printf("modi. A = %d\n", A);
/*************************/
int *P = NULL;
printf("orig. P = %p\n", P);
changeP(&P);
printf("modi. P = %p\n", P);
free(P);
return EXIT_SUCCESS;
}
이것은 포인터가 호출 된 함수 (단일 링크 된 목록에서 사용)에 의해 수정 될 때 포인터 값을 반환하지 않도록 도와줍니다.
OLD (나쁜) :
int *func(int *P)
{
...
return P;
}
int main(void)
{
int *pointer;
pointer = func(pointer);
...
}
새로운 (더 나은) :
void func(int **pointer)
{
...
}
int main(void)
{
int *pointer;
func(&pointer);
...
}
double*
.