从 uint32/16_t 创建一个字符串,然后解析回原始数字

标签 c char printf scanf uint32-t

我需要将一些 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));

包含更多错误。

  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*) .这通常是 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/

相关文章:

c - 使用 Pebble iOS Kit 获取 Pebble 应用程序元数据的正确方法

c - 用于在计算机之间发送消息的 Win32 API

c - 使用 C 修改从索引开始的数组内容

java - 如何在BIRT中将字符串拆分为字符数组?

c++ - 如何从字符数组中去除换行符?

c++ - 前置或附加到每个 printf 调用

c - 矩阵作为C中的双指针,如何移动它

c - 从产生奇怪输出的字符串中删除字符

C Linux printf 损坏的结果

c - 0 精度的 printf 0