0

バイナリ ファイルを void * 型としてメモリに読み込む関数があります。ファイル ヘッダーの情報は、必要なメモリの量と実際のデータ型 (数値ごとのバイト数 - たとえば、「long」と解釈する必要がある場合は 8) を示します。

私の問題は、メインが必要なデータ型またはメモリを認識していないことです。したがって、次のように関数を呼び出します。

long myfread(char *infile, void **tempdata,*datasize) 

char *infile="data.bin"; // name of the input file
void *tempdata=NULL; // where the data will be stored, initially 
long n; // total numbers read, returned by the function 
size_t datasize; // modified appropriately by the function 

n = myfread(infile,&tempdata,&datasize);

これまでのところ、メインは「tempdata」のバイトを読み取ることができますが、(たとえば)整数や浮動小数点数としては読み取れません。私の質問は、これを可能にするためにtempdataを再キャストする簡単な方法はありますか?

4

6 に答える 6

1

あなたは配列について話しているのではなく、メモリのブロックについて話していると思います。

void *char *またはint *;に関係なく、ポインター。メモリのアドレス(ほとんどがヒープ上にある仮想の場合があります)を指している場合、違いはそれがどのように解釈されるかだけです。

16 バイトのメモリ ブロックがあるとします。16 バイト、byte[]( int[]32 ビットあたり) 4 バイトなどです。インデックスを適用すると、バイト オフセットの増分はデータ型のサイズに従います。

最も重要なことは、データ型に対するメモリ ブロックの整合性です。つまり、メモリ ブロックのサイズを超える場所にアクセスしないでください。10 バイトのメモリがあり、ポインタがであるとint *aすると、 へのアクセスa[1]は単なるアクセス違反です。

配列全体を *void から *int に再キャストできますか?

のようなものはないと思いますvoid array。ポインター型のキャストについては、C で自由に行うことができます。

于 2013-06-07T23:16:06.427 に答える
1

編集した質問に基づいて、どのようにmyfread見えるかを推測できます。非常に単純化すると、次のようになります。

long myfread(const char *path, void **pmem, size_t *datasize) {
    long magically_found = 42;
    int *mem;
    int i;

    mem = malloc(magically_found * sizeof(int)); /* and we assume it works */
    *datasize = 12345;
    for (i = 0; i < magically_found; i++)
        mem[i] = i;
    *pmem = mem;
    return magically_found;
}

さて、あなたのmainでは、datasize == 12345戻ったときに割り当てられたメモリがints. これを知っていれば、次のように書くだけです。

    int *ip;
    ... /* your code from above, more or less */
    if (datasize != 12345) {
        panic("memory was not filled with ints");
        /* NOTREACHED */
    }
    ip = tempdata;

ip[i]ここから、任意の有効なi(少なくとも 0 かつ 未満) の にアクセスできますn

より難しい質問は、12345 が意味することをどのように知っているのか、12345 でないint場合は一体どうすればよいのかということです。そして、おそらく 12345 はとにかく意味がありません。多分 4 はどちらがたまたま 4 を持っていることを意味します。それで、何?intint or floatsizeofdatasize == 4

全体として、少なくとも、質問が指定されていないように思えます。

于 2013-06-07T23:24:10.943 に答える
1

わかりましたので、myfread次のようになります。

long myfread(char *infile, void **data, size_t *datasize)
{
   FILE *f = fopen(infile, "rb");   // Or some such.  
   ... 

   *datasize = ... // some calculation of some sort, e.g. seek to end of file?

   *data = malloc(*datasize ... );   // Maybe more calculation? 

   res = fread(f, data, datasize); 

   fclose(f);

   return res;
}

その後、更新さ*dataれたものをint *?として変換します。

int *my_int_array; 

n = myfread(infile,&tempdata,&datasize);

my_int_array = tempdata;   // If a C++ compiler, you need a cast to (int *)

for(int i = 0; i < datasize; i++)
{
   printf("%d\n", my_int_array[i]); 
}

もちろん、myfredad私が思うように動かなければ、すべての賭けは無効です。

于 2013-06-07T23:21:16.313 に答える
0
for(i = 0; i < index_max; i++) {
    printf("%d\n", ((int*)tempdata)[i]);
}
于 2013-06-08T00:03:31.557 に答える
0

あなたが何を望んでいるのか理解するのに苦労しています。あなたもそうかもしれません。に似た関数、readまたは読み取ったデータを格納する場所freadの型の引数を取る関数があるようです。void *これは、渡す型の変数を作成するという意味ではありません。void *代わりに、データを格納するオブジェクトのアドレスを渡します。

あなたの場合、int適切なサイズの配列を作成し、その配列のアドレス (またはその最初の要素のアドレス) を読み取りを行う関数に渡すだけです。例(仮定fread):

int my_array[100];
fread(my_array, sizeof my_array, 1, f);

事前にサイズがわからない場合、または呼び出し元の関数が返された後も存続する必要がある場合は、 を使用して配列にスペースを割り当てることができますmalloc

于 2013-06-07T22:30:37.017 に答える
0

はい、ポインターを別の型にキャストできますが、その場合、未定義の動作を回避することは困難です。たとえば、キャストしているバイナリ データが正しく配置されていること、およびデータを書き込んだコードのメモリ表現がそれを読み取っているコードのメモリ表現と同じであることを確認する必要があります。これは単なる学術的な問題ではありません。アーキテクチャ間でエンディアンの違いが見られる可能性が高く、たとえば、ARM マシンでは double を注意深く調整する必要があるからです。

memcpy を使用して、型付き配列であるかのようにメモリにアクセスする関数を作成することで、アラインメントの問題を解決できます。例えば、

int get_int(const char *array, int idx) {
    int result;
    memcpy(&result, array + idx * sizeof(int), sizeof(int));
    return result;
}

これを N 回書き出すのを避けるために、マクロ化できます。

#define MAKE_GET(T) T get_##T (const char *array, int idx) { \
    T result; \
    memcpy(&result, array + idx * sizeof(T), sizeof(T)); \
    return result; \
}

MAKE_GET(int)
MAKE_GET(float)
MAKE_GET(double)

エンディアンの問題、またはより一般的にはメモリ表現がマシン間で異なる可能性があるという問題を解決するには、バイナリ ファイルの形式を明確に定義する必要があります (たとえば、常に int をリトル エンディアンで記述するなど)。良いアプローチの 1 つは、テキストを使用することです (小さくする必要がある場合は、zlib などで圧縮します)。もう 1 つは、シリアル化ライブラリ (たとえば、Google のプロトコル バッファ) を使用することです。または、自分でロールすることもできます-それほど難しくありません。

于 2013-06-08T08:35:35.437 に答える