从uint32/16_t创建一个字符串,然后解析回原始数字
我需要把char *一些uint32_t和uint16_t数字放进去。然后我需要从缓冲区中取回它们。从uint32/16_t创建一个字符串,然后解析回原始数字
我已经阅读了一些问题,我试着用sprintf把它们放到char *和sscanf中再次得到原始数字。但是,我无法正确地获取它们。
下面是我的代码只有2个数字的例子。但我需要超过2个,这就是为什么我使用realloc。另外,我不知道怎么用sprintf和sscanf妥善uint16_t
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字节的对齐块,则直接位于分配的四个后面的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
的簿记数据中找到大小。
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);
`
我想,这也许能解决问题!
是的,但我需要添加多个数字。有时超过4位数字。 – Fdiazreal 2012-07-11 20:32:00
格式说明符'%d'
用于int
,因此对于uint32_t
是错误的。第一个uint32_t
是一个无符号类型,所以你至少应该使用'%u'
,但它可能也有不同的宽度,比int
或unsigned
。标准中预见了宏:PRIu32
为printf
和SCNu32
为scanf
。举个例子:
sprintf(buffer, "%" PRIu32, gid);
我可以使用该宏进行打印和其他功能吗?谢谢! – Fdiazreal 2012-07-11 20:21:08
当然,还有一大堆类似的宏和其他类型和说明符。 – 2012-07-11 20:44:15
sprintf返回的表示形式是char *。如果你试图存储一个整数数组作为它们的字符串表示,那么你的基本数据类型是char **。如果我们只存储字符串数据本身,这是一个粗糙的字符矩阵,但由于最长的字符串可以产生10个字符,加上一个用于终止空值,因此预先分配这么多字节来保存每个字符串是有意义的。
所以要存储n uint32_t的公司从阵列中的阵列S作为字符串:
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
,因此在调用它之前将指针存储到主缓冲区中,这样就不会丢失对数据的引用。首先重新分配d
缓冲区 - 再次为多个字符串分配足够的空间 - 然后如果成功,请参阅d
已更改。如果是这样,请破坏(free()
)s
缓冲区,malloc()
它再次并重建索引(因为如果d
已更改所有索引已过时,您必须这样做)。如果没有,realloc()
s
并修复新的指数。我建议在结构包装这件事,并具有一组例程来操作就可以的,例如:
typedef struct StringArray
{
char **strArray;
char *data;
size_t nStrings;
} StringArray;
这是一个大量的工作。你有有用C吗?作为C++ STL vector<string>
或list<string>
与istringstream
类和push_back()
容器方法,这非常容易。
是的,我正在写一个软件在ext2 FS中做操作,作为大学的练习。这是我们第一次使用C. – Fdiazreal 2012-07-11 20:20:08
非常感谢您的反馈。正如我所说,我是C新手。我不知道我必须检查realloc是否失败。 第一个解决方案,字符串表示,就是我所需要的。 我会执行它。 再次感谢您的时间! – Fdiazreal 2012-07-11 20:26:25
不客气。我希望你没有得到我对你的印象,只是在C程序员必须做错误检查,没有很好的例外,告诉你到底哪一行代码出了问题。当然,检查'malloc/realloc'失败很有可能永远不会检测到失败。但是有一次,它会让你高兴。 – 2012-07-11 20:36:23