• ベストアンサー

文字を格納できる限界を超えてもエラーにならない

こんにちは。今回、以下のコードを作成して、動的にメモリを確保しようとしています。使用しているのはVisual C++ 2008のC言語で、C99等と言った規格に準拠しているかどうかは不明です。 これは、まずメモリをmallocEx(内部でmalloc)で確保して文字を代入、それをさらにrealloc(内部でrealloc)で領域を増やして文字を追加、最後に出力してfree(内部でfree)する、というコードで、コードに直接矢印で示してありますが、わざとエラーの発生するコードを組み込んであるのですが、これが何の問題もなく処理されてしまいます。また、reallocExを実行すると、代入したイが不定のデータに変わってしまうようで、その後ロを代入するとイが消えてしまいます。 唯一正常に動くものは、freeExを実行後のポインタに代入しようとすると例外として処理されるところだけです。 ポインタを参照渡し等にしてもう少しコードをすっきりさせようとしていたのですが、結局メモリ操作がうまくいっていなかったようなので、freeEx関数以外は戻り値で戻すという形に戻しました。 ------------------ 実行するコード ------------------ char *test; test = mallocEx( 1 * sizeof(char) ); ←エラーが発生するか確かめるために要素は1つだけ確保 strcpy( test,"イ" ); ←要素を1つしか確保していないのに2バイトの文字でエラーなし printf("%s\n", test); test = reallocEx( test, (4 + 1) * sizeof(char)); strcpy( test,"ロ" ); ←これを実行すると、前回代入したイが消える printf("%s\n", test); freeEx(&test); //strcpy( test,"イロハ" ); ←これは例外処理で受け付けられない(正常) keywait(); return 0; ------------------ 使用する関数 ------------------ void *mallocEx( size_t nSize ) { void *tmp; /* malloc関数で割り当てたメモリへのポインタ */ tmp = malloc( nSize ); /* メモリを割り当てる */ /* メモリ割り当てに失敗した場合の処理 */ if( tmp == NULL ) { printf( "\a\n<!> mallocEx関数内、malloc関数のエラー:\nメモリが不足しています。指定された数のメモリブロックが確保できませんでした。\n" ); return NULL; /* 生成失敗 */ } return tmp; /* 生成成功 */ } void *reallocEx( void *ptr, size_t nSize ) { void *tmp; /* realloc関数で割り当てたメモリへのポインタ(安全確認用) */ /* ポインタを生成する型を判定する */ tmp = realloc( ptr, nSize ); /* メモリを再度割り当てる(安全のため(=NULLに備えて)内部ポインタ変数tmpで取得) */ /* メモリの再割り当てに失敗した場合の処理 */ if( tmp == NULL ) { printf( "\a\n<!> reallocEx関数内、realloc関数のエラー:\nメモリが不足しています。指定された数のメモリブロックが確保できませんでした。\n" ); return NULL; /* 再割り当て失敗 */ } return tmp; /* 再割り当て成功 */ } void freeEx( void **ptr ) { if( *ptr != NULL ) { free( *ptr ); *ptr = NULL; } }

noname#152491
noname#152491

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

  • ベストアンサー
  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.3

>test = reallocEx( test, (4 + 1) * sizeof(char)); >strcpy( test,"ロ" ); ←これを実行すると、前回代入したイが消える realloc()の戻り値は、malloc()の戻り値と必ずしも同じとは限らなかったはずです(※1)。 つまり、malloc()のときのtestとrealloc()のときのtestとは別の値。 ところで、もし、※1でないとしても、strcpy()というのは前に何が入っていたかとは 無関係に指定した領域を指定した内容で上書きします。 前回代入したイが消えるのは、エラーでも何でもなくて、当たり前のことです。

noname#152491
質問者

お礼

ありがとうございます。 恥ずかしながら、strcpy()の機能をstrcat()と勘違いしていたようです。 realloc()が失敗する可能性があるということは周知していました。もしかしたら不定な値を見たかもしれないので、増やした領域は0で初期化しておくべきですね…。 とりあえず、一回これまでの情報を使って、もう一度テストしたりしてみます。

その他の回答 (3)

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.4

> メモリ上で領域を確保した領域が少なくて、そこにその容量を超えるデータを代入したとしても、 代入した時点では、別の変数などの領域を破壊していても例外は起きないはずです。 例外が起きるとすれば、当該の、破壊した領域に「次回」アクセスしたときである、と私は認識しています。 発見しづらいバグの一因です。

noname#152491
質問者

お礼

わかりました。追加の説明ありがとうございます。

  • koko_u_
  • ベストアンサー率18% (459/2509)
回答No.2

>わざとエラーの発生するコードを組み込んであるのですが、 >これが何の問題もなく処理されてしまいます。 C で動作が未定義になっている場合、エラーが発生することを期待してはいけません。 エラーにしたい場合は、プログラマが明示的にこれを指示する必要があります。

noname#152491
質問者

お礼

ありがとうございます。 メモリ上で領域を確保した領域が少なくて、そこにその容量を超えるデータを代入したとしても、それが例え例外でも、定義やOSのチェック機能などがなければ、あたかも正常動作しているかのように振る舞うこともある、ということですか?

  • Tasuke22
  • ベストアンサー率33% (1799/5383)
回答No.1

何を証明したいのか今1つ分からないのですが。 確保していない領域を破壊しても、その破壊さ れた領域が使われないとエラーはおきません。 そのエラーのおき方も様々であり、チェッカーが 簡単にエラーを検出してくれる、といった単純な ものではありません。 領域をオーバした場所が、ページにまたがるか否か でも話が変わってきます。

noname#152491
質問者

お礼

ありがとうございます。 それはつまり、メモリのどこでどう領域が扱われるかによって、動作がどうなるかはわからない、ということですね。

関連するQ&A

  • ダブルポインタ?

    下記のような関数が存在し、 最終的には mainで宣言した変数 "a" にDataGetでコピーしたデータを mainで再び使用したいのですが、 下記の方法だとmainに何も帰ってきません。 (NULLのまま・・・) 下記の関数を使用しmainに上手くデータを 引き継ぐためにはどうすれば良いのでしょうか? void Mem(int nSize,void **ptr){ char *tmp; tmp = malloc(nSize); *ptr = tmp; } void DataGet(char *aa,char *a){ int nSize = 5; (void)Mem(nSize,(void **)&a); memcpy(a,aa,nSize); return; } void main(){ char aa[20]; char *a = 0x00; memset(aa,0x00,20); memcpy(aa,"test",4); (void)DataGet(aa,a); /* aデータをここから再び使いたい */ }

  • 関数にポインタを渡して動的確保する時について

    どうにも動的確保について間違っている気がするのでお尋ねいたします。 よくメモリを動的に確保する場合に私は次のようなプログラムを書きます ポインタを用意する(例えばint *p) mallocでメモリ確保 ポインタを関数に渡す(Func(p)) 関数側でreallocし、値を代入する(p=(int *)realloc(p,sizeof(int)*num)) 関数呼び出し側で、その値を使う しかし、この方法を使うとどうにも関数を呼び出した際のポインタ(p)と、reallocした後のポインタ(p)の値が違うことがあり、値が不定になることがあります。 (reallocのメモリの確保の仕方のせいでしょうか) この使い方は恐らくどこか間違っていると思うんですが、いまいち納得のいく解決策が思いつきません。 例えばポインタを引数ではなく戻り値として得ればできますが、2つ以上のポインタについてはできません。 何卒ご教授のほどをよろしくお願いいたします。

  • 文字列と整数型について

    はじめまして。 どうしても困っているのでヒントでも良いのでおねがいします。 関数内(func1)で確保した文字列変数のポインタを 別の関数(func2)にポインタ渡しします。 func2内で整数型で計算した結果を引数で示された文字列変数に 代入するというようなことをしたいと思ってます。 ここで、intは4byteとします。 メモリ長だけで見ると、bit[4] = tmp です。 void func1(){ char bit[4]; func2(bit); return; } void func2(char* p){ int tmp = 0x10101100; p = tmp; <---- ??? return; } そこでどのようにすれば、 代入することができるのか分かりません。 以下のような結果になるように代入したいと思っています。 bit[0] = 0x10; bit[1] = 0x10; bit[2] = 0x11; bit[3] = 0x00; 小さな文字列型に整数型をどのように渡せばよいのかが 一番疑問に思っているところです。 整数型に文字列型を代入する場合には 文字列のバイト指定とシフト演算で実現できています。 ヒントでもよいのでお願いします。

  • reallocについて

    現在、領域を拡張しながら、 ファイルを読み込んで呼び元に返却するPGを作成しています。 reallocがうまくいかないので、試しに小さいのを作って みましたが、これだとreallocの2度目で落ちます。 100文字ずつ呼んでいるので、拡張も100文字ずつ行っています。 メモリ確保に失敗なら、まだ分かるのですが、 ちょっと理由がわかりません。 reallocを複数繰り返していることも問題だと思いますが、 まずは正常に処理を流したいと考えています。 よろしくお願いします。 ~~~~~~ソース~~~~~~~~ //ファイルを読み込んでから領域を確保する #include <stdio.h> #include <string.h> #include <stdlib.h> #define BUFF 100 int main() { FILE *fp; char tmp[BUFF+1]; char *str; int len = 0; fp = fopen( "c:/test.txt" , "rb" ); if(fp ==0){ printf("ファイルがありません\n"); return -1; } //領域を初期化 str = (char *)malloc(1); memset(str,'\0',sizeof(str)); while(feof(fp)==0){ memset(tmp,'\0',sizeof(tmp)); fgets(tmp,BUFF,fp); //領域を再確保 len += BUFF+1; if(NULL == ((char *)realloc(str,len))){ printf("メモリ確保エラー"); } //読み込んだ値を変数に追加 strcat(str,tmp); } printf("文字列\n\n%s\n",str); printf("長さ:%d\n",len); fclose(fp); return 0; }

  • メモリ操作関数『malloc(),free()』

    /*10バイトのメモリ領域を確保し、その領域に文字列"Allocate"を代入せよ。*/ /*ただし、確保した領域は、プログラム終了までに開放すること。*/ #include<stdio.h> #include<stdlib.h> void main(void) { char *ptr; ptr = (char *)malloc(10); printf("Allocate\n"); free(ptr); } 今、ライブラリ関数を勉強しています。 この問題をとりあえず作ってみて、実行も成功したのですが、10バイトのメモリ確保の数値を変えても、何も変わらないため本当に問題の要求どおりのプログラムが作れているのか謎です。 間違っているなどのアドバイス宜しくお願いします。

  • ポインタ配列の動的確保

    ポインタの配列の動的確保について教えてください。 入力した数値をポインタ配列に入れるプログラムです。 下記のように書いてみました。(見づらくてごめんなさい) #include<stdio.h> #include<stdlib.h> #define kensu 3 main() { char abc[kensu+1]={'A','B','C','\0'}; char *ptr[kensu]; int i; printf("3つの整数を入力して下さい。\n"); for(i=0;i<kensu;i++){ ptr[i]=(char*)malloc(sizeof(char)*10); if(ptr[i]==NULL){ printf("メモリの取得に失敗しました"); exit(1); } printf("整数%c:",abc[i]); fgets(ptr[i],10,stdin); if(ptr[i][strlen(ptr[i])-1]=='\n') ptr[i][strlen(ptr[i])-1]='\0'; } for(i=0;i<kensu;i++) free(ptr[i]); } ちゃんと動いているようです。 しかし、ポインタ配列の動的確保をネットで調べてみると、ポインタのポインタ(?)を使って、下記のように2度mallocしています。 #include <stdio.h> #include <stdlib.h> #define N 3 int main(void) { char** arr; int i,j; arr = (char**)malloc(N * sizeof(char*)); /* ポインタ配列を確保 */ /* 配列の要素それぞれにつき、メモリ領域を確保 */ for(i=0;i<N;i++) arr[i] = (char*)malloc(N * sizeof(char));   ・・・ ポインタの配列を宣言して、配列の各要素に動的確保するのと ポインタのポインタを宣言し、ポインタ配列を動的確保して、再度配列の要素に動的確保するのとでは、何か違いがあるのでしょうか? ポインタのポインタを宣言し、ポインタ配列を確保する必要性が良く分かっていないのです。 ネット等で調べて見たのですが、理解力がないのかよく分かりませんでした。 どうか教えてください。

  • OpenCVをつかってリストの操作をしているのです

    OpenCVをつかってリストの操作をしているのですが;;;; リストの消去のところでうまくいきません void shoukyo(KATA *fw){ KATA *ptr=fw; for(;ptr!=NULL;ptr=ptr->next){ if(ptr->dereteflag==TRUE){ if(ptr->next==NULL){ delete(ptr); free(ptr); } else{ KATA *tmp=ptr->next; ptr=ptr->next; ptr->next=tmp->next; delete(tmp); free(tmp); } } } } KATAはある構造体の型です。 やりたい事はリストを走追していきptrのdereteflagがTRUEになっているものを消去したいため、 TRUEになっているものがみつかるとelse文の方では動的に確保した領域tmpをTRUEになっているものの 次のポインタとし、TRUEになった要素のポインタを自分nextへのポインタに変更し、nextにはtmpのnextを 入れるようにして最後にtmpを消去して;;といった感じでプログラムを書いたつもりなのですが a.out(3814,0x7fff7102cca0) malloc: *** error for object 0x12247ee00: pointer being freed was not allocated *** set a breakpoint in malloc_error_break to debug Abort trap というエラーメッセージが出て実行はできるのですが勝手に終了してしまいます>< どう改善したらよいでしょうか??

  • 動的なメモリ領域の確保

    double型変数5個分のメモリをmalloc関数により確保し,その確保した要素のアドレスを表示するように,プログラムを作る問題で、 (注)に「 %pで表示するためには,double型へのポインタ(double *)をvoid型へのポインタ(void *)にキャストする必要がある.」と書かれていたのですが、どういうことでしょうか? 以下のようでいいのでしょうか? #include<stdio.h> #include<stdlib.h> #define COUNT 5           // 動的に確保するメモリ領域数を示すマクロ定数の定義 int main(void) {  // 動的に確保するメモリ領域のアドレスを保持するポインタ変数の宣言  double * pointer;  int i;                  // for文で使用する変数の宣言  // int型変数5個分のメモリ領域を確保  pointer = (double *)malloc(sizeof(double) * COUNT);  if(pointer == NULL) {        // メモリ領域の確保が失敗した場合   printf("メモリ領域を確保できませんでした.\n");   exit(1);                // プログラムの終了  }  for(i = 0; i < COUNT; i++)   printf("%d番目のアドレスは%pです.\n", i + 1, pointer + i);  free(pointer);            // 確保したメモリ領域の解放  return 0; }

  • メモリ領域の確保の仕方

    お世話になります。 メモリ領域の確保の仕方とポインタの動向についての質問です。 呼び出し側の関数testで領域Aを確保する。 関数test内で確保した領域に作業領域Bを加え作業をする。 関数testを抜けるときにreallocで呼び出し側から得た サイズに領域Aを戻す。 このときの動作についてなんですが //呼び出し側 void Main(){   DATA *data_p;//データ構造体   long datacount = 5;//データ数(5はテスト用の可変値   data_p = (DATA*)malloc(sizeof(DATA)*datacount);   test(data_p,datacount);   free((void*)(data_p); } //関数test test(DATA data[],long datacount){   DATA *sagyodata_p;   long a = datacount+5;   sagyodata_p = data;   //作業用に領域を広げる   sagyodata_p = (DATA*)malloc(sizeof(DATA)*a);   //領域サイズを戻す   realloc(((void*)sagyodata_p),sizeof(DATA)*datacount); } としたときに 関数testでdata_pを参照したポインタsagyodata_pの中身は問題ないでしょうか? 現在の実行環境 .NET2003 C++では問題なく動いているようですが。 sagyodata_p = data; sagyodata_p = (DATA*)malloc(sizeof(DATA)*a); とするより、 realloc(((void*)data),sizeof(DATA)*a); としたほうがよいのでしょうか? この2つの違いがどのくらいあるのかお教えいただけたら 助かります。 よろしくお願いします。

  • realloc関数でメモリエラー

    いつもお世話になります。 今、Microsoft VS 6.0 でrealloc関数を使用したプログラムを作成した所、 メモリエラーが発生してしまいました。 どうも、調査を行なっていくと、realloc関数を何回か使用していくと、 別のメモリ領域を破壊(?)しているように思えます。 そのような現象って、他の方々も発生した事があるのでしょうか?? それとも何か落とし穴が??? どなたか情報をもっている方、是非是非お願いします。 ちなみに、reallocを使用せず、malloc関数のみで作成したプログラムでは 発生してません。 ---------------- 【reallocを使用していて作成した関数】   ファイルを読み込み、1件読むたびにrealloc関数でエリア拡張    (最初の1件目はmalloc関数にてエリア確保、2件目以降realloc使用) 【原因】   ポインタ変数の内容(アドレス値)が変更されている事が発覚      →おかしくなったエリアを参照しようとしてメモリエラーが発生       ↓   変更された個所を選定していくと、   「realloc」関数を使用後変更されている事が発覚      ※変更されていたポインタ変数は、「realloc」関数使用時には       全く参照もしていない変数で、ロジック上値を変更するような事は       なかった       ↓   「realloc」関数を使用すると常に発生しているわけではない事は確認済     (たまに発生 ←メモリ領域へのアロケートなので取得するエリアによって発生すると推測)       ↓   同様のロジックを予め件数を数えた後、   「malloc」関数を使用すると発生しない事は確認済

専門家に質問してみよう