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 个字节不能被其他部分使用程序,但属于程序的地址空间,写入它们可能不会被检测到)。但是当分配的块的末尾位于页面边界上时,它很容易崩溃。
此外,由于您为后续的sprintfs 将写入偏移量提前了四个字节,如果字符串表示(不包括 0 终止符)使用了超过四个字节(而程序没有),则前一个数字的一部分将被覆盖t 由于写入未分配的内存而崩溃)。
线
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*)。这通常是 8 个或 12 个字节,因此在分配的区域之外写入并不会导致崩溃或内存损坏(这可能会在很久以后导致崩溃)。
您必须跟踪分配给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 的簿记数据中找到大小。