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