c - Realloc 在字符串调整大小函数中失败

标签 c string pointers memory memory-leaks

我正在研究字符串的“胖指针”概念。基本上我有标题结构保持容量和长度信息。我用预设的字符长度分配它,然后将指针返回到第一个字符。当我想要标题信息时,我减去“sizeof”标题。

除调整大小功能外,所有功能都按照我期望的方式正常工作:

typedef uint8_t* utf8;

/*
 * Resize string
 */
bool string_resize( utf8 *str, size_t room ) {
    utf8* p = str;

    struct string_header *hdr = (string_header_t *) (*p - sizeof(string_header_t));

    size_t cap = hdr->capacity;
    size_t len = hdr->length;

    /* Backup the current capacity if the process fails */
    size_t bck = cap;

    if ( len + room <= cap ) {
        //printf("::hit\n");
        return true;
    }

    cap = len + room;

    if ( cap < MAX_PREALLOC ) {
        cap *= 2;
    } else {
        cap += MAX_PREALLOC;
    }

    hdr->capacity = cap;

    void * new = realloc( hdr, sizeof(string_header_t) + cap + 1 );

    if ( new == NULL ) {
        hdr->capacity = bck;
        return false;
    }

    *str = (utf8) new + sizeof(string_header_t);
    /* Remove garbage if there is any  after the string content */
    memset( *str+len, 0, cap-len + 1 );
    return true;
}

Valgrind 返回我在未由 malloc 分配的内存中读取的错误(尝试访问字符串的新部分时总是发生)。

如您所见,我使用(没有 typedef)uint8_t** 所以我应该将正确的指针传递给指向函数的指针,然后更改它。

非常感谢任何帮助。

[更新 1] 字符串操作上下文的附加函数:

typedef struct string_header {
     size_t capacity;
     size_t length;
} string_header_t;

/*
 * Allocate the string with the prefered length.
 */
utf8 string_alloc( size_t len ) {
    struct string_header *hdr = calloc(1, sizeof(string_header_t) + sizeof(uint8_t) * len);
    assert( hdr );
    hdr->capacity = len;
    hdr->length   = 0;
    return ((utf8) hdr) + sizeof(string_header_t);
}

/*
 * Allocate the new string with the initial default capacity.
 */
utf8 string_new() {
    return string_alloc( INITIAL_CAPACITY );
}

/*
 * Delete the string.
 */
void string_dealloc( utf8 self ) {
    if ( self == NULL )
        return;
    string_header_t *hdr = (string_header_t *) (self - sizeof(string_header_t));
    free(hdr);
}

static inline void string_push( utf8 s, char c ) {
    string_header_t* hdr = (string_header_t *) (s - sizeof(string_header_t));
    //*(s + hdr->length++) = (uint8_t) c;
    size_t len = hdr->length++;

    s[len] = c;
}

bool string_append_char( utf8 str, char c ) {
    if ( string_resize(&str, 1) != ARDP_SUCCESS )
        return ARDP_FAILURE;

    string_push( str, c );
    return ARDP_SUCCESS;
}

bool string_append_utf8( utf8 s, int cp ) {
    if ( cp < 0 or cp > 0x10ffff ) {
        return false;
    }
    else if ( cp < 0x80 ) {
        return string_append_char(s, cp & 0x7F);
    }
    else if ( cp < 0x800 ) {
        if ( string_resize( &s, 2 ) isnt ARDP_SUCCESS )
            return false;
        string_push( s, 0xC0 | ((cp >> 6) & 0x1F) );
        string_push( s, 0x80 | (cp & 0x3F) );
    }
    else if ( cp < 0x10000 ) {
        if ( string_resize( &s, 3 ) isnt ARDP_SUCCESS )
            return false;
        string_push( s, 0xE0 | ((cp >> 12) & 0xF) );
        string_push( s, 0x80 | ((cp >> 6)  & 0x3F) );
        string_push( s, 0x80 |  (cp & 0x3F) );
    }
    else {
        if ( string_resize( &s, 4 ) isnt ARDP_SUCCESS )
            return false;
        string_push( s, 0xF0 | ((cp >> 18) & 0x7) );
        string_push( s, 0x80 | ((cp >> 12) & 0x3F) );
        string_push( s, 0x80 | ((cp >> 6)  & 0x3F) );
        string_push( s, 0x80 |  (cp & 0x3F) );
    }
    return true;
}

bool string_finish( utf8 str ) {
    if ( string_resize(&str, 1) )
        return false;

    string_header_t *hdr = (string_header_t *) (str - sizeof(string_header_t));
    *(str + hdr->length) = '\0';
     return true;
}

[update 2] Valgrind 日志(几乎都和这个一样):

==96370== Invalid read of size 8
==96370==    at 0x100011201: string_append_char (string.c:68)
==96370==    by 0x100000AE7: test_string (example.c:84)  
==96370==    by 0x100000BEA: main (example.c:106)
==96370==  Address 0x100aac6d0 is 0 bytes inside a block of size 24 free'd
==96370==    at 0x1000098B8: realloc (in /usr/local/Cellar/valgrind/HEAD/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==96370==    by 0x100011243: string_append_char (string.c:92)
==96370==    by 0x100000ADA: test_string (example.c:83)
==96370==    by 0x100000BEA: main (example.c:106)
==96370==  Block was alloc'd at
==96370==    at 0x100009551: calloc (in /usr/local/Cellar/valgrind/HEAD/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==96370==    by 0x1000110F2: string_new (string.c:38)
==96370==    by 0x100000A5A: test_string (example.c:72)
==96370==    by 0x100000BEA: main (example.c:106)

==96370== Invalid write of size 8
==96370==    at 0x100011274: string_append_char (string.h:44)
==96370==    by 0x100000AE7: test_string (example.c:84)
==96370==    by 0x100000BEA: main (example.c:106)
==96370==  Address 0x100aac6d8 is 8 bytes inside a block of size 24 free'd
==96370==    at 0x1000098B8: realloc (in /usr/local/Cellar/valgrind/HEAD/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==96370==    by 0x100011243: string_append_char (string.c:92)
==96370==    by 0x100000ADA: test_string (example.c:83)
==96370==    by 0x100000BEA: main (example.c:106)
==96370==  Block was alloc'd at
==96370==    at 0x100009551: calloc (in /usr/local/Cellar/valgrind/HEAD/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==96370==    by 0x1000110F2: string_new (string.c:38)
==96370==    by 0x100000A5A: test_string (example.c:72)
==96370==    by 0x100000BEA: main (example.c:106)

[更新 3] 一些示例代码:

void test_string(void) {
    utf8 str = string_new();

    string_debug( str );
    string_append_char( str, 'h');
    string_append_char( str, 't');
    string_append_char( str, 't');
    string_append_char( str, 'p'); 
    string_append_char( str, ':');
    string_append_char( str, '/');
    string_append_char( str, '/');
    string_append_char( str, 'g');
    string_append_char( str, 'o');
    string_append_char( str, 'o');
    string_append_char( str, 'g');
    string_append_char( str, 'l');
    string_append_char( str, 'e');
    string_append_char( str, '.');
    string_append_char( str, 'c');
    string_append_char( str, 'o');
    string_append_char( str, 'm');
    string_append_char( str, '/');
    string_append_char( str, '?');
    string_append_char( str, 's');
    string_append_char( str, '=');
    string_append_char( str, 'f');
    string_append_char( str, 'i');
    string_append_char( str, 's');
    string_append_char( str, 'h');

    //string_finish(str);

    printf("String %s", str);

    string_dealloc(str);
}

最佳答案

您正在尝试使用字符串指针作为字符串结构的代理。但是字符串结构可能会被重新分配,因此字符串的地址可能会改变。为了让(例如)string_append_char 的调用者知道更改,他们必须有某种机制来接收字符串指针的新值。但他们没有;他们传入一个 uint8_t* 并返回一个 bool。如果附加导致重新分配,一旦 string_append_char 返回,新地址将丢失。

您可以通过传递句柄(即 uint8_t**)而不是简单的 uint8_t* 来做到这一点。但在很多方面,这都违背了界面的意义。至少,您最终会使用 &str 和其他 str 进行一些调用,这将使您的代码脆弱且难以阅读。

实际上,您还不如直接使用string 结构,并包含一个内联函数来提取C 字符串指针,类似于C++ 接口(interface)。额外的间接级别可能看起来有点低效,但事实证明使用它编程要容易得多。

关于c - Realloc 在字符串调整大小函数中失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34467050/

相关文章:

c - 在结构中初始化一个 const 数组

c - AES_ctr128_encrypt() "Segmentation fault"(OpenSSL)

c++ - 为什么 C++ 删除运算符不将指针设置为 NULL?

c - Linux C 文件的 md5sum

C - 在线程中的函数中传递字符串并返回它会导致不存在、不可打印的字符串?

java - 将数据从一个 Activity 传递到另一个 Activity 并保存

xml - 从 xml 标签中提取 QString

c - 使用指针在 C 中对结构进行排序

c - 传递给函数的 malloc 数组的地址 - 段错误

c - 尝试为动态分配的二维数组赋值时程序崩溃