2015-12-25 64 views
1

我正在玩弄字符串的「胖指針」想法。基本上我有頭結構容量和長度信息。我分配預設長度的字符,然後將指針返回到第一個字符。當我想要標題信息時,我會減去'sizeof'標題。Realloc在字符串調整大小功能中失敗

所有功能都正常工作,我希望他們除了調整大小功能的方式:

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

[更新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); 
} 
+2

'#define is ==''#define isnt!=' - 請不要! –

+0

你也可以發佈valgrind顯示的錯誤嗎? – Jay

+0

用Valgrind日誌更新了這個問題......我稍微玩了一下,似乎指針沒有被改變......(它仍然指向舊地址)...用於隨後的調用。 –

回答

2

您試圖使用字符串指針作爲字符串結構的代理。但是字符串結構可能會重新分配,因此字符串的地址可能會更改。爲了使調用者(例如)string_append_char知道改變,他們必須有一些機制來接收字符串指針的新值。但他們沒有;他們通過uint8_t*並獲得bool。如果追加引起重新分配,則新地址將在string_append_char返回時丟失。

您可以通過傳遞句柄(即uint8_t**)而不是簡單的uint8_t*來實現。但是在許多方面,這都破壞了界面。至少,您最終會使用&str和其他str進行一些調用,這會使您的代碼變得脆弱而難以閱讀。

真的,您不妨直接使用string結構,幷包含一個內聯函數來提取C字符串指針,類似於C++接口。額外的間接性看起來似乎有點低效,但事實證明編程起來要容易得多。

+0

是的...對於正常使用它是正確的答案......但是因爲這是用於語言實驗,我在代碼嘗試中做了很多奇怪的事情break'n'make它最終我使用傳遞哈特函數操縱字符串和指向字符串的指針來創建/銷燬函數。 –

+0

@ jr.root.cs:夠公平的。你可能想考慮更簡單的表達式'(uint8_t *)(@ hdr [1])'和'((string_header_t *)str)[ - 1]' – rici

+0

...並且可能包裝內聯靜態函數'hdr2str'和'str2hdr' – rici

0

下面是我爲另一個SO回寫的「智能」字符串套件。它可能會有所幫助:

// javapgmr/xstr -- "smart" string "class" for C 

typedef struct { 
    size_t xstr_maxlen;     // maximum space in string buffer 
    char *xstr_lhs;      // pointer to start of string 
    char *xstr_rhs;      // pointer to current string append 
} xstr_t; 

// xstrinit -- reset string buffer 
void 
xstrinit(xstr_t *xstr) 
{ 

    memset(xstr,0,sizeof(xstr)); 
} 

// xstragain -- reset string buffer 
void 
xstragain(xstr_t xstr) 
{ 

    xstr->xstr_rhs = xstr->xstr_lhs; 
} 

// xstrgrow -- grow string buffer 
void 
xstrgrow(xstr_t *xstr,size_t needlen) 
{ 
    size_t curlen; 
    size_t newlen; 
    char *lhs; 

    lhs = xstr->xstr_lhs; 

    // get amount we're currently using 
    curlen = xstr->xstr_rhs - lhs; 

    // get amount we'll need after adding the whatever 
    newlen = curlen + needlen + 1; 

    // allocate more if we need it 
    if ((newlen + 1) >= xstr->xstr_maxlen) { 
     // allocate what we'll need plus a bit more so we're not called on 
     // each add operation 
     xstr->xstr_maxlen = newlen + 100; 

     // get more memory 
     lhs = realloc(lhs,xstr->xstr_maxlen); 
     xstr->xstr_lhs = lhs; 

     // adjust the append pointer 
     xstr->xstr_rhs = lhs + curlen; 
    } 
} 

// xstraddchar -- add character to string 
void 
xstraddchar(xstr_t *xstr,int chr) 
{ 

    // get more space in string buffer if we need it 
    xstrgrow(xstr,1); 

    // add the character 
    *xstr->xstr_rhs++ = chr; 

    // maintain the sentinel/EOS as we go along 
    *xstr->xstr_rhs = 0; 
} 

// xstraddstr -- add string to string 
void 
xstraddstr(xstr_t *xstr,const char *str) 
{ 
    size_t len; 

    len = strlen(str); 

    // get more space in string buffer if we need it 
    xstrgrow(xstr,len); 

    // add the string 
    memcpy(xstr->xstr_rhs,str,len); 
    xstr->xstr_rhs += len; 

    // maintain the sentinel/EOS as we go along 
    *xstr->xstr_rhs = 0; 
} 

// xstrcstr -- get the "c string" value 
char * 
xstrcstr(xstr_t *xstr,int chr) 
{ 

    return xstr->xstr_lhs; 
} 

// xstrfree -- release string buffer data 
void 
xstrfree(xstr_t *xstr) 
{ 
    char *lhs; 

    lhs = xstr->xstr_lhs; 
    if (lhs != NULL) 
     free(lhs); 

    xstrinit(xstr); 
} 
+0

謝謝!我有類似的智能字符串,但我想讓它變得更聰明,因爲如果我使用標題的隱藏(如頂部的函數所示),那麼對於世界來說,字符串顯示爲字符串*,可以簡單放到printf()等,因爲它指向第一個字符串字符。 我寧願搜索如何處理realloc函數,如果它是正確的,或者我應該修改函數來識別指針變化。 –

+0

我已閱讀您的調整大小,它似乎沒關係。但是,我會編輯你的問題,併發布結構定義和一些更多的功能,因爲問題的根源可能在頭的初始創建中[未顯示]。此外,每當我做這樣的「隱藏」結構時,我會在結構中放入一個幻數簽名來檢查是否存在損壞/錯位。 「智能」結構就像C++ std :: string。所有呼叫者傳遞指向它的好處通常是一個好處(例如,許多有用的func可能)。獲取C字符串是微不足道的,因爲在我的xstrcstr –

+0

添加了所需的代碼... –