我需要在一个char*中放入一些uint32_t和uint16_t数字。然后我需要把它们从缓冲区拿回来。
我读过一些问题,并尝试使用sprintf将它们放入char*中,然后sscanf再次获得原始数字。然而,我不能正确地得到它们。
这是我的代码的一个例子,只有两个数字。但我需要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位整数,以2的补码表示,没有填充位。
CCD_ 4将被解释为CCD_ 6的CCD_。在上述假设下,gid
被解释为介于-2147483648和2147483647之间的数字。因此,十进制字符串表示可以包含'-'
,包含1到10个数字和0终止符,总共使用2到12个字节。但是您只分配了四个字节,所以每当999 < gid < 2^32-99
(带符号的二的补码解释是> 999
或< -99
)时,sprintf
都会写入超过分配的缓冲区大小。
这是不明确的行为。
它很可能不会立即崩溃,因为分配四个字节通常会有效地为您提供更大的内存块(例如,如果malloc
总是返回16个字节对齐的块,则直接位于分配的四个字节后面的12个字节不能被程序的其他部分使用,而是属于程序的地址空间,对它们的写入可能无法被检测到)。但是,当分配的块的末尾位于页面边界上时,它很容易在以后崩溃。
此外,由于您将后续sprintf
的写入偏移量提前了四个字节,因此如果字符串表示(不包括0-termnator)使用了超过四个字节(而程序尚未因写入未分配的内存而崩溃),则会覆盖前一数字的一部分。
线路
buffer = realloc(buffer, sizeof(uint32_t) + sizeof(buffer));
包含更多错误。
如果CCD_ 16发生故障,则CCD_。使用临时并检查是否成功
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 ' ' */
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
的记账数据中找到大小。
格式说明符'%d'
用于int
,因此用于uint32_t
是错误的。首先,uint32_t
是一个无符号类型,所以您至少应该使用'%u'
,但它的宽度也可能与int
或unsigned
不同。标准中预见到了宏:PRIu32
用于printf
,SCNu32
用于scanf
。例如:
sprintf(buffer, "%" PRIu32, gid);
sprintf返回的表示是char*。如果您试图将一个整数数组存储为它们的字符串表示,那么您的基本数据类型是char**。如果我们只存储字符串数据本身,这是一个不规则的字符矩阵,但由于uint32_t
可以产生的最长字符串是10个字符,加上终止null的一个字符,因此预先分配这么多字节来容纳每个字符串是有意义的。
因此,将数组a中的n个uint32_t存储为字符串:
const size_t kMaxIntLen=11;
uint32_t *a,b;
// fill a somehow
...
size_t n,i;
char **s.*d;
if((d=(char*)malloc(n*kMaxIntLen))==NULL)
// error!
if((s=(char**)malloc(n*sizeof(char*)))==NULL)
// error!
for(i=0;i<n;i++)
{
s[i]=d+i; // this is incremented by sizeof(char*) each iteration
snprintf(s[i],kMaxIntLen,"%u",a[i]); // snprintf to be safe
}
现在第i个数字是s[i],所以打印它只是printf("%s",s[i]);
,将它作为整数检索到b
是sscanf(s[i],"%u",&b);
。
后续的内存管理有点棘手。与其不断使用realloc()
来增加缓冲区,不如预先分配一块内存,并只在耗尽时进行更改。如果realloc()
失败,它将返回NULL
,所以在调用它之前,请存储一个指向主缓冲区的指针,这样就不会丢失对数据的引用。首先重新分配sprintf(buffer, "%d", gid)
0缓冲区-再次为多个字符串分配足够的空间-如果成功,则查看d
是否已更改。如果是,请销毁(free()
)s
缓冲区,再次销毁malloc()
并重建索引(必须这样做,因为如果d
已更改,则所有索引都已过时)。如果不是,则realloc()
s
并修复新索引。我建议将整个事情包装在一个结构中,并有一组例程对其进行操作,例如:
typedef struct StringArray
{
char **strArray;
char *data;
size_t nStrings;
} StringArray;
这是一项艰巨的工作。你有使用C吗?作为带有gid
0类和push_back()
容器方法的C++STL vector<string>
或list<string>
,这要容易得多。
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, "%4d", &valorGID);
buffer += sizeof(uint32_t);
sscanf(buffer, "%d", &valorUID);
printf("ValorGID %d ValorUID %d n", valorGID, valorUID);
`
我认为这可以解决问题!