• ベストアンサー

メモリ確保の謎。

C言語のメモリの確保の所でふと疑問に思ったのですが、 malloc,calloc,realloc,memset,memcpyなどの関数を使うときって #include <stdio.h> #include <stdlib.h> #include <memory.h> #include <malloc.h> とか書かないといけないと本にはありますが、#include <stdio.h>だけで なんのエラーにもならずに実行できてしまうのはなぜでしょうか? 実際のプログラムにはmallocとreallocしか使ってないのですが、#include <stdio.h>でできてしまいます。 でも教科書には他にも書かなきゃいけないとかいてありますが、なぜ書かなくても実行できてしまうのでしょうか?

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

  • ベストアンサー
  • shige_70
  • ベストアンサー率17% (168/946)
回答No.1

それはたまたま動いただけです。 別の環境ではコンパイルに失敗する場合もあると思います。 ヘッダファイルには、そういったライブラリ関数の引数や返り値に関する情報(プロトタイプ宣言といいます)が納められていて、それをインクルードすることで、『正しく』コンパイルできるのです。 ヘッダをインクルードしていれば、ミスで引数を間違えた時にもちゃんとコンパイルエラーになってくれるのでわかります。しかし、インクルードしてないと、逆に正しいはずの時にエラーになったり、もしくは正当性を確認せずよろしくコンパイルしてくれたりします。 ですから、必ず書いておくべきです。たまたま書かなくてもコンパイルに成功して正しく動いたとしてもあくまでそれは偶然でしかないのです。 このことは、メモリ系の関数に限らずすべてにおいてそうですので、心に留めておいてください。どの関数がどのヘッダを必要とするかは関数のマニュアルを参照してください。 ちなみに、一般的には、malloc(),realloc()しか使わないのであれば(calloc()も)、通常はstdlib.hだけでこと足りるはずです。memset(),memcpy()もstring.hだけあれば使えます。

usui323
質問者

お礼

回答ありがとうございました。 次からはきちんと書くようにしようと思います。

その他の回答 (2)

  • shige_70
  • ベストアンサー率17% (168/946)
回答No.3

#1です。 #2の方の回答のように認識していても問題はないのですが一部勘違いされている部分があるようなので失礼ながら指摘させて戴きます。 関数のプロトタイブ宣言を怠ると、帰り値はたしかにintがたと見なされますが、引数は通常、プログラム中で使用されている箇所で記述した引数がそのまま渡ります(書かれているとおりにスタックに積まれる)。そして、関数側ではその関数の仕様どおりに引数を取り出します。(スタックから引き出される)。 したがって、実際は、ヘッダのインクルードを忘れても、引数に関しては問題は発生しません。 プロトタイプ宣言があれば、コンパイラが引数の仕様を 知ることができ、もし間違った引数を与えようとすると警告を出します。こうすれば、プログラム作成者がミスに気づくことができます。プロトタイプ宣言を省略すれば、コンパイラは関数にどういう引数を与えるのが正しいかを知るよしもありませんから、プログラム中で使用されている引数が正しいとみなしてコンパイルするしかないのです。 プロトタイプ宣言というのは、(ANSI以降の)新しい仕様で、(ANSIより前のいわゆるK&R時代の)昔のC言語では存在しませんでした。そのころは関数呼び出しで正しい引数を書くことは自己責任であり、コンパイラではミスを検出しなかったのです。まあ、そのころもヘッダファイルというのはあってちゃんとインクルードしてあげないといけないのは同じだったんですけどね。

usui323
質問者

お礼

回答ありがとうございました。 詳しい回答ありがとうございます。

  • V-bravo-U
  • ベストアンサー率51% (155/301)
回答No.2

 ヘッダーファイルの中には関数宣言も含まれています。関数宣言を 怠ると「全てint型の引数を持つint型を返す関数」としてコンパイル されてしまうこともあります。質問者のコンパイラはこの仕様でコンパイル されているものと推測されます。  今回の場合ですとmalloc関数とrealloc関数しか使われていないということ ですが、まさにたまたま正常に動いているケースです。なぜなら、一般的に int,unsigned int,ポインタ,これらのビット幅は同じで幸いにも mallocもreallocも戻り値と引数が全てint型に一致するからです。 memsetとmemcpyも同様の理由です。  #includeを端折ってコンパイルが通っても実行結果が意図したものに ならない場合もあります。例えば数学関数を使いたいのに#include <math.h>を 省略したために返ってくる結果が0.0か1.0のどちらかしか返ってこないという こともあります。このため、デバッグを困難になる原因になります。  ですから、関数(自作/他作/標準問わず)を使うときは使いたい関数の ヘッダーファイルを必ずincludeする必要があります。絶対に忘れないように しましょう。

usui323
質問者

お礼

回答ありがとうございました。 次からは忘れないようにしようと思います。

関連するQ&A

  • C言語 動的なメモリの確保 実行できない

    malloc関数を使いメモリを確保しそこへ"ABCD"と記憶させ、ポインタ*Cを使い確保したメモリの内容を表示するプログラムです。 ********************************************* #include <stdio.h> #include <stdlib.h> int main(void) {   int i;   char *C;   C = (char *) malloc (sizeof(char) * 5);   C = "ABCD";   for(i = 0; i < 5; i++){     if(C[i] != NULL){       printf("%s", C[i]);    ←※エラー※     }   }   free(C);   return 0; } ********************************************* 正常にコンパイルできますが実行エラーになります。VCを使いF10のデバッグテストで※のところエラーになります。なぜなのでしょうか?

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

    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; }

  • メモリ操作関数『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バイトのメモリ確保の数値を変えても、何も変わらないため本当に問題の要求どおりのプログラムが作れているのか謎です。 間違っているなどのアドバイス宜しくお願いします。

  • reallocについて

    以下のソースでmallocやreallocの使い方で間違った部分があれば 指摘してください。 #include <iostream.h> #include <malloc.h> typedef unsigned char byte; main() { byte *p = (byte*)malloc(10); memcpy(p, "123456789", 9); p = (byte*)realloc(p, 5); *(p+4) = 0; printf("%s", (char*)p); p = (byte*)realloc(p, 0); return 0; } realloc(p, 5)のようにreallocでメモリブロックを10から5に 小さくしてもいいんですか? 足りなくなったメモリを増やすためのreallocしか見たことがありません。 reallocでは先頭バイトの位置が保たれていることは保証されますか? 別の言い方をすると、memcpy(p, "123456789", 9);によって 1が入ってるバイトを、reallocした後の*pが差すことが保証されていますか? もし保証されるなら、例えば上記のソースで1234と表示される 別の言い方をすると、reallocでメモリブロックの値は変更されない ことは保証されていますか?

  • C言語 動的なメモリの確保 コンパイル時エラーも警告もないのに実行できない

    配列A[3]を{2, 4, 6}と初期化させ、malloc関数を使いメモリを確保しそこへ先程の配列Aの要素を記憶させ、ポインタ*Nを使い確保したメモリの要素を表示するプログラムです。 ********************************************* #include <stdio.h> #include <stdlib.h> int main(void) {   int A[3] = {2, 4, 6};   int i;   int *N;   N = (int *) malloc (3);   for(i = 0; i < 3; i++){     N[i]= A[i];   }   for(i = 0; i < 3; i++){     printf("%d", N[i]);   }   free(N);   return 0; } ********************************************* VCを使いF10のデバッグテストでは正常に動くのですが、コマンドラインからではエラー報告画面がでます。なぜなのでしょうか?

  • realloc

    reallocの使いかたに関してです。 #include <stdio.h> #include <stdlib.h> main () { int narray[5]; int *pn, *pn2; pn=narray; printf("%p <=> ",pn); printf("%p\n",narray); memset(pn,0,sizeof(int)*5); if((pn2 = (int *)realloc(pn, sizeof(narray)*2))==NULL){ printf("error"); exit(0); } pn=pn2; memset(pn,0,sizeof(int)*5*2); printf("realloc!\n"); return(0); } この記述はどこかまちがっているでしょうか? 最初の配列のサイズを2倍に増やすというだけのぷろぐらむなのですが。 どうもreallocの場所でセグメンテーションフォルトになります。

  • 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()関数で確保し、 読み込む過程で領域が不足すると再割り当てを 行うようにしたいのですが、 エラーが出力されます。 ソース(エラー処理は省略)は次の通りです。 textdata = (char *) malloc (size * sizeof (char)); for (i = 0; !feof (stream); ) { character = fgetc (stream); textdata[i] = character; i++; if ((i % size) == 0) { temp = (char *) malloc (2 * size * sizeof (char)); memcpy (temp, textdata, size * sizeof (char)); free (textdata); textdata = temp; } } 出力されるエラーは次の通りです。 *** glibc detected *** ./lsm: malloc(): memory corruption (fast): 0x09a34198 *** (省略) アボートしました どなたか解決方法を御指導願います。

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

    こんにちはです。 メモリの動的確保なのですが、 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はダメなのでしょうか? アドバイスいただけたらありがたいです。宜しくお願いいたします。

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

    お世話になります。 メモリ領域の確保の仕方とポインタの動向についての質問です。 呼び出し側の関数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つの違いがどのくらいあるのかお教えいただけたら 助かります。 よろしくお願いします。

専門家に質問してみよう