【发布时间】:2017-11-27 00:36:28
【问题描述】:
我开始学习 c 编程,并且尝试将字符串拆分为子字符串列表。我创建了一个用于创建字符串的结构,称为 String。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct dynamic_string {
char* data;
size_t capacity;
size_t size;
} String;
void free_string(String* s) { free(s->data); s->data = NULL; }
void print_string(String* s) { for (int i = 0; i < s->size; ++i) { printf("%c", s->data[i]); } printf("\n"); }
void clear_string(String* s) { s->size = 0; s->data[0] = '\0'; }
int string_init(String* s, size_t init_capacity) {
s->data = malloc(init_capacity);
if (!s->data) { return -1; };
s->size = 0; s->capacity = init_capacity;
return 0;
}
void add_char(String* s, char element) {
size_t new_size = s->size + 1;
if (new_size > s->capacity) { s->capacity = s->capacity << 1; s->data = realloc(s->data, s->capacity); }
s->data[s->size] = element;
s->size = new_size;
}
void add_charp(String* s, char* string) {
size_t stringlen = strlen(string);
size_t new_size = s->size + stringlen;
if (new_size > s->capacity) { s->capacity = new_size << 1; s->data = realloc(s->data, s->capacity); }
strcpy(s->data + s->size, string);
s->size = new_size;
}
void add_string(String* s, String* string) {
size_t stringlen = string->size;
size_t new_size = s->size + stringlen;
if (new_size > s->capacity) { s->capacity = new_size << 1; s->data = realloc(s->data, s->capacity); }
strcpy(s->data + s->size, string->data);
s->size = new_size;
}
现在这不是问题所在。当我尝试创建这些字符串的列表时会出现问题。我还为此做了一个结构。
typedef struct dynamic_string_pointer {
String* data;
size_t capacity;
size_t size;
} StringVec;
void free_stringvec(StringVec* sv) { free(sv->data); sv->data = NULL; }
void print_stringvec(StringVec* sv) { for (int i = 0; i < sv->size; ++i) { print_string(&sv->data[i]); } }
int stringvec_init(StringVec* sv, size_t init_capacity) {
sv->data = malloc(init_capacity * sizeof(String));
if (!sv->data) { return -1; }
sv->size = 0; sv->capacity = init_capacity;
return 0;
}
void add_string_to_vec(StringVec* sv, String string) {
size_t new_size = sv->size + 1;
if (new_size > sv->capacity) { sv->capacity = sv->capacity << 1; sv->data = realloc(sv->data, sv->capacity * sizeof(String)); }
sv->data[sv->size] = string;
sv->size = new_size;
}
这种结构似乎没有问题,如果您向其中添加不同的字符串,它也可以正常工作。问题是当我构建一个字符串并使用相同的变量将其添加到列表中时。
例如:
void tokenize(char* text) {
StringVec tokens; stringvec_init(&tokens, 32);
String token; string_init(&token, 8);
bool first_char = false;
for (int i = 0; i < strlen(text); ++i) {
if (isToken(text[i])) {
if (!equals_charp(&token, "")) { add_string_to_vec(&tokens, token); }
clear_string(&token); add_char(&token, text[i]);
add_string_to_vec(&tokens, token);
clear_string(&token); first_char = false; continue;
}
if (text[i] != ' ' && text[i] != '\t' && text[i] != '\n'){ add_char(&token, text[i]); first_char = true; }
else if (first_char) { add_char(&token, text[i]); }
}
print_stringvec(&tokens);
free_string(&token);
free_stringvec(&tokens);
}
在这里,我试图通过使用变量 token 并将其添加到令牌来构建令牌列表。问题似乎是,当令牌传递给 add_string_to_vec 时,它在堆栈上始终具有相同的地址。这似乎是正确的,因为当我打印令牌时,它只是一遍又一遍地打印最后一个令牌。所以我尝试替换
sv->data[sv->size] = string; with memcpy(sv->data + sv->size, &string, sizeof(String));
认为这会将内存从字符串复制到 sv->data 以便在添加其他标记时它会起作用。这似乎有同样的问题。我通常使用具有 std::vector 和 std::string 的 c++,这使得这很容易。我可能只是愚蠢,或者我只是了解内存如何运作得足够好,但我无法找到一种方法来让它发挥作用。
【问题讨论】:
-
sv->data = malloc(init_capacity * sizeof(String));奇怪... 还有if (!equals_charp(&token, ""))非常 奇怪... -
顺便说一句,在
add_charp()中,如果new_size == s->capacitystrcpy()会将最终的\0写入未拥有的内存。 -
您只调用一次
string_init。这会分配一个缓冲区,然后在token字符串的所有副本之间共享该缓冲区。每当您复制String时,您都必须分配一个新缓冲区并从旧data复制字符。 (然后您将在确定何时致电free时遇到很多 的麻烦。 -
顺便说一句:
for (int i = 0; i < strlen(text); ++i) {...}是性能杀手。 (静态/内联函数是你的朋友) -
BTW2:如果您通常使用 C++,那么您不会从尝试用 C 编写 C++ 中学到任何东西。这可能就是您被 C 人否决的原因。