【问题标题】:Creating a list of strings in c not working?在c中创建字符串列表不起作用?
【发布时间】: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-&gt;data[sv-&gt;size] = string; with memcpy(sv-&gt;data + sv-&gt;size, &amp;string, sizeof(String));

认为这会将内存从字符串复制到 sv->data 以便在添加其他标记时它会起作用。这似乎有同样的问题。我通常使用具有 std::vector 和 std::string 的 c++,这使得这很容易。我可能只是愚蠢,或者我只是了解内存如何运作得足够好,但我无法找到一种方法来让它发挥作用。

【问题讨论】:

  • sv-&gt;data = malloc(init_capacity * sizeof(String)); 奇怪... 还有if (!equals_charp(&amp;token, "")) 非常 奇怪...
  • 顺便说一句,在add_charp() 中,如果new_size == s-&gt;capacity strcpy() 会将最终的\0 写入未拥有的内存。
  • 您只调用一次string_init。这会分配一个缓冲区,然后在token 字符串的所有副本之间共享该缓冲区。每当您复制String 时,您都必须分配一个新缓冲区并从旧data 复制字符。 (然后您将在确定何时致电free 时遇到很多 的麻烦。
  • 顺便说一句:for (int i = 0; i &lt; strlen(text); ++i) {...} 是性能杀手。 (静态/内联函数是你的朋友)
  • BTW2:如果您通常使用 C++,那么您不会从尝试用 C 编写 C++ 中学到任何东西。这可能就是您被 C 人否决的原因。

标签: c string memory


【解决方案1】:

我按照 Bo Persson 所说的做了这些功能。现在它工作得很好。 (我稍微改变了 tokenize 函数的工作方式。)

String next_token(char* text, size_t size, int* start) {
    String token; string_init(&token, 4);

    char last_char = ' ';
    for (int i = *start; i < size; ++i) {
        if (last_char == ' ' && text[i] == ' ') { continue; }
        if (text[i] == '\t' || text[i] == '\n') { if (last_char != ' ') { add_char(&token, ' '); } continue;}
        if (isToken(text[i])) {
            *start = i;
            if (token.size == 0) { *start += 1; add_char(&token, text[i]); }
            return token;
        }
        add_char(&token, text[i]);
        last_char = text[i];
    }

    return token;
}

StringVec tokenize(char* text) {
    StringVec tokens; stringvec_init(&tokens, 32);

    size_t textlen = strlen(text);

    for (int i = 0; i < textlen;) {
        String token = next_token(text, textlen, &i);
        add_string_to_vec(&tokens, token);
    }

    return tokens;
}

我在这里释放令牌。

void free_stringvec(StringVec* sv) { 
    for (int i = 0; i < sv->size; ++i) {
        free_string(&sv->data[i]); 
    } 
    free(sv->data); sv->data = NULL; 
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-04-15
    • 2015-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-29
    相关资源
    最近更新 更多