c - 代码中可能存在内存错误以及可能的解决方案?

标签 c regex valgrind

下面给出的源代码是一些更详细的 C 源代码的精简版本,它解析输入字符串以查看它们是否与预先确定的模式匹配。该代码尝试解析输入字符串(您可以假设它是有效的空终止字符串)。如果 字符串包含有效的无符号整数,函数返回 0,否则返回错误 -1。无符号整数与正则表达式 ^[0-9]+$ 匹配。

我尝试运行 valgrind 命令来找出可能的错误,该错误显示以下输出(我无法理解)。

==15269== 
==15269== Invalid read of size 1
==15269==    at 0x400770: parse_exact (assign2b.c:23)
==15269==    by 0x400957: xtz_parse_unsigned (assign2b.c:82)
==15269==    by 0x400A26: test_parse_unsigned (assign2b.c:102)
==15269==    by 0x400B06: main (assign2b.c:128)
==15269==  Address 0x51f2045 is 0 bytes after a block of size 5 alloc'd
==15269==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==15269==    by 0x4EBAD81: strdup (strdup.c:43)
==15269==    by 0x400AF1: main (assign2b.c:127)
==15269== 
==15269== Invalid read of size 1
==15269==    at 0x400770: parse_exact (assign2b.c:23)
==15269==    by 0x400957: xtz_parse_unsigned (assign2b.c:82)
==15269==    by 0x400A26: test_parse_unsigned (assign2b.c:102)
==15269==    by 0x400B9B: main (assign2b.c:142)
==15269==  Address 0x51f2135 is 0 bytes after a block of size 5 alloc'd
==15269==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==15269==    by 0x400B72: main (assign2b.c:140)

下面给出的是代码。请告诉代码中的错误和可能的解决方案,以及如何使用 valgrind 推导出相同的错误

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

    #define OK 9999
    #define EOS '\0'
    #define XT_SUCCESS 0
    #define XT_FAIL -1

    typedef int (*PARSE_FUNC)(const char *s, const char **endptr);

    static int parse_exact(const char *s, const char **endptr, PARSE_FUNC pfunc)
    {
        const char *cp = s;
        int c;
        int state = 1;
        while (state != XT_SUCCESS && state != XT_FAIL)
        {
            c = *cp++;  // nextchar
            switch(state)
            {
            case 1:
                state = pfunc(--cp, endptr);
                cp = *endptr;
                if (state == XT_SUCCESS) state = 2;
                else cp++;  // on FAIL jump ahead to get undone on exit
                break;
            case 2:
                if (EOS == c) state = OK;
                else state = XT_FAIL;
                break;
            case OK:
                state = XT_SUCCESS;
                break;
            default:
                /* LOGIC ERROR */
                assert(0==1);
                break;
            }
        }
        if (endptr) 
            *endptr = --cp;
        return state;
    }

    static int base_unsigned(const char *s, const char **endptr)
    {
        const char *cp = s;
        int c;
        int state = 1;
        while (state != XT_SUCCESS && state != XT_FAIL)
        {
            c = *cp++;  // getnextchar
            switch(state)
            {
            case 1:
                if (isdigit(c)) state = 2;
                else state = XT_FAIL;
                break;
            case 2:
                if (isdigit(c)) state = 2;
                else state = XT_SUCCESS;
                break;
            default:
                /* LOGIC ERROR */
                assert(0==1);
                break;
            }
        }
        if (endptr) 
            *endptr = --cp;
        return state;
    }

    int xtz_parse_unsigned(const char *s, const char **endptr)
    {
        PARSE_FUNC pfunc = base_unsigned;
        return parse_exact(s, endptr, pfunc);
    }

    void xt_pr_error(int status, const char *s, const char *endptr)
    {
        if (0 != status)
        {
            if (endptr[0])
                printf("ERROR: '%c' at position %d is not allowed", *endptr, (endptr - s)+1);
            else if ((endptr - s) > 0)
                printf("ERROR: cannot end with '%c'", endptr[-1]);
            else
                printf("ERROR: value is empty");
        }
    }

    void test_parse_unsigned(const char *s, int expected)
    {
        int status;
        const char *endptr; // Ptr to first invalid character
        status = xtz_parse_unsigned(s, &endptr);
        printf("Test input='%s' status=%d ", s, status);
        xt_pr_error(status, s, endptr);
        if (status != expected)
            printf(" NOT EXPECTED!\n");
        else
            printf(" (OK)\n");
    }


    int main(void)
    {
        char s1234[] = "1234";
        char s12a4[] = "12a4";
        char *ptr;

        // Tests with string literals
        test_parse_unsigned("1234", XT_SUCCESS);
        test_parse_unsigned("12a4", XT_FAIL);

        // Tests with static strings arrays
        test_parse_unsigned(s1234, XT_SUCCESS);
        test_parse_unsigned(s12a4, XT_FAIL);

        // Tests using strdup()
        ptr = strdup("1234");
        test_parse_unsigned(ptr, XT_SUCCESS);
        free(ptr);

        ptr = strdup("123a");
        test_parse_unsigned(ptr, XT_FAIL);
        free(ptr);

        ptr = strdup("1a34");
        test_parse_unsigned(ptr, XT_FAIL);
        free(ptr);

        // Test using malloc and strcpy()
        ptr = malloc(5);
        strcpy(ptr, "1234");
        test_parse_unsigned(ptr, XT_SUCCESS);
        free(ptr);

        return 0;
    }

最佳答案

很难从你的代码中判断出真正的错误在哪里,你必须使用调试器来检查它。但从它的外观来看,读取超过 1 个字节,您的字符串没有正确以 null 终止,或者您没有很好地处理该情况。

valgrind 指向的函数有点难以掌握,因为你没有明确的字符串结束条件,即当 c'\0 时'

另外:

*cp++这样的东西属于博物馆,不要使用表达式来消除它们的副作用。在这里,您可以轻松地使用 for 循环,以 cp 作为迭代变量,而不是 while 循环

for (const char *cp = s;
     state != XT_SUCCESS && state != XT_FAIL;
     ++cp) {
    ...
}

如果您一周后回来,将状态变量与命名常量和数字混合使用对于其他人或您自己来说是疯狂的且难以理解的

关于c - 代码中可能存在内存错误以及可能的解决方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22804263/

相关文章:

javascript - 通过jquery在日期&lt;input&gt;框中自动添加斜杠/符号

java - 使用正则表达式删除左括号之前的字符串

c - 将结构数据复制到链表时出现段错误

c++ - 抑制 GTK+ 中的 Valgrind 错误

c++ - 在 massif 工具中跟踪子进程的命令

c - Scanf矩阵在c中不起作用?

python - 在 Python 中删除字符串中的最后一个斜杠和数字

c - 3D vector ,返回指针还是返回结构?

c - C中是否允许 boolean 返回类型?

c - 以下程序的输出解释