2012-07-11 61 views
0

我需要把char *一些uint32_t和uint16_t數字放進去。然後我需要從緩衝區中取回它們。從uint32/16_t創建一個字符串,然後解析回原始數字

我已經閱讀了一些問題,我試着用sprintf把它們放到char *和sscanf中再次得到原始數字。但是,我無法正確地獲取它們。

下面是我的代碼只有2個數字的例子。但我需要超過2個,這就是爲什麼我使用realloc。另外,我不知道怎麼用sprintf和sscanf妥善uint16_t

uint32_t gid = 1100; 
uint32_t uid = 1000; 
char* buffer = NULL; 
uint32_t offset = 0; 

buffer = realloc(buffer, sizeof(uint32_t)); 
sprintf(buffer, "%d", gid); 
offset += sizeof(uint32_t); 

buffer = realloc(buffer, sizeof(uint32_t) + sizeof(buffer)); 
sprintf(buffer+sizeof(uint32_t), "%d", uid); 

uint32_t valorGID; 
uint32_t valorUID; 

sscanf(buffer, "%d", &valorGID); 
buffer += sizeof(uint32_t); 
sscanf(buffer, "%d", &valorUID); 

printf("ValorGID %d ValorUID %d \n", valorGID, valorUID); 

而我得到的是

ValorGID 11001000 ValorUID 1000 

我需要得到的是

ValorGID 1100 ValorUID 1000 

我新的C,所以任何幫助,將不勝感激。

回答

1
buffer = realloc(buffer, sizeof(uint32_t)); 
sprintf(buffer, "%d", gid); 
offset += sizeof(uint32_t); 

buffer = realloc(buffer, sizeof(uint32_t) + sizeof(buffer)); 
sprintf(buffer+sizeof(uint32_t), "%d", uid); 

這並沒有真正意義,並如預期除了在幸運的情況下將無法正常工作。

讓我們假設通常的CHAR_BIT == 8成立,所以sizeof(uint32_t) == 4。此外,讓我們假設int是無填充位的二進制補碼錶示中的帶符號的32位整數。

sprintf(buffer, "%d", gid)gid的位模式的十進制字符串表示形式解釋爲int打印到緩衝區。在上述假設下,gid被解釋爲-2147483648和2147483647之間的數字。因此,十進制串表示可以包含'-',包含1到10個數字和0終止符,總共使用2到12個字節。但是你只分配了四個字節,所以無論何時999 < gid < 2^32-99(有符號二進制補碼解釋是> 999< -99),sprintf都會寫入已分配的緩衝區大小。

這是未定義的行爲。

它很可能不會立即崩潰,因爲分配四個字節通常會有效地爲您提供更大的內存塊(例如,如果malloc總是返回16字節的對齊塊,則直接位於分配的四個後面的12個字節不能被其他部分該程序,但屬於該程序的地址空間,寫入它們可能不會被發現)。但是,當分配塊的末尾位於頁面邊界上時,它很容易崩潰。

此外,由於您將後續sprintf s的寫偏移量提前四個字節,如果字符串表示形式(不包括0-termnator)使用多於四個字節(而程序沒有但由於寫入未分配的內存而崩潰)。

buffer = realloc(buffer, sizeof(uint32_t) + sizeof(buffer)); 

包含更多的錯誤。

  1. buffer = realloc(buffer, new_size);失去了參照分配的存儲器,並導致泄漏如果realloc失敗。使用臨時並檢查新分配的成功

    char *temp = realloc(buffer, new_size); 
    if (temp == NULL) { 
        /* reallocation failed, recover or cleanup */ 
        free(buffer); 
        exit(EXIT_FAILURE); 
    } 
    /* it worked */ 
    buffer = temp; 
    /* temp = NULL; or let temp go out of scope */ 
    
  2. 新的大小sizeof(uint32_t) + sizeof(buffer)總是相同的,sizeof(uint32_t) + sizeof(char*)。這通常是八或十二個字節,所以在分配區域之外寫入並不需要太多數字,並導致崩潰或內存損壞(這可能會在很久之後導致崩潰)。

必須跟蹤分配到buffer字節數,並用它來計算新的大小。沒有(便攜式的)方式來確定從指針到其開始的已分配內存塊的大小。


現在的問題是你想要在緩衝區中存儲字符串表示還是位模式。

存儲字符串表示的問題是字符串表示的長度隨着值的變化而變化。因此,您需要在數字表示之間包含分隔符,或者在必要時通過填充(空格或前導零)確保所有表示具有相同的長度。這將例如像

#include <stdint.h> 
#include <inttypes.h> 

#define MAKESTR(x) # x 
#define STR(x) MAKESTR(x) 

/* A uint32_t can use 10 decimal digits, so let each field be 10 chars wide */ 
#define FIELD_WIDTH 10 

uint32_t gid = 1100; 
uint32_t uid = 1000; 

size_t buf_size = 0, offset = 0; 
char *buffer = NULL, *temp = NULL; 
buffer = realloc(buffer, FIELD_WIDTH + 1); /* one for the '\0' */ 
if (buffer == NULL) { 
    exit(EXIT_FAILURE); 
} 
buf_size = FIELD_WIDTH + 1; 
sprintf(buffer, "%0" STR(FIELD_WIDTH) PRIu32, gid); 
offset += FIELD_WIDTH; 

temp = realloc(buffer, buf_size + FIELD_WIDTH); 
if (temp == NULL) { 
    free(buffer); 
    exit(EXIT_FAILURE); 
} 
buffer = temp; 
temp = NULL; 
buf_size += FIELD_WIDTH; 
sprintf(buffer + offset, "%0" STR(FIELD_WIDTH) PRIu32, uid); 
offset += FIELD_WIDTH; 
/* more */ 

uint32_t valorGID; 
uint32_t valorUID; 

/* rewind for scanning */ 
offset = 0; 

sscanf(buffer + offset, "%" STR(FIELD_WIDTH) SCNu32, &valorGID); 
offset += FIELD_WIDTH; 
sscanf(buffer + offset, "%" STR(FIELD_WIDTH) SCNu32, &valorUID); 

printf("ValorGID %u ValorUID %u \n", valorGID, valorUID); 

與零填充固定寬度字段。如果您希望使用分隔符而不是固定寬度,則所需長度和偏移量的計算會變得更加複雜,但除非數字較大,否則將佔用更少的空間。

如果你寧願存儲位模式,這將是存儲的最簡潔的方式,你會使用類似

size_t buf_size = 0, offset = 0; 
unsigned char *buffer = NULL, temp = NULL; 
buffer = realloc(buffer, sizeof(uint32_t)); 
if (buffer == NULL) { 
    exit(EXIT_FAILURE); 
} 
buf_size = sizeof(uint32_t); 
for(size_t b = 0; b < sizeof(uint32_t); ++b) { 
    buffer[offset + b] = (gid >> b*8) & 0xFF; 
} 
offset += sizeof(uint32_t); 

temp = realloc(buffer, buf_size + sizeof(uint32_t)); 
if (temp == NULL) { 
    free(buffer); 
    exit(EXIT_FAILURE); 
} 
buffer = temp; 
temp = NULL; 
buf_size += sizeof(uint32_t); 
for(size_t b = 0; b < sizeof(uint32_t); ++b) { 
    buffer[offset + b] = (uid >> b*8) & 0xFF; 
} 
offset += sizeof(uint32_t); 

/* And for reading the values */ 
uint32_t valorGID, valorUID; 

/* rewind */ 
offset = 0; 
valorGID = 0; 
for(size_t b = 0; b < sizeof(uint32_t); ++b) { 
    valorGID |= buffer[offset + b] << b*8; 
} 
offset += sizeof(uint32_t); 
valorUID = 0; 
for(size_t b = 0; b < sizeof(uint32_t); ++b) { 
    valorUID |= buffer[offset + b] << b*8; 
} 
offset += sizeof(uint32_t); 

¹如果你知道如何malloc等工作在您的實施中,可以從malloc的簿記數據中找到大小。

+0

非常感謝您的反饋。正如我所說,我是C新手。我不知道我必須檢查realloc是否失敗。 第一個解決方案,字符串表示,就是我所需要的。 我會執行它。 再次感謝您的時間! – Fdiazreal 2012-07-11 20:26:25

+0

不客氣。我希望你沒有得到我對你的印象,只是在C程序員必須做錯誤檢查,沒有很好的例外,告訴你到底哪一行代碼出了問題。當然,檢查'malloc/realloc'失敗很有可能永遠不會檢測到失敗。但是有一次,它會讓你高興。 – 2012-07-11 20:36:23

0
uint32_t gid = 1100; 
uint32_t uid = 1000; 
char* buffer = NULL; 
uint32_t offset = 0; 

buffer = realloc(buffer, sizeof(uint32_t)); 
sprintf(buffer, "%d", gid); 
offset += sizeof(uint32_t); 

buffer = realloc(buffer, sizeof(uint32_t) + sizeof(buffer)); 
sprintf(buffer+sizeof(uint32_t), "%d", uid); 

uint32_t valorGID; 
uint32_t valorUID; 

sscanf(buffer, "%4d", &valorGID); 
buffer += sizeof(uint32_t); 
sscanf(buffer, "%d", &valorUID); 

printf("ValorGID %d ValorUID %d \n", valorGID, valorUID); 

`

我想,這也許能解決問題!

+0

是的,但我需要添加多個數字。有時超過4位數字。 – Fdiazreal 2012-07-11 20:32:00

1

格式說明符'%d'用於int,因此對於uint32_t是錯誤的。第一個uint32_t是一個無符號類型,所以你至少應該使用'%u',但它可能也有不同的寬度,比intunsigned。標準中預見了宏:PRIu32printfSCNu32scanf。舉個例子:

sprintf(buffer, "%" PRIu32, gid); 
+0

我可以使用該宏進行打印和其他功能嗎?謝謝! – Fdiazreal 2012-07-11 20:21:08

+0

當然,還有一大堆類似的宏和其他類型和說明符。 – 2012-07-11 20:44:15

1

sprintf返回的表示形式是char *。如果你試圖存儲一個整數數組作爲它們的字符串表示,那麼你的基本數據類型是char **。如果我們只存儲字符串數據本身,這是一個粗糙的字符矩陣,但由於最長的字符串可以產生10個字符,加上一個用於終止空值,因此預先分配這麼多字節來保存每個字符串是有意義的。

所以要存儲n uint32_t的公司從陣列中的陣列S作爲字符串:

const size_t kMaxIntLen=11; 

uint32_t *a,b; 
// fill a somehow 
... 

size_t n,i; 
char **s.*d; 

if((d=(char*)malloc(n*kMaxIntLen))==NULL) 
    // error! 
if((s=(char**)malloc(n*sizeof(char*)))==NULL) 
    // error! 
for(i=0;i<n;i++) 
    { 
    s[i]=d+i; // this is incremented by sizeof(char*) each iteration 
    snprintf(s[i],kMaxIntLen,"%u",a[i]); // snprintf to be safe 
    } 

現在的第i個數字是在S [I]這樣打印出來就是printf("%s",s[i]);,並檢索它作爲一個整數到bsscanf(s[i],"%u",&b);

隨後的內存管理有點棘手。而不是經常使用realloc()來增長緩衝區,最好預先分配一塊內存,並且只在耗盡時才改變它。如果realloc()失敗,則返回NULL,因此在調用它之前將指針存儲到主緩衝區中,這樣就不會丟失對數據的引用。首先重新分配d緩衝區 - 再次爲多個字符串分配足夠的空間 - 然後如果成功,請參閱d已更改。如果是這樣,請破壞(free()s緩衝區,malloc()它再次並重建索引(因爲如果d已更改所有索引已過時,您必須這樣做)。如果沒有,realloc()s並修復新的指數。我建議在結構包裝這件事,並具有一組例程來操作就可以的,例如:

typedef struct StringArray 
{ 
char **strArray; 
char *data; 
size_t nStrings; 
} StringArray; 

這是一個大量的工作。你有用C嗎?作爲C++ STL vector<string>list<string>istringstream類和push_back()容器方法,這非常容易。

+0

是的,我正在寫一個軟件在ext2 FS中做操作,作爲大學的練習。這是我們第一次使用C. – Fdiazreal 2012-07-11 20:20:08

相關問題