• ベストアンサー

動的メモリの確保の仕方がわからなくて困っています

バージョンリソースの情報を取得しようと以下の関数をつくったのですがバージョンがうまく読み出せません。環境はVC6.0++です。 /*--------------------------------------------------------*/ char *get_version() { #pragma comment (lib, "version.lib") char szFileName[MAX_PATH]; DWORD dwZero = 0, dwVerInfoSize; unsigned char *szBufferBlock; void *szBufferVersion; UINT VersionLen; GetModuleFileName( NULL, szFileName, sizeof( szFileName)); dwVerInfoSize = GetFileVersionInfoSize( szFileName, &dwZero); if (dwVerInfoSize){ szBufferBlock = (unsigned char *)calloc( dwVerInfoSize, sizeof(char)); GetFileVersionInfo( szFileName, dwZero, dwVerInfoSize, szBufferBlock); if(!szBufferBlock){ } VerQueryValue( szBufferBlock, TEXT("\\StringFileInfo\\041104b0\\ProductVersion"), &szBufferVersion, &VersionLen); } free((void *)szBufferBlock); return (char *)szBufferVersion; }/*--------------------------------------------------------*/ unsigned char *szBufferBlock;を unsigned char szBufferBlock[1604];のようにして 静的に確保するとバージョンをちゃんと返すのですが、 上記のようにcallocをやnew演算子を使うとソソソソソソソソソソソソのような文字列しか返さなくなります。 原因がわかりません。教えてください。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.2

主旨はANo.1の方と同じですが、すこし詳細に回答してみます。 1)VerQueryValueの仕様 BOOL VerQueryValue(  const LPVOID pBlock, // address of buffer for version resource  LPTSTR lpSubBlock, // address of value to retrieve  LPVOID *lplpBuffer, // address of buffer for version value pointer  PUINT puLen // address of length buffer ); GetFileVersionInfoで取得した情報領域を指定して、検索するバージョン情報の指定をすると、 第3パラメータに取得対象の領域のポインタを返却、第4ポインタにはその長さを返却します。 この時、第3パラメータのポインタは、第1パラメータの領域内のアドレスを指しています。 2)get_version関数の返り値 MSDNに以下のような記述があります。(左記のVerQueryValueの仕様を参照してください)  The memory pointed to by *lplpBuffer is freed when the associated pBlock memory is freed. (第1パラメータの関連したメモリが開放された時に、第3パラメータが指示するメモリは開放される) return (char *)szBufferVersion; の前に、free((void *)szBufferBlock); がありますから、 1)で説明した第3パラメータであるポインタはこの時点で無効アドレスになります。 従ってどこを指しているかは不明であり、なにが返ってくるかわかりません。 結果として質問文にあったような状況が発生します。 3)その他 ANo.1の方が、動的メモリの開放を外に出すことを推奨していますが、 char* pVer = get_version(); puts(pVer); free(pVer); 現在のget_version関数の仕様は、動的確保メモリで確保した領域にGetFileVersionInfo関数で情報を 取得し、その中からVerQueryValueで対象の情報のアドレスを取得してそれを返却しています。 つまり、動的確保メモリの一部を返却するのであって、先頭アドレスを返す保障がありません。 従って妥当な手段とは思えません。 それと、返却するszBufferVersion[void *]を、char*にキャストしていますが、VerQueryValueから 返ってくるタイミングで、szBufferVersionの終端が'\0'になっている保障はありません。 つまり文字列である保障がありません。ですから、長さを別途返すのだ思います。 この点も、現在のget_version関数の仕様として問題があると思います。 むしろ、calloc/freeの対はget_version関数の中に閉じさせて、この返却値=文字列ポインタの 指す領域を別途動的メモリ確保の上で、VerQueryValue関数の返却値をコピーし'\0'の終端加工して 返却する方が良いのではないでしょうか?当然この領域は通常はfreeしませんが、最終的get_version 関数内でfreeさせます。 最終的に返却域をfreeするかどうかはget_versionのパラメータでコントロールすれば良いかと思い ます。

supernoob
質問者

お礼

よくわかりました。 VerQueryValueの解説は、英語がわからないので、ずっと日本語の解説を見ていました。日本語の解説にはこう書かれています。 lplpBuffer 変数へのポインタを指定します。関数から制御が返ると、この変数に、pBlockパラメータが指すバッファから取得された指定のバージョン情報が格納されます。関連するメモリが解放された時点で、*lplpBufferが指すメモリも解放されます。 バージョン情報が格納されますと書いてあるんで、てっきり文字列が格納されるのかと誤解していました。関連するメモリというのもよくわからなくて、dennou2000さんの説明を見て理解できました。 よく考えればメモリを確保してない変数に文字列が入るというのもおかしな話しで・・・ これですっきりしました。ありがとうございました。

その他の回答 (1)

  • tettsu
  • ベストアンサー率30% (4/13)
回答No.1

最後の部分 > free((void *)szBufferBlock); > return (char *)szBufferVersion; ここに問題があります。 freeでメモリ開放したポインタを戻り値としています。 どうしても動的確保したいのであれば、get_versionの中でfree関数を呼び出さずにおいて、 char* pVer = get_version(); puts(pVer); free(pVer); というように、get_versionの外でfree関数を呼ぶとよいと思います。 それと、 >unsigned char szBufferBlock[1604]; このように書くと、ちゃんとバージョンを返すとありますが、この書き方だと、 スタック上にメモリを一時的に確保してあるだけで、実際にプログラムを実行してうまく言ったとしても、たまたま動いているだけなので、 static unsigned char szBufferBlock[1604]; このようにstatic宣言しておかないと安定した動作はしないと思います。

supernoob
質問者

お礼

理解できました。 VerQueryValue関数の理解が足らなかったみたいです。 とりあえずget_version()関数を呼び出す関数の中でメモリを確保して、それをget_version()に渡してみようと思います。 ありがとうございました。

関連するQ&A

  • 「動的確保した2次元配列のメモリ解放」を関数化したい

    質問タイトルの通りですが、 「動的確保した2次元配列のメモリ解放」をC言語で関数化したいと思っています。しかし、関数の引数には動的確保した配列の先頭アドレスのみ渡す形にしたいです。そのような場合の関数化は可能ですか? どうもうまくいかず、困っています。 以下、具体的に、サンプルソースを記述します。 わかる方、よろしくお願いします。 //====================================================// #include<stdio.h> unsigned char** AllocByteArray2d(int column, int row); void FreeByteArray2d(unsigned char** box); int main(voidls){ unsigned char array**; array = AllocByteArray2d(2, 3); FreeByteArray2d(array); return 0; } unsigned char** AllocByteArray2d(int column, int row){ unsigned char* box; box = (unsigned char**)malloc( sizeof(unsigned char*)*column ) int i; for(i=0; i<column; i++){ box[i] = (unsigned char*)calloc( row, sizeof(unsigned char)); if(box[i] == NULL) exit(EXIT_FAILURE); } return box; } //引数では配列の先頭アドレスだけ渡す形にしたい void FreeByteArray2d(unsigned char** box){ //ここをどう書いたらいいかわからない }

  • 配列の動的確保

    No.847223 reallocについて No.847300 ポインタについて と質問させてもらい、御回答をいただき、理解した(つもりな)のですが、以下のことが実現できなくこまっております。 (以前の質問はこれを実現するために質問しました。) まず配列array[1][20]を用意します(つまり文字列最高20字格納できる要素数1個の配列を用意)。 そして動的にこの配列のサイズを変更して、なにか文字列を入力する毎に、代入するスペースを逐次確保したいわけです。(メモリが溢れない限りスペースを確保しまくる) そこでcallocやreallocの記述の仕方に困っています。 まず、callocについて char array[1][20]; char *pn, *pn2; pn = (char *)calloc(sizeof(array)/sizeof(char),sizeof(char)); このボイドポインタをキャストする部分にchar* と char** のどちらを使えばいいか、です。 そしてreallocについて、 if( (pn2 = (char *)realloc(pn, sizeof(array)*cnt)) == NULL ){ printf("メモリの確保失敗!\n"); exit(0); } pn=pn2; strcpy(pn[cnt],input); 【ただし、cntは毎回1づつ増加する。】 【inputはchar型の配列で、なんらかの文字列がはいっている。】 としているのですが、これもキャストの仕方がわかりませんし、strcpyで、セグメンテーションフォルトになります。構造体を使ったリスト形式も考えたのですが、reallocの使いかたを覚えたいのであえてこの形式で実現しようとしています。 結局どうしたいかというと、realloc部をforループさせて、cntを1ずつ増加させ、 pn[1][20] つぎは pn[2][20] つぎは pn[3][20] とどんどん増やしていきたいわけデス。 すこしわかりにくい説明だとおもいますが、不明点や、言い回しがオカシイ箇所があればご指摘下さい。

  • 沢山の変数を扱う時、うまく出来ません・・。

    変数が沢山ある時、エラーが起こったかどうかは どうやって判断したらいいんでしょうか。 今50個位変数名があるとします。 今は端おって5つにします。 int a,b,d; char c,e a = (int *) calloc(500,sizeof(int)); b = (int *) calloc(200,sizeof(int)); c = (char *)calloc(700,sizeof(char)); d = (int *) calloc(400,sizeof(int)); e = (char *)calloc(100,sizeof(char)); if(a==NULL || b==NULL || c==NULL || d==NULL || e==NULL)   printf("エラー発生\n"); こうやって50個もif文の中にいれたら大変ですよね。 変数名も長いですし。うまく1つでもエラーが起きたら全体がエラーになるように出来ませんかね? 自分としてはこういう風に考えたんですけど int sum=1; a = (int *) calloc(500,sizeof(int)); sum*=a; b = (int *) calloc(200,sizeof(int)); sum*=b; c = (char *)calloc(700,sizeof(char)); sum*=c; d = (int *) calloc(400,sizeof(int)); sum*=d; e = (char *)calloc(100,sizeof(char)); sum*=e; if(sum==0)   printf("エラー発生\n"); コレくらいしかないですかね?

  • 構造体のメモリの確保のされかた

    2つ質問があります。 1. #include <iostream> using namespace std; union Data { unsigned long val1_val2; struct { unsigned short val1; unsigned short val2; } value; }; int main(void) { Data data; data.val1_val2 = (40 /* val1 */ << (sizeof(data.value.val1) * 8)) + 10 /* val2 */; cout << "val1 = " << data.value.val1 << endl; cout << "val2 = " << data.value.val2 << endl; return 0; } 上記ソースコードを実行すると、「val1 = 10 val2 = 40」という結果がでます。val1は上位2バイトを指しているはずだと考えて書いたのですが、何が間違っているのでしょうか? 構造体は上に書いたメンバーを下位に割り当てていくのでしょうか? 2. 32ビットコンピューターでは構造体のサイズは4の倍数バイトになると聞いたのですが、VC++で struct s1{ char c; }; のサイズをsizeof演算子で見てみると、1バイトで struct s2{ unsigned bit: 1; }; のサイズは4バイトでした。 前者は本当に1バイトで扱われているのでしょうか? 以上2つよろしくお願いします。

  • templateの使い方を教えて下さい。

    質問タイトルの通りです。 今、 unsigned char* AllocByteArray1d(unsigned long int n){ unsigned char *box; box = (uchar *)calloc(n, sizeof(uchar)); if(box == NULL){ puts("can't allocate memory..."); exit(1); } return box; } という関数があって、これはunsigned charの配列をとってくれる関数になってます。これをtemplateを使って、intの配列もとれるようにしたいんです。 恥ずかしながら試しに、 template <typename T> T* AllocByteArray1d(unsigned long int n){ T *box; box = (T *)calloc(n, sizeof(T)); if(box == NULL){ puts("can't allocate memory..."); exit(1); } return box; } とやってみましたがダメでした。事前にTのデータ型がわからないからだろうか、と思って template unsigned char* AllocByteArray1d(unsigned long int); を入れて実体化させてみましたが、これでもダメでした。 どこを修正すれば使えるようになるのか、C++に詳しい方に教えて頂けると幸いです。

  • メモリ動的確保について

    こんにちはです。 メモリの動的確保なのですが、 typedef struct DATA{ char name[256]; char pass[256]; int money; }BANK; void insert(BANK *p,int max); int main(){ int i; size_t st; BANK *person; person = (struct DATA *)malloc(sizeof(struct DATA)); //person = (struct DATA *)malloc(5); if(person == NULL){ printf("確保失敗\n"); exit(-1); } //memset(person,'\0',sizeof(struct DATA)); と、言う風に、記載ソースは途中ですがメモリをとりました。 mallocの後ろの部分ですが、sizeof(struct DATA)と5ではどうちがうのでしょう??2通りともコンパイルエラーはないです。 5は動的に最大5までとるって事はわかるのですが、struct DATAの方はいくつとるのです??いくつもで入力次第です? そして、動的したのにたいしてmemsetしたら実行エラー(コンパイルは通りました)おきました。動的にたいしてmemはダメなのでしょうか? アドバイスいただけたらありがたいです。宜しくお願いいたします。

  • 文字列の探索

    ファイル名を指定して文字列の探索を行うというプログラムをC言語で作成したのですが、 コンパイルのときに警告で「問題のあるポインタの変換(関数 main )」と出て、うまい具合に動きません。改良点を教えてください。 #include<stdio.h> #include<string.h> #include<stdlib.h> unsigned char *s1; unsigned char *s2; unsigned char *cp; FILE *fp; char fname[64]; void TestStrStr(void); main(){ s1 = calloc(256, sizeof(unsigned char)); s2 = calloc(256, sizeof(unsigned char)); printf("Input Filename..."); scanf("%s",fname); while(1){ fp = fopen(fname, "r"); if(fp == NULL){ printf("ファイルを開くことができません...\n"); printf("Input Filename..."); scanf("%s",fname); }else break; } s1=fp; // printf("文字列1を入力してください:"); // scanf("%s",s1); printf("文字列2を入力してください:"); scanf("%s",s2); TestStrStr(); return 0; } void TestStrStr(void){ cp = strstr(s1, s2); if(cp == NULL) printf("'%s'に'%s'のいずれの文字も含まれない.\n", s1, s2); else printf("'%s'の中に現れる'%s'という文字列は%d文字目にある.\n", s1, s2, cp - s1 + 1); free(s1); free(s2); }

  • icon format について

    Visual Studio.NET 2003 MFC を使用しています。 View画面で、アイコンを表示しようと思いました。 アイコン読み込みようのクラスを作成しました。 class icon { public: void IconHeadRead(FILE *fp,int *width,int *height) { unsigned char uc; unsigned short us; unsigned int ui; fread(&us,sizeof(unsigned short),1,fp); fread(&us,sizeof(unsigned short),1,fp); fread(&us,sizeof(unsigned short),1,fp); fread(&uc,sizeof(unsigned char),1,fp);*width=uc; fread(&uc,sizeof(unsigned char),1,fp);*height=uc; fread(&uc,sizeof(unsigned char),1,fp); fread(&uc,sizeof(unsigned char),1,fp); fread(&us,sizeof(unsigned short),1,fp); fread(&us,sizeof(unsigned short),1,fp); fread(&ui,sizeof(unsigned int),1,fp); }; void IconDataRead(FILE *fp,unsigned char *red,unsigned char *green,unsigned char *blue) { unsigned char uc; fread(&uc,sizeof(unsigned char),1,fp);*red=uc; fread(&uc,sizeof(unsigned char),1,fp);*green=uc; fread(&uc,sizeof(unsigned char),1,fp);*blue=uc; }; }; このred,green,blueの値を使用して、 SetPixelでドローしようと思いましたが、 変な表示になります。 width,heightは、正しいようです。 ICON フォーマットについて教えてください。 よろしくお願いします。 同じような要領で、bitmapは保存、読み込みはできました。

  • 動的メモリ 解放がうまくいかない

    よろしくお願いします。 一ファイル20万行程度のCSV形式のテキストファイルが、50個ほどあります。 これを一行づつ読み込んで、strtok( ,",")でデータを取得しようと思っています。 ファイルの行数はまちまちなので、新しいファイルを読み込むときに そのファイルの行数を調べて(ここでは count 行あります) callocをつかって、メモリを確保しました。 btxt=(char **)calloc(count,sizeof(char *));/*動的メモリ確保*/ for(i=0;i<count;i++) btxt[i]=(char *)calloc(120,sizeof(char)); /*一行120文字まで*/ if(btxt==NULL){printf("btxt 確保エラー\n"); exit(0);} 上記btxt配列にすべての行数を読み込んで、strtok()処理をした後 for(v=0;v<120;v++){ if(btxt[v]){ free(btxt[v]); btxt[v]=NULL; }  } free(btxt);  btxt=NULL; で解放してから、次のファイルに移ります。 問題は、ループするごとにメモリ容量がどんどん減ってきて、30ループもさせると メモリ不足でエラーが出ることです。 ブレークポイントを使って調べてみたのですが ループ一回目 calloc前 707.7 MB: calloc後 748.6MB 解放(したつもり)後 747.9 MB ループ二回目 calloc前 750.6 MB: calloc後 794.6MB 解放(したつもり)後 793.8 MB ・・・・・・・・・・・・・・・・・・・ ループ四回終了時には868.3MBにもなって、初めより160MBも使ってしまいます。 free()が効いてないと思うのですが、どこがおかしいのか教えてくださいませ。

  • 多元配列について(ANSI C)

    動的多元配列で、 Voidポインタに、多種の型がぶら下がった多元配列を作り、 読み書きをしたいのですがどのようにしたらよろしいでしょうか。 具体的には、 x[0][1]は、intで「2」が入っている x[0][4]は、intで「9」が入っている x[1][2]は、charでしかも文字列の配列で「goo」が入っている x[1][5]は、charでしかも文字列の配列で「教えて」が入っている x[0]は、int型の配列。X[1]は、文字列型の配列。 というようなものです。 一応ソースは作ってみたのですが、int型では問題なくいくのですが、 文字列は、コンパイルはできますが、実行すると予期せぬことが起きます。 #include <stdio.h> #include <stdlib.h> #include <string.h> int main (void) {      void **topPointa;      int * iDataInput;      int * iDataOutput ;      char * chDataInput;      char * chDataInput2;      char * chDataOutput1 ;      // ポインタアドレス用のメモリ確保      topPointa = (void *) calloc (10 , sizeof(void *));      if(topPointa == NULL) {           printf("メモリが確保できません\n");           exit(-1);      }      //int配列のメモリ確保      iDataInput = (int * ) calloc (10 , sizeof(int));      if(iDataInput == NULL) {           printf("メモリが確保できません\n");           exit(-1);      }      iDataInput[0] = 3 ;      iDataInput[1] = 4 ;      topPointa[0] = (void * ) &iDataInput;      //int配列の取り出し      iDataOutput = *(int *) topPointa[0];      printf( "int: %d\n", iDataOutput[0] );      printf( "int: %d\n", iDataOutput[1] );      //char配列 のメモリ確保      chDataInput = (char * ) calloc (10 , sizeof(char *));      if(chDataInput == NULL) {           printf("メモリが確保できません\n");           exit(-1);      }      chDataInput2 = (char * ) calloc (10 , sizeof(char));      if(chDataInput2 == NULL) {           printf("メモリが確保できません\n");           exit(-1);       }      strcpy(chDataInput2 , "hoe");      chDataInput[0] = &chDataInput2;      topPointa[1] = (void * ) &chDataInput;      //char配列の取り出し      chDataOutput1 = *(char *) topPointa[1];      printf( "char: %S\n", chDataOutput1[0] );      free(iDataInput);      free(chDataInput);      free(chDataInput2);      return 0; } 言語は、C言語ANCI Cでお願いします。 以上。よろしくお願いします。

専門家に質問してみよう