[英]How to allocate memory of triple pointer in a C function
我是 C 新手,有一個案例,我想讀取一個簡單的文本文件,每行一個單詞,我想將它保存到一個數組中。 但是,我想在函數 main 中聲明一個雙指針,然后通過引用將其傳遞給另一個函數來分配內存並填充雙指針。 如果不拋出分段錯誤,我似乎無法讓它工作。 任何建議將不勝感激。
這是我嘗試過的:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
test.txt = content between the "---"
---
first
second
third
fourth
fifth
sixth
seventh
---
*/
void allocmem (char ***tptr) {
FILE *infile = fopen("test.txt", "r");
int bufferLength = 5;
char *buffer = malloc(sizeof(char) * bufferLength);
int c, i, row, rows;
c = i = row = 0;
rows = 7;
/*
I want to allocate an array of char strings to store the list from test.txt in
I have tried different variants here
*/
tptr = malloc(sizeof(char**) * rows);
for (int r = 0; r < rows; r++) {
*tptr[r] = malloc(sizeof(char*) * bufferLength);
}
/*
// another attempt
*tptr = malloc(sizeof(char*) * rows);
for (int r = 0; r < rows; r++) {
*tptr[r] = malloc(sizeof(char) * bufferLength);
}
*/
while ((c = getc(infile)) != EOF) {
if (c == '\n') {
buffer[++i] = '\0';
printf("buffer: %s\n", buffer);
// I am also a little unsure how to append my buffer to the pointer array here too
strcpy(*tptr[row++], buffer);
memset(buffer, '\0', bufferLength);
i = 0;
} else {
buffer[i++] = c;
}
}
fclose(infile);
}
int main () {
// double pointer to hold array value from test.txt in
char **dptr;
// allocate memory and read test.txt and store each row into the array
allocmem(&dptr);
/*
print each element of dptr here yielding the expected results of:
first
second
third
fourth
fifth
sixth
seventh
*/
return 0;
}
要回答您的問題,可以按type ***ptr
分配三重指針(如@David C. Rankin 所述),但這根本不實用。 該函數可以簡單地使用malloc
或calloc
分配內存並存儲結果,最后通過取消引用將指向結果的指針寫入參數給出的指針,例如*argument_ptr = result_pointer
。 我已經重寫了代碼以避免分段錯誤。 這是代碼:
void allocmem (char ***tptr) {
// The aim of this function is to return the array of strings read from the file
FILE *infile = fopen("test.txt", "r"); // Read text file
const int bufferLength = 10, number_of_strings = 7; // Read string 7 times, for a maximum length of 10
char **array = (char**)calloc(sizeof(char*), number_of_strings); // Allocate memory for a bunch of strings (char*), but the memory for the characters are not initialized
for (int i = 0; i < number_of_strings; i++){
char *string = (char*)calloc(sizeof(char), bufferLength); // Memory for the string, a.k.a. character array
fgets(string, bufferLength, infile); // Read the string (word) from file
char *c;
if (c = strchr(string, '\n')) *c = '\0'; // Remove the newline character if exists
array[i] = string; // Assign the string to the array
}
fclose(infile); // Close the file
*tptr = array; // Return the array created by modifying the given pointer
}
如果不拋出分段錯誤,我似乎無法讓它工作。
第 35 行和第 53 行的分段錯誤是由錯誤的解除引用引起的。 在第 35 行,您應該簡單地使用tptr[r]
來訪問char**
指針,這與第 53 行的情況相同。
雖然鏈接的示例比您在此處需要的要復雜得多,但原理是相同的。 當您聲明一個函數void
並消除返回指向新分配的內存塊的指針的能力時,您將需要傳遞需要為其分配存儲空間的指針的地址。
為什么是地址?
當您最初分配或重新分配時,內存塊的地址將發生變化。 如果您只是將指針傳遞給要分配的函數,則該函數會收到原始指針的副本,該副本具有自己的且非常不同的地址。 在 C 中,所有參數都是按值傳遞的,函數接收一個副本。 在函數返回時對函數中的副本所做的任何更改都將丟失。
但是,如果您傳遞變量的地址(指針間接的附加級別),則該函數會收到指向您的變量的指針的副本,但該指針保存原始指針的地址。 所以現在您可以進行更改並將新的內存塊分配給函數中取消引用的指針,這會將內存塊的新起始地址分配給原始指針,並且更改將在調用函數中看到。
在您的情況下,您希望從文件中讀取未知數量的字符串,並將它們添加到void
函數中分配的字符串集合。 如果您在main()
簡單地分配/重新分配,您可以簡單地聲明一個char **ptrs;
(雙指針),然后分配/重新分配從文件中讀取的每個字符串的指針數量,為字符串分配存儲空間,並將塊的地址分配給新分配的指針,然后復制字符串指向您的指針 - 並重復直到用完字符串。
在void
函數中分配和復制時唯一改變的是您將傳遞char **ptrs;
的地址char **ptrs;
到函數添加一級指針間接(因此函數參數類型將是char ***
,然后在函數內您必須刪除該附加一級間接以對指針進行操作。您還需要傳遞保存當前分配的指針數量的變量的地址——因此您知道在函數內需要重新分配realloc()
多少。
總而言之,你的函數可以寫成:
/* allocate pointer for *strings, allocate/copy s to (*strings)[*nptr] */
void allocmem (char ***strings, size_t *nptrs, const char *s)
{
size_t len = strlen (s); /* determine length of s */
/* reallocate *string to a temporary pointer, adding 1 pointer */
void *tmp = realloc (*strings, (*nptrs + 1) * sizeof **strings);
if (!tmp) { /* validate EVERY allocation/reallocation */
perror ("realloc-allocptr-strings");
exit (EXIT_FAILURE);
}
*strings = tmp; /* assign newly allocated block of pointers to *strings */
(*strings)[*nptrs] = malloc (len + 1); /* allocate storage for s */
if (!(*strings)[*nptrs]) { /* ditto */
perror ("malloc-(*strings)[*nptrs]");
exit (EXIT_FAILURE);
}
memcpy ((*strings)[*nptrs], s, len + 1); /* copy s to allocated storage */
*nptrs += 1; /* increment pointer count after successful allocation/copy */
}
(注意:在分配失敗時,程序簡單地退出——但對於實際代碼,您需要某種方式來知道是否發生了分配和字符串是否被復制。保存nptrs
的前/后計數可以給出部分圖片,但不能告訴你哪個分配失敗了——如果realloc()
失敗了,那么由於你使用了指向realloc()
的tmp
指針,你的指針和字符串在函數調用之前就存在了,並且仍然可以訪問,盡管原始指針)
(注2:括號周圍*strings
中(*strings)[*nptrs]
需要被因為在C運算符的優先級, [..]
的優先級高於所述引用操作'*'
)
將一個示例放在一起來讀取您的文件並存儲字符串是您分配的指針和字符串集合,您可以這樣做:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
/* allocate pointer for *strings, allocate/copy s to (*strings)[*nptr] */
void allocmem (char ***strings, size_t *nptrs, const char *s)
{
size_t len = strlen (s); /* determine length of s */
/* reallocate *string to a temporary pointer, adding 1 pointer */
void *tmp = realloc (*strings, (*nptrs + 1) * sizeof **strings);
if (!tmp) { /* validate EVERY allocation/reallocation */
perror ("realloc-allocptr-strings");
exit (EXIT_FAILURE);
}
*strings = tmp; /* assign newly allocated block of pointers to *strings */
(*strings)[*nptrs] = malloc (len + 1); /* allocate storage for s */
if (!(*strings)[*nptrs]) { /* ditto */
perror ("malloc-(*strings)[*nptrs]");
exit (EXIT_FAILURE);
}
memcpy ((*strings)[*nptrs], s, len + 1); /* copy s to allocated storage */
*nptrs += 1; /* increment pointer count after successful allocation/copy */
}
int main (int argc, char **argv) {
char buf[MAXC], /* temp storage for each line read from file */
**strings = NULL; /* must initialize pointer NULL */
size_t nptrs = 0; /* number of pointers allocated */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
buf[strcspn (buf, "\n")] = 0; /* trim \n from end of buf */
allocmem (&strings, &nptrs, buf); /* add string to strings */
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (size_t i = 0; i < nptrs; i++) { /* output all stored strings */
printf ("strings[%zu] : %s\n", i, strings[i]);
free (strings[i]); /* free storage for string */
}
free (strings); /* free pointers */
}
示例使用/輸出
$ ./bin/3starstrings dat/test1-7.txt
strings[0] : first
strings[1] : second
strings[2] : third
strings[3] : fourth
strings[4] : fifth
strings[5] : sixth
strings[6] : seventh
內存使用/錯誤檢查
在你寫的,可動態分配內存的任何代碼,您有任何關於分配的內存任何塊2個職責:(1)始終保持一個指針的起始地址的存儲器中,以便塊,(2),當它是沒有它可以被釋放不再需要。
您必須使用內存錯誤檢查程序來確保您不會嘗試訪問內存或寫入超出/超出分配塊的范圍,嘗試讀取或基於未初始化值的條件跳轉,最后確認你釋放了你分配的所有內存。
對於 Linux valgrind
是正常的選擇。 每個平台都有類似的內存檢查器。 它們都易於使用,只需通過它運行您的程序即可。
$ valgrind ./bin/3starstrings dat/test1-7.txt
==23799== Memcheck, a memory error detector
==23799== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==23799== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==23799== Command: ./bin/3starstrings dat/test1-7.txt
==23799==
strings[0] : first
strings[1] : second
strings[2] : third
strings[3] : fourth
strings[4] : fifth
strings[5] : sixth
strings[6] : seventh
==23799==
==23799== HEAP SUMMARY:
==23799== in use at exit: 0 bytes in 0 blocks
==23799== total heap usage: 17 allocs, 17 frees, 5,942 bytes allocated
==23799==
==23799== All heap blocks were freed -- no leaks are possible
==23799==
==23799== For counts of detected and suppressed errors, rerun with: -v
==23799== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始終確認您已釋放所有分配的內存並且沒有內存錯誤。
仔細檢查一下,如果您還有其他問題,請告訴我(請記住,被稱為3 星級程序員並不是一種恭維——而是從您的函數返回一個指針:)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.