mallocによる動的メモリ確保

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら

トピックに返信する


答えを正確にご入力ください。答えられるかどうかでスパムボットか否かを判定します。

BBCode: ON
[img]: ON
[flash]: OFF
[url]: ON
スマイリー: OFF

トピックのレビュー
   

展開ビュー トピックのレビュー: mallocによる動的メモリ確保

Re: mallocによる動的メモリ確保

#6

by pocket » 7年前

かずまさん

回答いただきありがとうございます。
ソースコードの詳細をしっかりと把握できていなかったようです。
具体的な例で考えるべきところを考えずに、質問してしまいました。
お手数をおかけして大変申し訳ありません。

strcpyの部分で、前の'\0'に対して上書きしているため、適切に動作しているのですね。
ポインタを使う場合は、今回のようにバッファサイズにかなりシビアになる必要があるということを学びました。

私の質問に対して、考えてくださいましたすべての皆様に感謝いたします。
本当にありがとうございました。

Re: mallocによる動的メモリ確保

#5

by かずま » 7年前

fopen したファイルの中身が

コード:

abc
12345
だったとします。
fgets で s に { 'a', 'b', 'c', '\n', '\0' } の5バイトが
読み込まれます。
strlen は '\0' の前までの文字数を返しますから、len は 4 です。
buff の指す領域には、'\0' も含めた 5 バイトを格納しないと
いけないので、(len + 1) バイト確保します。
strcpy で '\0' を含む 5バイトをコピーします。
index は 4 になります。これは buff の指す領域の中の
'\0' 以外の総文字数です。領域のサイズは (len + 1) です。

次の fgets で s に { '1', '2', '3', '4', '5', '\n', '\0' } の
7バイトが読み込まれます。
len は 6 です。
"abc\n" を "abc\n12345\n" にしないといけないので、必要な領域の
サイズは 4 + 6 + 1 です。+1 は最後の '\0' の分です。
strcpy は buff + 4 の位置にコピーしますから、
最初の '\n' の後の '\0' は '1' によって上書きされてなくなります。
buff の指す領域には '\0' は常に最後の 1つしかありません。

なぜ、複数の '\0' が存在すると思ったのですか?
上記のように、具体的な例でコードをトレースしてみましたか?

Re: mallocによる動的メモリ確保

#4

by pocket » 7年前

お忙しいところをアドバイス頂き本当に有難うございます。

【***みけCATさん***】
丁寧にご指摘いただきありがとうございます。
あまり、メモリを意識したプログラミングをしたことがなかったため、
私の理解に大きな間違いがあることがわかりました。
特に、fgets関数の挙動に関しては正確に理解できていませんでした。
第二引数は「最大文字数-1」だったのですね。勉強になりました。

また、realloc関数は第二引数で指定したバッファ分を増やすものだと勘違いしておりました。
第二引数ではバッファサイズ全体を指定しないといけないのですね。

また、10ずつ固定で増やすという方法は私も良くないなとは思っていましたが、
どうすればいいかわからず困っていました。
fgetsで読み込んだ文字数をstrlenで文字数を調べればいいのですね。勉強になりました。


【***かずまさん***】
私も自分でプログラムを作成していましたが、理想的なソースコードを記載していただいて、本当に有難うございます。
私は文字列のコピーをfor文で回していましたが、strcpyを使えば一行で書けるのですね。勉強になります。

何点が疑問点がありまして、ご質問させていただければと思います。
・一点目
まず、15行目で、「realloc(buff, index + len + 1);」とされていますが、なぜ「+1」されているのでしょうか?
例えば、wihileループの最初はindex=0,len=一行のサイズなので、「+1」がなくてもいいような気がします。
実際に「+1」を消して、実行しても適切に動作します。

・二点目
strcpy関数について調べると、「'\0' までコピーします」と言った解説が出ました。
今回strpy関数を使用されていますが、最終的にはbuffの中に複数の'0'が存在するということにはならないのでしょうか?
strcpy関数を使用するたびに、buffに'0'が入ってしまうのではないかということです。
私の理解ではprintf関数は'0'(ヌル文字)で文字列終了の判定をこなっていると思います。
なので 今回buffに複数の'0'が存在するのであれば、表示が途中で止まってしまうのではないかと思いました。

みけCATさんのコメントにもありましたが、最後に'0'を入れる必要があり、
最後のみstrcpy関数を使用するのであれば、わかるのですが、毎回使用してなぜ適切に動作するのかをお教えいただければ幸いです。

少し長くなってしましたが、何卒よろしくお願いいたします。

Re: mallocによる動的メモリ確保

#3

by かずま » 7年前

realloc は、第1引数が NULL だと、malloc と同じ
動作をするので、次のようなコードが書けます。

コード:

#include <stdio.h>   // fopen, fclose, fgets, printf, FILE
#include <stdlib.h>  // realloc, free, exit
#include <string.h>  // strlen, strcpy

void err(int val, const char *msg) { printf("%s\n", msg), exit(val); }
 
int main(void)
{
    char *buff = NULL, s[256];
    int index = 0;
    FILE *fp = fopen("memory_dynamic_control.c", "r");
    if (!fp) err(1, "fopen failed");
    while (fgets(s, sizeof s, fp) != NULL) {
        int len = strlen(s);
        char *tmp = realloc(buff, index + len + 1);
        if (!tmp) free(buff), err(EXIT_FAILURE, "realloc failed");
        buff = tmp;
        strcpy(buff + index, s);
        index += len;
    }
    fclose(fp);
    printf("%s", buff);
    free(buff);
    return 0;
}
参考になりますか?

Re: mallocによる動的メモリ確保

#2

by みけCAT » 7年前

pocket さんが書きました:fgets関数で10バイトずつ読み込んでいます。
このプログラムはfgets関数で10バイトずつ読み込んでいません。
fgets関数にはバッファサイズとして10が渡されているので、高々9バイトのみを読み込んで最後にナル文字を格納するはずです。
さらに、fgets関数は改行文字までで読み込みを終了するので、9バイト読み込まれるとも限りません。
pocket さんが書きました:バッファに格納する際に、realloc関数を使って、バッファサイズを10バイトずつ増やしています。
realloc関数では常に10バイトのみを確保しており、バッファサイズを増やしているということはありません。
そのため、memory_dynamic_control.cが10バイト以上または2行以上の場合、バッファに格納する際に確保された領域の範囲外にアクセスし、未定義動作となります。
pocket さんが書きました:実行結果はプログラムの一行目しか表示されません。
前述のfgetsの仕様より、運悪く範囲外へのアクセスが実行時エラーにならなければ、memory_dynamic_control.cの1行目(の最初9バイトまで)が表示されるのは自然な動作でしょう。
pocket さんが書きました:アドバイスや、別解などをご教授いただければ幸いです。
  • バッファへ格納して次に格納する位置を移動する大きさは10バイト固定ではなく、fgetsで読み込んだ長さに合わせましょう。文字列の長さはstrlen関数で求められます。
  • reallocで十分な領域を確保しましょう。fgetsで読み込んだ後、バッファに格納する後ではなく前にreallocを用いたほうが長さを合わせやすいでしょう。10バイト固定にしておいて領域を増やさないというのはもってのほかです。
  • 文字列データを読み込んだ後は、printfに渡す前に文字列の最後に、そして最後のみにナル文字を格納しましょう。

mallocによる動的メモリ確保

#1

by pocket » 7年前

お世話になっております。

普段はC++を使用しているのですが、今回はC言語限定ということで、
C言語でプログラムを作成しています。

プログラムの概要を記載いたします。
ファイルを読み込んでその内容をバッファに格納したいです。
C++であればstringで処理をするのですが、今回はmallocを使って動的にメモリ確保を行いたいです。

ただし、メモリサイズを事前に決めるのではなく、reallocを使用して処理を行いたいです。
以下にソースコードを記載いたします。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <err.h>
 
int main(void)
{
 
        FILE *fp;  /* ファイルポインタの宣言 */
        char *buff = (char*)malloc(10 * sizeof(char));;
        if (! buff) {
                err(EXIT_FAILURE, "can not malloc");
        }
        //char c;
        if ((fp = fopen("memory_dynamic_control.c", "r")) == NULL) {  /* ファイルのオープン */
                printf("file open error!!\n");
                return 1;
        }
        char s[256];
        int index=0;
        int i=0;
        int max=0;
        while (fgets(s, 10, fp) != NULL) {
                //printf("%s", s);
                
                //index=0;
                i=0;
                max=index+10;
                for(index; index<max; index++){
                        *(buff+index)=s[i++];           //読み込んだ文字列をバッファにコピー
                        //printf("%d",i);
                }
                
                //バッファサイズを更新
                char *tmp;
                tmp = realloc(buff, 10 * sizeof(char));  
                if (! tmp) {
                        free(buff);        // 失敗したら自分で解放する
                        err(EXIT_FAILURE, "can not realloc");
                }
                buff = tmp;
                index+=10;
                
        }
        printf("%s", buff);

	free(buff);	// プログラムが終了するので、解放しないでもOSがしてくれる。
 
        return 0;
}

処理の概要を説明いたします。
まず、このソースコード名が「memory_dynamic_control.c」となっており、
このプログラムは自分自身の内容を表示する物となっています。
最初にファイルを開いて、fgets関数で10バイトずつ読み込んでいます。
そして、10バイトずつバッファに格納していきます。
バッファに格納する際に、realloc関数を使って、バッファサイズを10バイトずつ増やしています。
最後の「printf("%s", buff);」の部分で、ソースコーソの内容をすべて表示したいのですが、
実行結果はプログラムの一行目しか表示されません。

自分でも確認いたしましたが、一行目しか表示されないことの原因がわかりません。
アドバイスや、別解などをご教授いただければ幸いです。

よろしくお願いいたします。

※最初はセグメンテーションフォルトが出るという旨を投稿いたしましたが、
上記のプログラムでは実行はできていました。
ただ、望んだ結果ではなく、プログラムの一行目しか表示されていないという結果でした。

ページトップ