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));
包含更多的錯誤。
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 */
新的大小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
的簿記數據中找到大小。
非常感謝您的反饋。正如我所說,我是C新手。我不知道我必須檢查realloc是否失敗。 第一個解決方案,字符串表示,就是我所需要的。 我會執行它。 再次感謝您的時間! – Fdiazreal 2012-07-11 20:26:25
不客氣。我希望你沒有得到我對你的印象,只是在C程序員必須做錯誤檢查,沒有很好的例外,告訴你到底哪一行代碼出了問題。當然,檢查'malloc/realloc'失敗很有可能永遠不會檢測到失敗。但是有一次,它會讓你高興。 – 2012-07-11 20:36:23