0

私はchar*にいくつかのuint32_tとuint16_tの数字を入れる必要があります。次に、それらをバッファから戻す必要があります。

私はいくつかの質問を読み、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が初めてなので、助けていただければ幸いです。

4

4 に答える 4

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) == 4intさらに、それがビットのパディングなしの2の補数表現の符号付き32ビット整数であると仮定します。

sprintf(buffer, "%d", gid)toバッファgidとして解釈されるビットパターンの10進文字列表現を出力します。int上記の仮定の下では、gidは-2147483648から2147483647までの数値として解釈されます。したがって、10進文字列表現には、が含まれる場合があり'-'、1〜10桁が含まれ、0ターミネータが含まれ、全体で2〜12バイトが使用されます。ただし、割り当てたバイトは4バイトしかないため、999 < gid < 2^32-99(符号付き2の補数の解釈が> 999または< -99)の場合は常にsprintf、割り当てられたバッファーサイズを超えて書き込みます。

それは未定義の振る舞いです。

通常、4バイトを割り当てると、メモリのチャンクが効果的に大きくなるため、すぐにはクラッシュしない可能性があります(たとえばmalloc、常に16バイトの整列ブロックを返す場合、割り当てられた4のすぐ後ろの12バイトは、プログラムの他の部分では使用できませんが、プログラムのアドレス空間に書き込み、それらへの書き込みはおそらく検出されないでしょう)。ただし、割り当てられたチャンクの終わりがページ境界にある場合、後で簡単にクラッシュする可能性があります。

また、後続のsに対して書き込みオフセットを4バイト進めるためsprintf、文字列表現(0ターミネータを除く)が4バイトを超えて使用された場合(書き込みのためにプログラムがまだクラッシュしなかった場合)、前の数値の一部が上書きされます。割り当てられていないメモリへ)。

この線

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

于 2012-07-11T14:32:44.100 に答える
1

フォーマット指定子'%d'はのためのものでintあり、したがって。のために間違っていuint32_tます。最初uint32_tは符号なしタイプなので、少なくともを使用する必要が'%u'ありますが、またはとは幅が異なる場合もありintますunsignedPRIu32標準では、 forprintfSCNu32forのマクロが予測されていますscanf。例として:

sprintf(buffer, "%" PRIu32, gid);
于 2012-07-11T06:05:51.000 に答える
1

sprintfによって返される表現はchar*です。整数の配列を文字列表現として格納しようとしている場合、基本的なデータ型はchar**です。文字列データ自体のみを格納する場合、これは文字の不規則な行列ですが、uint32_t生成できる最長の文字列は10文字であり、終了するnullの場合は1文字であるため、各文字列を保持するためにこの数のバイトを事前に割り当てることは理にかなっています。

したがって、配列aの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);です。

その後のメモリ管理は少し注意が必要です。常にusingを使用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>使用すると非常に簡単になります。istringstreampush_back()

于 2012-07-11T15:39:27.717 に答える
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);

`

これで問題が解決するかもしれません!

于 2012-07-11T05:25:07.533 に答える