循环后C引用消失了

时间:2013-04-16 15:22:07

标签: c loops pointers for-loop

我的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;
}

EDIT4: 我刚注意到,如果我添加

char* sndfpgjps = splitChar;

到我的split()代码,它不会删除引用。谁知道为什么?

5 个答案:

答案 0 :(得分:3)

这一行: -

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

...会导致内存泄漏,并且效率极低。旧的子串留下悬空。并且永远不会被释放。

会好得多
currentSubString += splitCharPosition + 1;

我不认为这是 问题,但它是 问题。

另外,当您使用strlen()等C库函数时,为什么不使用strtok或更好,strtok_r

答案 1 :(得分: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;
}

答案 2 :(得分:1)

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

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

target [i]仅针对i == 0定义。我相信你想写

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

答案 3 :(得分:0)

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

另外,你至少可以使用splitChar指向const的指针。你真的不想修改它,对吗?更好的想法,使它成为一个字符,而不是一个指针,因为它的名字表明只有一个字符可以拆分,而不是字符串。

答案 4 :(得分:0)

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

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

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

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

首先需要获取目标指向(*target)的值,然后将其索引并传递该阵列位置的地址。