【问题标题】:C reference gone after for loop在 for 循环之后的 C 引用
【发布时间】:2013-04-09 02:02:47
【问题描述】:

我的 C 代码有问题。

int split(char* source, char*** target, char* splitChar) {
    int i;
    int currentLength;
    int splitCharPosition;
    char* currentSubstring = source;
    int splitCount = charcount(source, splitChar) + 1;

    *target = (char**) malloc(splitCount * sizeof(char**));
    for(i=0;i<splitCount;i++) {
        splitCharPosition = indexOf(currentSubstring, splitChar);
        substring(currentSubstring, target[i], 0, splitCharPosition);
        currentLength = strlen(currentSubstring);
        substring(currentSubstring, &currentSubstring, splitCharPosition + 1, curr  entLength-splitCharPosition);
    }
    return splitCount;
}

问题是,如果我使用调试器,在第一次运行 for 循环后,指向 splitChar 的指针被设置为 0x0。 有人知道为什么设置为 0x0 吗?

编辑:

int indexOf(char* source, char* template) {
int i;
int j;
int index;
for (i = 0; source[i]; i++) {
    index = i;
    for (j = 0; template[j]; j++) {
        if (source[i + j] != template[j]) {
            index = -1;
            break;
        }
    }
    if (index != -1) {
        return index;
    }
}
return -1;
}

EDIT2:

int charcount(char* source, const char* countChar) {
int i;
int count = 0;
for(i=0;source[i];i++) {
    if(source[i] == countChar[0]) {
        count++;
    }
}
return count;
}

EDIT3:

char* substring(char* source, char** target, int start, int length) {
    *target = (char*) malloc(length + 1);
    strncpy(*target, source + start, length);
    target[length] = '\0';
    return *target;
}

编辑4: 我只是注意到,如果我添加

char* sndfpgjps = splitChar;

对于我的 split() 代码,它不会删除引用。有人知道为什么吗?

【问题讨论】:

  • 您在来这里发布您的问题之前提到您使用了调试器而获得了支持。
  • 这可能只是我,但我觉得这段代码对于它想要实现的目标来说过于复杂。
  • 你唯一接触splitChar的地方是indexOf(),所以先看看那里(或显示功能)
  • splitChar 也被传递给charcount(),所以也向我们展示这个函数。
  • char*** target 是一种 非常 难闻的 IMO。

标签: c loops pointers for-loop


【解决方案1】:

这一行:-

    substring(currentSubstring, &currentSubstring, splitCharPosition + 1, curr  entLength-splitCharPosition);

... 会导致内存泄漏,并且效率极低。旧的子字符串悬空。并且从未被释放。

这样写会更好

currentSubString += splitCharPosition + 1;

我不认为这是 问题,但这是 一个 问题。

另外,既然您正在使用像 strlen() 这样的 C 库函数,为什么不使用 strtok 或更好的 strtok_r

【讨论】:

  • 是否有可以将给定字符串拆分为字符串的C库函数?
  • 标准 C 库中没有函数可以根据您想要的分隔符字符串拆分字符串。最接近的方法是 strtok() 函数,但它会拆分任何单个字符,而不是字符串(并且有许多其他问题 - 尽可能避免 strtok();使用 strtok_r() 或 @ 987654329@ 当你不能)。一个原因是标准 C 库几乎在所有地方都避免了内存分配(当然,malloc()realloc()calloc()free() 除外)。 TR 24731-2 将改变这一点;它没有被 C2011 采用。
【解决方案2】:

我对代码有一些保留,但这在valgrind 下可以正常工作(没有泄漏,没有滥用)。除了常量字符串被标记为常量外,我基本上没有改变子函数。 split() 中的代码已被简化。正如我在评论中指出的那样,我建议编写主要的 split() 函数,以便您拥有一个本地 char **string_list; 来分配和填充。然后,当您要返回时,分配*target = string_list;。这将使您更容易理解正在发生的事情。三重间接是讨厌的。你可以在这里(只是)证明它的合理性,但尽量减少你花在使用三指针上的时间。修订版采用了该策略。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern int split(const char *source, char ***target, const char *splitStr);

static int
indexOf(const char *source, const char *template)
{
    int i;
    int j;
    int index;
    for (i = 0; source[i]; i++)
    {
        index = i;
        for (j = 0; template[j]; j++)
        {
            if (source[i + j] != template[j])
            {
                index = -1;
                break;
            }
        }
        if (index != -1)
            return index;
    }
    return -1;
}

static int
charcount(const char *source, const char *countChar)
{
    int count = 0;
    for (int i = 0; source[i]; i++)
    {
        if (source[i] == countChar[0])
            count++;
    }
    return count;
}

static char *
substring(const char *source, int start, int length)
{
    char *target = (char *)malloc(length + 1);
    if (target != 0)
    {
        memmove(target, source + start, length);
        target[length] = '\0';
    }
    return target;
}

int
split(const char *source, char ***target, const char *splitStr)
{
    int    splitCount = charcount(source, splitStr) + 1;
    char **result = (char **)malloc(splitCount * sizeof(*result));

    if (result == 0)
        return -1;

    int    splitLength = strlen(splitStr);
    char **next = result;
    const char *currentSubstring = source;

    for (int i = 0; i < splitCount; i++)
    {
        int splitCharPosition = indexOf(currentSubstring, splitStr);
        if (splitCharPosition < 0)
            break;
        *next++ = substring(currentSubstring, 0, splitCharPosition);
        currentSubstring += splitCharPosition + splitLength;
    }
    *next++ = substring(currentSubstring, 0, strlen(currentSubstring));
    *target = result;
    return (next - result);     /* Actual number of strings */
}

static void print_list(int nstrings, char **strings)
{
    for (int i = 0; i < nstrings; i++)
    {
        if (strings[i] != 0)
            printf("%d: <<%s>>\n", i, strings[i]);
    }
}

static void free_list(int nstrings, char **strings)
{
    for (int i = 0; i < nstrings; i++)
        free(strings[i]);
    free(strings);
}

int main(void)
{
    const char source[] = "This is a string; it is really!";
    char **strings;
    int nstrings;

    nstrings = split(source, &strings, " ");
    printf("Splitting: <<%s>> on <<%s>>\n", source, " ");
    print_list(nstrings, strings);
    free_list(nstrings, strings);

    nstrings = split(source, &strings, "is");
    printf("Splitting: <<%s>> on <<%s>>\n", source, "is");
    print_list(nstrings, strings);
    free_list(nstrings, strings);

    return 0;
}

请注意,在第二个示例中,charcount() 返回 6 但只有 4 个字符串。这导致对源代码的后期调整。 (你可以realloc()result,所以它的大小正好合适,但除非确实标记了差异——比如“超过 10 个条目”,否则它可能不值得担心。)错误处理并不完美;在分配失败后它不会访问无效内存,但它也不会停止尝试分配。它也不会报告分配单个字符串失败 - 它会报告分配指针数组失败。

我可能会通过创建一个结构来避免三重指针:

typedef struct StringList
{
    size_t     nstrings;
    char     **strings;
} StringList;

然后,您可以将指向其中之一的指针传递给split(),以及free_list()print_list() 等实用函数。然后free_list() 函数将修改结构,以便在结构指向的数据被释放后两个元素都归零。

我也很想使用 indexOf() 的不同实现:

int indexOf(const char *haystack, const char *needle)
{
    const char *pos = strstr(haystack, needle);
    if (pos != 0)
        return (pos - haystack);
    return -1;
}

【讨论】:

    【解决方案3】:

    我不知道子字符串是做什么的,也不知道它有什么签名,但是在行中

    substring(currentSubstring, target[i], 0, splitCharPosition);
    

    target[i] 只为 i==0 定义。我相信你想写

    substring(currentSubstring, (*target)[i], 0, splitCharPosition);
    

    【讨论】:

    • 你是对的。那是我一直想做的。但不幸的是,这并不能解决我的问题。
    • 这是对一个问题的正确诊断。我建议编写主要的split() 函数,以便您有一个本地char **string_list; 来分配和填充。然后,当您要返回时,分配*target = string_list;。这将使您更容易理解正在发生的事情。三重间接是讨厌的。你可以在这里(只是)证明它的合理性,但尽量减少你花在使用三重指针上的时间,如图所示。
    【解决方案4】:

    查看您的调试器是否也支持数据断点,即如果修改了内存中的某个位置,则中断。然后将一个放在 splitChar 的实际地址,另一个放在它指向的地址。 (因为您没有指定指针是 null 还是指向 nil。)看看它在哪里中断。可能是完全不相关的地方;这将表明缓冲区溢出。

    此外,您至少可以将 splitChar 设为指向 const 的指针。您实际上并不想修改它,对吗?更好的主意,让它成为一个字符,而不是一个指针,因为它的名字暗示你只有一个字符可以分割,而不是一个字符串。

    【讨论】:

    • 用 const 试过了,还是不行。名字可能选错了,我也想拆分成一个字符串。
    【解决方案5】:

    第一次调用substring 看起来不正确:

    substring(currentSubstring, target[i], 0, splitCharPosition);
    

    我怀疑它应该像下面这样索引实际分配的内存:

    substring(currentSubstring, &((*target)[i]), 0, splitCharPosition);
    

    您首先需要获取目标指向的值 (*target),然后从中索引并传递该数组位置的地址。

    【讨论】:

    • 你是对的。那是我一直想做的。但不幸的是,这并不能解决我的问题。
    猜你喜欢
    • 2018-01-07
    • 2017-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多