如何为每个字符在C中为双指针分配内存char **

我的问题是当我们使用char **时,如何为单词的每个字符分配内存。 我想这样做

words = malloc(sizeof(char*) * numberOfWords); 

当我准备复制字符时:

 int i = 0; while(some condition){ (*words)[i] = malloc(1); //here I am getting error "Incompatible pointer to integer conversion assigning to char from void*" arrayOfWords++; i++; } 

所以我想要做的是一次为每个字符分配它的空间,而不是为一些固定的最大字节数。 单词的长度可能会严重不同

提前致谢!

这已经变得比预期的要长一些,但这是如何分配单词列表的承诺示例。 为了使示例简单,我们假设我们要在空白处拆分字符串。 给定NUL终止的字符串,我们想要创建一个由NUL终止的字符串的NULL终止数组,表示单个字。 不应该浪费任何记忆。

为了给出一个完整的工作示例(只是连接以下代码块),这里是我们需要的头文件:

 #include  #include  #include  #include  #include  

我们将分配一些内存,所以我们最好考虑正确地释放它。 让我们从那开始:

 void free_words(char * * words) { /* Treat NULL gracefully for consistency with the standard libary's free(). */ if (words != NULL) { size_t i; for (i = 0; words[i] != NULL; ++i) free(words[i]); free(words); } } 

请注意,我们要求单词数组以NULL结尾。 否则,我们没有机会分辨到它的结束。

现在到tokenizer。 我们可以循环遍历字符串两次并提前计算它有多少个单词。 但总的来说,这可能是不可能的,例如,如果字符串实际上是连续的输入流。 我还想展示一个数组如何动态增长。 如果增长数组,我们应该总是通过乘法因子(通常为2)增加其大小,以保持线性摊销的渐近复杂度。 (如果您不知道“线性摊销的渐近复杂度”是什么意思,请将推荐的程序作为最佳实践。)

 char * * tokenize(const char *const sentence) { size_t capacity = 1; size_t word_count = 0; ssize_t word_start = -1; size_t i = 0; char * * words = malloc(capacity * sizeof(char *)); char * * temp; if (words == NULL) goto fail; words[word_count] = NULL; do { if (isspace(sentence[i]) || sentence[i] == '\0') { if (word_start >= 0) { /* We have found the end of the current word. */ const size_t word_length = i - word_start; char * word; if (word_count + 1 >= capacity) { /* We need to grow the array. */ capacity *= 2; temp = realloc(words, capacity * sizeof(char *)); if (temp == NULL) goto fail; words = temp; } word = malloc((word_length + 1) * sizeof(char)); if (word == NULL) goto fail; strncpy(word, sentence + word_start, word_length); word[word_length] = '\0'; words[word_count++] = word; words[word_count] = NULL; word_start = -1; } } else { if (word_start < 0) { /* We have found the begin of a new word. */ word_start = i; } } } while (sentence[i++]); /* Trim the array to the exact size needed. */ temp = realloc(words, (word_count + 1) * sizeof(char *)); if (temp == NULL) goto fail; words = temp; return words; fail: free_words(words); return NULL; } 

实际找到单词边界的逻辑很简单,但我不会在这里解释,因为这与内存管理问题几乎没有任何关系。

每当我们找到一个新单词的结尾时,我们检查该数组是否仍然足够大以容纳它并在需要时增长它。 在确保数组足够大之后,我们分配足够的内存来保存下一个字,复制数据并将其插入到数组中。 我们还需要注意使用NUL字节和带有NULL指针的字数组来终止字符串。

我们正在跟踪数组在可变capacity的当前容量以及到目前为止在变量word_count插入的word_count 。 当我们决定增长数组时,我们使用realloc函数尝试调整为该指针保留的空间量 - 如果不可能 - 分配新空间,复制数据并释放旧数据。

在我们最终返回数组之前,我们将其大小调整为实际需要的大小。 这样做是否有用,可能会引发辩论。 我只想表明你可以做到。

在我们分配内存的任何时候,我们都应该准备好处理内存不足的情况。 标准库的函数通过返回NULL报告此情况。 如果我们的内存不足,我们也会通过返回NULL使操作失败。 但是,如果没有首先释放到目前为止分配的内存,我们就不能这样做。 你可能会因为我为此目的而使用goto感到冒犯,但事实certificate这是一种相当普遍且普遍接受的语言function用法。 (像这样使用它只是模仿exception会给我们的function,如果只有C的话。)

有关其精确语义,请参阅mallocreallocfree的手册页。 这是一个相当严肃的建议:我刚看到太多代码滥用它们,特别是在极端情况下。

要完成示例,以下是我们的函数的使用方法:

 int main() { const char sentence[] = "The quick brown fox jumps over the sleazy dog."; char * * words = tokenize(sentence); size_t i; if (words == NULL) return EXIT_FAILURE; for (i = 0; words[i] != NULL; ++i) { printf("words[%ld] = '%s'\n", i, words[i]); } free_words(words); return EXIT_SUCCESS; } 

最后一句话:在这个例子中,将所有单词存储在同一个数组中并将它们分开NUL字节并使用指向每个单词开头的指针的第二个数组会更有效。 这将只使用两个堆分配的数组,并将数据更紧密地放在内存中,从而提高访问效率。 作为练习,您可以尝试相应地修改上面的示例。 (提示:你需要重新分配很多东西。)一般来说,为了性能和可维护性的原因,尽量保持堆分配指针的数量尽可能小。 绝对不要分配单个字节。

我看到的问题:

这条线:

 *words = malloc(sizeof(char*) * numberOfWords); 

应该:

 words = malloc(sizeof(char*) * numberOfWords); 

这条线:

 (*words)[i] = malloc(1); 

应该:

 words[i] = malloc(1); 

words[i]的类型是char*(*words)[i]char 。 编译器抱怨将指针( malloc的返回值)赋值给char

听起来你想要一个动态字符串,每次你添加一个字符时它会自动增加它的长度。 在C中执行此操作相当复杂,需要了解动态内存分配和数据结构。

一种策略是首先分配固定的内存块并逐渐向其添加数据(在本例中为字符)。 填满分配的内存后,需要分配一个新的更大的内存块(通常是原始块大小的两倍),然后将数据复制到新的内存块中。

请注意,此策略不会一次增加一个字节的已分配内存的大小。 这样做会非常低效。

这或多或少是标准数据结构在其他语言和/或库中的作用(即C ++中的std::stringstd::vector以及Java中的ArrayList )。

 #include  #include  #include  char **copy_words(char *src_words[], int numberOfWords){ char **words = malloc(sizeof(char*) * numberOfWords); int i = 0; while(i < numberOfWords){ words[i] = malloc(strlen(src_words[i]) + 1); strcpy(words[i], src_words[i]); i++; } return words; } int main(void){ char *words[] = { "first", "second", "..end"}; int numOfWords = sizeof(words)/sizeof(*words); char **clone = copy_words(words, numOfWords); int i; for(i = 0; i < numOfWords; ++i){ printf("%s\n", clone[i]); free(clone[i]); } free(clone); return 0; } 

你可以让一个永远增长的字符串很容易……

  #define STRING_CHUNK_SIZE 100 typedef struct { char* s; unsigned int size; unsigned int allocated_size; } string; void string_create(string* s) { s->s = malloc(STRING_CHUNK_SIZE); s->s[0] = 0; s->size = 0; s->allocated_size = STRING_CHUNK_SIZE; } void string_add(string* s, char* str) { int len = strlen(str); if(s->size + len + 1 >= s->allocated_size) { int room = s->allocated_size - s->size; int needed = len+1-room; int togrow = needed / STRING_CHUNK_SIZE; if(needed % STRING_CHUNK_SIZE) togrow += STRING_CHUNK_SIZE; s->allocated_size += togrow; s->s = realloc(s->s, s->allocated_size); } s->size += len; strcat(s->s, str); } char* string_p(string* s) { return s->s; } void string_destroy(string* s) { free(s->s); } 

你可以使用类似的东西

 int i; string s; string_create(&s); string_add(&s, "blah"); printf("%s\r\n", string_p(&s)); for(i = 0; i<100; i++) { string_add(&s, "blah"); } printf("%s\r\n", string_p(&s)); string_destory(&s); 

如果你真的想要一次分配1个字节,那么将#define块大小更改为1