C: 구조물에 대한 포인터 배열에 대한 포인터(할당/할당 취소 문제)
저는 무언가를 위해 C에 다시 들어갔지만, 이 메모리 관리가 어떻게 작동하는지 대부분 기억하는 데 어려움을 겪고 있습니다.구조물에 대한 포인터 배열에 대한 포인터를 갖고 싶습니다.
내가 가지고 있다고 말하시오:
struct Test {
int data;
};
그런 다음 배열:
struct Test **array1;
이거 맞는건가요?제 문제는 이것을 다루는 것입니다.따라서 배열의 각 포인터는 개별적으로 할당된 것을 가리킵니다.하지만 제가 먼저 해야 할 일이 있습니다.
array1 = malloc(MAX * sizeof(struct Test *));
위의 내용을 이해하는 데 어려움을 겪고 있습니다.이 작업을 수행해야 하며, 왜 이 작업을 수행해야 하는 이유는 무엇입니까?특히 포인터가 가리키는 각 항목에 메모리를 할당하려면 포인터에 메모리를 할당한다는 것은 무엇을 의미합니까?
구조물에 대한 포인터 배열에 대한 포인터를 가지고 있다고 말합니다.이제 이전에 만든 어레이와 동일한 어레이를 가리키도록 하겠습니다.
struct Test **array2;
위에서 했던 것처럼 포인터를 위한 공간을 할당해야 합니까? 아니면 그냥 할 수 있습니까?
array2 = array1
할당된 배열
할당된 배열을 사용하면 충분히 쉽게 추적할 수 있습니다.
포인터 배열을 선언합니다.의 각 는 이배의각요다가리니다킵음을는소열을 .struct Test
:
struct Test *array[50];
그런 다음 원하는 대로 구조에 포인터를 할당하고 할당합니다.루프를 사용하는 것은 간단합니다.
array[n] = malloc(sizeof(struct Test));
그런 다음 이 배열에 대한 포인터를 선언합니다.
// an explicit pointer to an array
struct Test *(*p)[] = &array; // of pointers to structs
사할수있다를 사용할 수 .(*p)[n]->data
n번째 멤버를 참조합니다.
이 물건이 헷갈리더라도 걱정하지 마세요.그것은 아마도 C의 가장 어려운 측면일 것입니다.
동적 선형 배열
구조체 블록(구조체에 대한 포인터가 아닌 사실상 구조체의 배열)을 할당하고 블록에 대한 포인터를 가지려면 다음 작업을 더 쉽게 수행할 수 있습니다.
struct Test *p = malloc(100 * sizeof(struct Test)); // allocates 100 linear
// structs
그런 다음 이 포인터를 가리킬 수 있습니다.
struct Test **pp = &p
구조체에 대한 포인터 배열은 더 이상 없지만 전체를 상당히 단순화합니다.
동적으로 할당된 구조물의 동적 배열
가장 유연하지만 자주 필요하지는 않습니다.첫 번째 예제와 매우 유사하지만 추가 할당이 필요합니다.저는 이것을 잘 컴파일 할 수 있는 완전한 프로그램을 작성했습니다.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
struct Test {
int data;
};
int main(int argc, char **argv)
{
srand(time(NULL));
// allocate 100 pointers, effectively an array
struct Test **t_array = malloc(100 * sizeof(struct Test *));
// allocate 100 structs and have the array point to them
for (int i = 0; i < 100; i++) {
t_array[i] = malloc(sizeof(struct Test));
}
// lets fill each Test.data with a random number!
for (int i = 0; i < 100; i++) {
t_array[i]->data = rand() % 100;
}
// now define a pointer to the array
struct Test ***p = &t_array;
printf("p points to an array of pointers.\n"
"The third element of the array points to a structure,\n"
"and the data member of that structure is: %d\n", (*p)[2]->data);
return 0;
}
출력:
> p points to an array of pointers.
> The third element of the array points to a structure,
> and the data member of that structure is: 49
아니면 전체 세트:
for (int i = 0; i < 100; i++) {
if (i % 10 == 0)
printf("\n");
printf("%3d ", (*p)[i]->data);
}
35 66 40 24 32 27 39 64 65 26
32 30 72 84 85 95 14 25 11 40
30 16 47 21 80 57 25 34 47 19
56 82 38 96 6 22 76 97 87 93
75 19 24 47 55 9 43 69 86 6
61 17 23 8 38 55 65 16 90 12
87 46 46 25 42 4 48 70 53 35
64 29 6 40 76 13 1 71 82 88
78 44 57 53 4 47 8 70 63 98
34 51 44 33 28 39 37 76 9 91
단일 동적 할당 구조물의 동적 포인터 배열
이 마지막 예는 다소 구체적입니다.이전 예제에서 살펴본 것처럼 동적인 포인터 배열이지만, 이와 달리 요소는 모두 단일 할당으로 할당됩니다.이것은 원래 할당을 방해하지 않으면서 다른 구성으로 데이터를 정렬하는 데 가장 주목할 만한 용도가 있습니다.
가장 기본적인 단일 블록 할당에서와 같이 단일 요소 블록을 할당하는 것으로 시작합니다.
struct Test *arr = malloc(N*sizeof(*arr));
이제 별도의 포인터 블록을 할당합니다.
struct Test **ptrs = malloc(N*sizeof(*ptrs));
그런 다음 포인터 목록의 각 슬롯을 원래 배열 중 하나의 주소로 채웁니다.포인터 산술을 사용하면 요소에서 요소 주소로 이동할 수 있으므로 이는 간단합니다.
for (int i=0;i<N;++i)
ptrs[i] = arr+i;
이 시점에서 다음 두 가지 모두 동일한 요소 필드를 참조합니다.
arr[1].data = 1;
ptrs[1]->data = 1;
그리고 위의 내용을 검토한 후에 그 이유가 명확해졌으면 좋겠습니다.
포인터 배열과 원래 블록 배열을 마치면 다음과 같이 해제됩니다.
free(ptrs);
free(arr);
: 각항을자사수용없다습니의 각 하지 않습니다.ptrs[]
.그것은 그들이 할당된 방식이 아닙니다.블록으로 되었습니다.arr
), 그리고 그것이 그들이 해방되어야 하는 방법입니다.
그런데 왜 누군가가 이걸 하고 싶어할까요?몇 가지 이유.
첫째, 메모리 할당 호출 수를 획기적으로 줄입니다. 히려오.N+1
(포인터 배열의 경우 하나, 개별 구조의 경우 N) 이제 배열 블록의 경우 하나, 포인터 배열의 경우 하나만 있습니다.메모리 할당은 프로그램이 요청할 수 있는 가장 비싼 작업 중 하나이며, 가능하면 최소화하는 것이 좋습니다(참고: 파일 IO는 다른 작업입니다. fyi).
또 다른 이유:동일한 기본 데이터 배열에 대한 다중 표현입니다.데이터를 오름차순과 내림차순으로 정렬하고 정렬된 표현을 동시에 사용할 수 있도록 한다고 가정합니다.데이터 어레이를 복제할 수는 있지만, 이 경우 많은 양의 복사가 필요하고 메모리 사용량이 상당합니다.대신 추가 포인터 배열을 할당하고 기본 배열의 주소로 채운 다음 해당 포인터 배열을 정렬합니다.이는 정렬되는 데이터가 클 때(항목당 킬로바이트 또는 그보다 더 클 때) 특히 중요한 이점이 있습니다.원래 항목은 기본 배열의 원래 위치에 남아 있지만, 이제 실제로 항목을 이동하지 않고도 정렬할 수 있는 매우 효율적인 메커니즘을 갖게 되었습니다.항목에 대한 포인터 배열을 정렬하면 항목이 전혀 이동되지 않습니다.
저는 이것이 받아들이기 힘든 것이라는 것을 알지만, 포인터 사용은 당신이 C 언어로 할 수 있는 많은 강력한 것들을 이해하는 데 매우 중요합니다. 그러니 책을 읽고 계속 기억을 새롭게 하세요.그것은 돌아올 것입니다.
다른 사람들이 제안한 것처럼 실제 어레이를 선언하는 것이 더 나을 수도 있지만, 당신의 질문은 메모리 관리에 관한 것으로 보이므로 저는 그것에 대해 논의하겠습니다.
struct Test **array1;
이것은 주소에 대한 포인터입니다.struct Test
(구조체 자체에 대한 포인터가 아니라 구조체의 주소를 저장하는 메모리 위치에 대한 포인터입니다.)선언은 포인터에 메모리를 할당하지만 포인터가 가리키는 항목에는 할당하지 않습니다.포인터를 통해 어레이에 액세스할 수 있으므로 다음과 같은 작업을 수행할 수 있습니다.*array1
이 소유유형배형인 배열에 로서.struct Test
하지만 그것이 가리키는 실제 배열은 아직 없습니다.
array1 = malloc(MAX * sizeof(struct Test *));
하여 저장합니다.MAX
항에대포인터 유형의 에 대한 struct Test
다시 말하지만, 구조 자체에 메모리를 할당하지 않고 포인터 목록에만 할당합니다.하지만 이제 당신은 치료할 수 있습니다.array
할당된 포인터 배열에 대한 포인터로 사용됩니다.
사하기위를 사용하기 는.array1
실제 구조를 만들어야 합니다. 각 구조체를 만 하면 .
struct Test testStruct0; // Declare a struct.
struct Test testStruct1;
array1[0] = &testStruct0; // Point to the struct.
array1[1] = &testStruct1;
힙에 구조체를 할당할 수도 있습니다.
for (int i=0; i<MAX; ++i) {
array1[i] = malloc(sizeof(struct Test));
}
메모리를 할당했으면 동일한 구조 목록을 가리키는 새 변수를 만들 수 있습니다.
struct Test **array2 = array1;
. 메모리를 추가로 할당하기 때문입니다.array2
메모동메일가모리를당한다니킵리리할한와가▁points킵리니▁you다▁▁same▁to▁to'▁the에 할당한 메모리와 동일한 메모리를 가리킵니다.array1
.
때때로 포인터 목록에 대한 포인터를 갖고 싶지만, 화려한 것을 하지 않는 한
struct Test *array1 = malloc(MAX * sizeof(struct Test)); // Pointer to MAX structs
포인터가 됩니다.array1
에 충분한 할당MAX
점 구물, 및array1
그 기억까지.이제 다음과 같은 구조에 액세스할 수 있습니다.
struct Test testStruct0 = array1[0]; // Copies the 0th struct.
struct Test testStruct0a= *array1; // Copies the 0th struct, as above.
struct Test *ptrStruct0 = array1; // Points to the 0th struct.
struct Test testStruct1 = array1[1]; // Copies the 1st struct.
struct Test testStruct1a= *(array1 + 1); // Copies the 1st struct, as above.
struct Test *ptrStruct1 = array1 + 1; // Points to the 1st struct.
struct Test *ptrStruct1 = &array1[1]; // Points to the 1st struct, as above.
그럼 뭐가 달라요?분명한 첫 번째입니다. 두 번째 방법은 하나의 를 할당하고 구조 자체에 공간을 추가로 하는 것입니다.malloc()
전화입니다. 남는 일은 무엇을 사시나요?
첫 번째 방법이 실제 포인터 배열을 제공하기 때문입니다.Test
구조체, 각 포인터는 임의의 것을 가리킬 수 있습니다.Test
구조, 메모리의 모든 곳에서, 연속적일 필요는 없습니다.를 각 메모리에 하여 자유롭게 할 수 .Test
필요에 따라 구조를 지정하고 포인터를 재할당할 수 있습니다.예를 들어 포인터를 교환하는 것만으로 두 개의 구조를 바꿀 수 있습니다.
struct Test *tmp = array1[2]; // Save the pointer to one struct.
array1[2] = array1[5]; // Aim the pointer at a different struct.
array1[5] = tmp; // Aim the other pointer at the original struct.
반면에, 두 번째 방법은 모든 것을 위해 하나의 연속적인 메모리 블록을 할당합니다.Test
으로 분할합니다.MAX
그리고 어레이의 각 요소는 고정된 위치에 상주합니다. 두 구조를 교환하는 유일한 방법은 복사하는 것입니다.
포인터는 C에서 가장 유용한 구조 중 하나이지만 가장 이해하기 어려운 구조일 수도 있습니다.만약 당신이 C를 계속 사용할 계획이라면, 당신이 그것들에 익숙해질 때까지 포인터, 배열 및 디버거를 가지고 노는 데 시간을 보내는 것은 아마도 가치 있는 투자일 것입니다.
행운을 빕니다.
typdefs를 사용하여 한 번에 레이어를 작성하여 레이어를 작성하는 것이 좋습니다.그렇게 함으로써, 필요한 다양한 유형이 훨씬 더 명확해질 것입니다.
예를 들어:
typedef struct Test {
int data;
} TestType;
typedef TestType * PTestType;
이렇게 하면 구조체용과 구조체에 대한 포인터용으로 두 가지 유형이 새로 만들어집니다.
다음으로 구조체 배열을 원할 경우 다음을 사용합니다.
TestType array[20]; // creates an array of 20 of the structs
구조체에 대한 포인터 배열을 원하는 경우 다음을 사용합니다.
PTestType array2[20]; // creates an array of 20 of pointers to the struct
그런 다음 구조체를 배열에 할당하려면 다음과 같은 작업을 수행합니다.
PTestType array2[20]; // creates an array of 20 of pointers to the struct
// allocate memory for the structs and put their addresses into the array of pointers.
for (int i = 0; i < 20; i++) {
array2 [i] = malloc (sizeof(TestType));
}
C에서는 한 어레이를 다른 어레이에 할당할 수 없습니다.대신 루프를 사용하여 한 배열의 각 요소를 다른 배열의 요소에 할당해야 합니다.
편집: 또 다른 흥미로운 접근 방식
또 다른 접근 방식은 몇 가지 사항을 캡슐화하는 보다 객체 지향적인 접근 방식입니다.예를 들어 동일한 유형의 계층을 사용하여 두 가지 유형을 만듭니다.
typedef struct _TestData {
struct {
int myData; // one or more data elements for each element of the pBlob array
} *pBlob;
int nStructs; // count of number of elements in the pBlob array
} TestData;
typedef TestData *PTestData;
때, 이 이름이 . 충분히 적절한 이름이 지정되어 있습니다.CreateTestData (int nArrayCount)
.
PTestData CreateTestData (int nCount)
{
PTestData ret;
// allocate the memory for the object. we allocate in a single piece of memory
// the management area as well as the array itself. We get the sizeof () the
// struct that is referenced through the pBlob member of TestData and multiply
// the size of the struct by the number of array elements we want to have.
ret = malloc (sizeof(TestData) + sizeof(*(ret->pBlob)) * nCount);
if (ret) { // make sure the malloc () worked.
// the actual array will begin after the end of the TestData struct
ret->pBlob = (void *)(ret + 1); // set the beginning of the array
ret->nStructs = nCount; // set the number of array elements
}
return ret;
}
이제 아래 소스 코드 세그먼트와 같이 새 개체를 사용할 수 있습니다.CreateTestData()에서 반환된 포인터가 유효한지 확인해야 합니다. 하지만 이는 실제로 수행할 수 있는 작업을 보여주기 위한 것입니다.
PTestData go = CreateTestData (20);
{
int i = 0;
for (i = 0; i < go->nStructs; i++) {
go->pBlob[i].myData = i;
}
}
진정으로 역동적인 환경에서는 다음과 같은 환경을 원할 수도 있습니다.ReallocTestData(PTestData p)
을 TestData
개체에 포함된 배열의 크기를 수정하기 위한 개체입니다.
방법을 를 다 에 TestData 개체와 할 수 .free (go)
개체와 해당 배열이 동시에 해제됩니다.
편집: 추가 확장
이 캡슐화된 유형을 사용하여 이제 몇 가지 다른 흥미로운 작업을 수행할 수 있습니다.예를 들어 복사 기능을 사용할 수 있습니다.PTestType CreateCopyTestData (PTestType pSrc)
새 인스턴스를 만든 다음 인수를 새 개체에 복사합니다.에▁function 기능을 재사용합니다.PTestType CreateTestData (int nCount)
복사할 개체의 크기를 사용하여 유형의 인스턴스를 만듭니다.새 객체를 생성한 후 소스 객체에서 데이터의 복사본을 만듭니다.마지막 단계는 소스 개체에서 데이터 영역을 가리키는 포인터를 고정하여 새 개체의 포인터가 이전 개체의 데이터 영역이 아닌 자체의 데이터 영역을 가리키도록 하는 것입니다.
PTestType CreateCopyTestData (PTestType pSrc)
{
PTestType pReturn = 0;
if (pSrc) {
pReturn = CreateTestData (pSrc->nStructs);
if (pReturn) {
memcpy (pReturn, pSrc, sizeof(pTestType) + pSrc->nStructs * sizeof(*(pSrc->pBlob)));
pReturn->pBlob = (void *)(pReturn + 1); // set the beginning of the array
}
}
return pReturn;
}
구조는 다른 객체와 크게 다르지 않습니다.캐릭터부터 시작하겠습니다.
char *p;
p = malloc (CNT * sizeof *p);
이기 때문에 *p 문 자로므이는,,sizeof *p
는 (char) == 1의 크기이며, CNT 문자를 할당했습니다. 예:
char **pp;
pp = malloc (CNT * sizeof *pp);
문자에 이므로 *p는 문자에 대한 포인터입니다.sizeof *pp
크기는 (문자*)입니다.우리는 CNT 포인터를 할당했습니다. 예:
struct something *p;
p = malloc (CNT * sizeof *p);
이기 때문에 *p 것는이다인구, 그서래조.sizeof *p
의 크기입니다.우리는 CNT 구조를 할당했습니다. 예:
struct something **pp;
pp = malloc (CNT * sizeof *pp);
struct에 이므로 *pp는 구조대한포므로이인터에체,sizeof *pp
의 크기입니다.우리는 CNT 포인터를 할당했습니다.
언급URL : https://stackoverflow.com/questions/15397728/c-pointer-to-array-of-pointers-to-structures-allocation-deallocation-issues
'sourcecode' 카테고리의 다른 글
MySQL에서 이미지를 저장하는 데 사용할 데이터 유형은 무엇입니까? (0) | 2023.07.23 |
---|---|
MySQL에서 GROUP_CONCAT에서 1024자 이상을 되돌릴 수 있습니까? (0) | 2023.07.23 |
ORA-00955 "이름이 이미 기존 개체에서 사용되고 있습니다." (0) | 2023.07.23 |
바이트 배열을 Oracle RAW에서 System으로 변환합니다.가이드? (0) | 2023.07.23 |
스프링의 MockMvc를 저지 리소스와 함께 사용할 수 있습니까? (0) | 2023.07.23 |