我需要将一些 uint32_t 和 uint16_t 数字放入 char* 中。然后我需要从缓冲区中取回它们。
我已经阅读了一些问题,并尝试使用 sprintf 将它们放入 char* 中,然后 sscanf 再次获取原始数字。但是,我无法正确获取它们。
这是我的代码示例,只有 2 个数字。但我需要超过 2 个,这就是我使用 realloc 的原因。另外,我不知道如何通过 uint16_t 正确使用 sprintf 和 sscanf
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 的新手,所以任何帮助将不胜感激。
最佳答案
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 字节对齐的 block ,分配的四个字节后面的十二个字节不能被其他部分使用的程序,但属于程序的地址空间,并且写入它们可能不会被发现)。但是当分配的 block 的末尾位于页面边界时,它很容易在以后崩溃。
此外,由于您将后续的 sprintf
的写入偏移量提前了四个字节s,如果字符串表示(不包括 0 终止符)使用超过四个字节(同时程序还没有因写入未分配的内存而崩溃),则前一个数字的一部分将被覆盖。
线
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
中找到大小的簿记数据。
关于从 uint32/16_t 创建一个字符串,然后解析回原始数字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11425817/