2012-07-11 37 views
0

我需要把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,所以任何帮助,将不胜感激。

回答

1
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)); 

包含更多的错误。

  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*)。这通常是八或十二个字节,所以在分配区域之外写入并不需要太多数字,并导致崩溃或内存损坏(这可能会在很久之后导致崩溃)。

必须跟踪分配到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的簿记数据中找到大小。

+0

非常感谢您的反馈。正如我所说,我是C新手。我不知道我必须检查realloc是否失败。 第一个解决方案,字符串表示,就是我所需要的。 我会执行它。 再次感谢您的时间! – Fdiazreal 2012-07-11 20:26:25

+0

不客气。我希望你没有得到我对你的印象,只是在C程序员必须做错误检查,没有很好的例外,告诉你到底哪一行代码出了问题。当然,检查'malloc/realloc'失败很有可能永远不会检测到失败。但是有一次,它会让你高兴。 – 2012-07-11 20:36:23

0
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); 

`

我想,这也许能解决问题!

+0

是的,但我需要添加多个数字。有时超过4位数字。 – Fdiazreal 2012-07-11 20:32:00

1

格式说明符'%d'用于int,因此对于uint32_t是错误的。第一个uint32_t是一个无符号类型,所以你至少应该使用'%u',但它可能也有不同的宽度,比intunsigned。标准中预见了宏:PRIu32printfSCNu32scanf。举个例子:

sprintf(buffer, "%" PRIu32, gid); 
+0

我可以使用该宏进行打印和其他功能吗?谢谢! – Fdiazreal 2012-07-11 20:21:08

+0

当然,还有一大堆类似的宏和其他类型和说明符。 – 2012-07-11 20:44:15

1

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]);,并检索它作为一个整数到bsscanf(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()容器方法,这非常容易。

+0

是的,我正在写一个软件在ext2 FS中做操作,作为大学的练习。这是我们第一次使用C. – Fdiazreal 2012-07-11 20:20:08

相关问题