動的確保について Ⅱ

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

動的確保について Ⅱ

#1

投稿記事 by おーばー » 7年前

コード:

#ifndef INPUT
 #define INPUT
 
 #include<stdio.h>
 #include<stdlib.h>
 #include<string.h>
 
 #define SIZE 256
 int ST_CONTINUE;
 int LIM_STR;
 
 void ADD_CHARACTER(int ch);
 char *read_line(char *str);
 char READ_ALL(char **str);
 
 #endif
 

コード:

 #include<stdio.h>
 #include<stdlib.h>
 #include<string.h>
 
 #define SIZE 256
 
 static int ST_LIM=0;
 static int ST_USED=0;
 static char *ST_BUFFER=NULL;
 extern int ST_CONTINUE=1;
 extern int LIM_STR=0;
 
 void ADD_CHARACTER(int ch)
 {
  if(ST_USED==ST_LIM)
  {
   ST_BUFFER=realloc(ST_BUFFER,(sizeof(char*)*(SIZE+ST_LIM)));
   ST_LIM+=SIZE;
  }
  
  ST_BUFFER[ST_USED++]=ch;
 }
  
 char *read_line(char *str)
 {
  char *temp=NULL;
  int ch=0,i,j;
  for(i=0;ch!=EOF&&ch!='\n';i++)
  {
   ST_USED=0;
   for(j=0;;j++)
   {
    ch=getchar();
    
    if(ch=='\n'||ch==EOF)
    {
     if(j==0||ch==EOF)ST_CONTINUE=0;
     ADD_CHARACTER('\0');
     break;
    }
    ADD_CHARACTER(ch);
    }
   //str=malloc(sizeof(char*)*ST_USED);??
   temp=malloc(sizeof(char*)*SIZE);
   strcpy(temp,ST_BUFFER);
   //printf("#%d#",ST_CONTINUE);
  }
  return temp;
 }

 char **READ_ALL(char **str)
 {
  //char **str=NULL;
  int i,j;
  int ch=0;
  int current=0;
  char *buf=NULL;
  for(LIM_STR=0;ST_CONTINUE==1;LIM_STR++){
   if(current==LIM_STR){
    str=malloc(sizeof(char*)*(current+SIZE));
    current+=SIZE;
   }
   str[LIM_STR]=read_line(str[LIM_STR]);
   //printf("#%s#",str[i]);

  }
   /*  for(i=0;i<LIM_STR;i++)
   printf("%s ",str[i]); */

   
  return str;
 }

コード:

   #include<stdio.h>
 #include<stdlib.h>
 #include<string.h>
 #include"input.h"
 
 
 int main(void)
 {
  char **str=NULL;
  int i;
  READ_ALL(str);
  
  for(i=0;i<LIM_STR;i++)
   printf("%s ",str[i]);
  
  return 0;
 }  
1度別のトピックで質問させていただいて、そこから頂いたヒント、助言等で少しずつ作らせていただいている途中、また行き詰まりました……

ヘッダファイル、関数の実態?、メイン関数、という順番で載せています。

問題に気づいたのが最後のメイン関数の各入力文字の出力で、メイン関数内で、str(READ_ALLで読み込んだ文字列の集まり)を出力させるとエラーが出て、メイン関数内でなく、READ_ALL関数の最後(/**/の中)で出力させてると、問題なく出力に成功します、READ_ALL関数内で出力できて、メイン関数内で出力出来ないのはなぜなのですか?

box
記事: 2002
登録日時: 13年前

Re: 動的確保について Ⅱ

#2

投稿記事 by box » 7年前

おそらく、
おーばー さんが書きました:

コード:

  char **str=NULL;
  int i;
  READ_ALL(str);
READ_ALLの中でstrの値を書き換えたいのであれば、strのアドレスを渡す必要があるのではないか、と思います。
ところで、本題と関係があるかどうかはわかりませんが、
おーばー さんが書きました:

コード:

 char **READ_ALL(char **str)
  return str;
READ_ALL関数は呼び出し元にchar **の値を返そうとしているのに対し、先頭で引用したmain関数において
その戻り値を使っている気配がありません。つまり、今のコードではREAD_ALL関数の戻り値を捨ててしまっています。
戻り値を使わないんだったら関数の型をvoidにすればいいし、
戻り値を使うんだったらmain関数でその戻り値を使うようなコードを書く必要があると思います。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

box
記事: 2002
登録日時: 13年前

Re: 動的確保について Ⅱ

#3

投稿記事 by box » 7年前

簡単に言うと、質問者さんの現状はこうなっているように見えます。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *f(char *s)
{
    s = malloc(sizeof(char) * 10);      // 領域を割り当てて
    strcpy(s, "abcde");                 // 値を突っ込むと
    printf("@@@ s=\"%s\"\n", s);       // 関数の中では値をセットできているが…
    return s;
}

int main(void)
{
    char *s = NULL;

    printf("s=\"%s\"\n", s);            // sの初期値を出力
    f(s);                                // f関数でsの中身を書き換えたつもりが
    printf("s=\"%s\"\n", s);            // 書き換わっていない
    return 0;
}
この状況を打開するには

コード:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *f(char **s)
{
    *s = malloc(sizeof(char *) * 10);
    strcpy(*s, "abcde");
    printf("@@@ s=\"%s\"\n", *s);
    return *s;
}

int main(void)
{
    char *s = NULL;

    printf("s=\"%s\"\n", s);
    f(&s);
    printf("s=\"%s\"\n", s);
    return 0;
}
こうする必要があるのではないかな?と思います。
戻り値を捨てている件はさておき。
最後に編集したユーザー box on 2016年8月10日(水) 22:58 [ 編集 1 回目 ]
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

box
記事: 2002
登録日時: 13年前

Re: 動的確保について Ⅱ

#4

投稿記事 by box » 7年前

ついでだから、戻り値を使うバージョンを。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *f(char *s)
{
    s = malloc(sizeof(char) * 10);
    strcpy(s, "abcde");
    printf("@@@ s=\"%s\"\n", s);
    return s;
}

int main(void)
{
    char *s = NULL;

    printf("s=\"%s\"\n", s);
    s = f(s);
    printf("s=\"%s\"\n", s);
    return 0;
}
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 動的確保について Ⅱ

#5

投稿記事 by みけCAT » 7年前

box さんが書きました:簡単に言うと、質問者さんの現状はこうなっているように見えます。
呼び出し先関数の中での値の変更が呼び出し元に伝わらないという問題以外に、
本来sizeof(char)を使って確保する領域の大きさを計算するべきと思われる場所でsizeof(char*)を使って計算をしているという問題もありますね。
sizeof(char)は1と定義されており、sizeof(char*)はsizeof(char)以上になるはずなので、メモリが足りずに範囲外にアクセスするという不都合にはならないでしょうが、
数倍(典型的には2~8倍程度)の無駄なメモリが確保され、気持ち悪いです。
オフトピック
はちゃんとsizeof(char *)を使ったコードを現状として提示していたのに、潰してしまいましたか…
しかも「こうする必要があるのではないかな?と思います。」という方のコードではsizeof(char *)を使った計算のままだし。
さらに、
  • マクロでない普通の変数や関数に大文字とアンダースコアだけの名前が使われているのが気持ち悪い
  • ヘッダで変数を定義し、ソースで変数をextern宣言しているのが気持ち悪い
という問題もあります。
特に後者はこのヘッダを複数のソースコードから利用すると多重定義エラーになってしまうので、
ヘッダでextern宣言をしてソースコードで実体の定義をする方がいいでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 動的確保について Ⅱ

#6

投稿記事 by みけCAT » 7年前

オフトピック
ちなみに、printf関数の%sにNULLを渡すのはよくないですね。
N1570 7.21.6.1 The fprintf functionの中の変換指定子sの解説を見ると、
If no l length modifier is present, the argument shall be a pointer to the initial
element of an array of character type. 280) Characters from the array are
written up to (but not including) the terminating null character. If the
precision is specified, no more than that many bytes are written. If the
precision is not specified or is greater than the size of the array, the array shall
contain a null character.
となっており、ヌルポインタについての記述がありません。
従って、N1570 7.1.4 Use of library functionsの
Each of the following statements applies unless explicitly stated otherwise in the detailed
descriptions that follow: If an argument to a function has an invalid value (such as a value
outside the domain of the function, or a pointer outside the address space of the program,
or a null pointer, or a pointer to non-modifiable storage when the corresponding
parameter is not const-qualified) or a type (after promotion) not expected by a function
with variable number of arguments, the behavior is undefined.
というルールに従い、ヌルポインタを渡すと未定義動作になるでしょう。

実際、

コード:

#include <stdio.h>
int main(void) {
    char* a = NULL;
    printf("%s\n", a);
	return 0;
}
というコードを実行したところ、Segmentation Faultになりました。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

おーばー

Re: 動的確保について Ⅱ

#7

投稿記事 by おーばー » 7年前

お二方またまたありがとうございます!
順番が逆になるのですがNo.5のコメントについてで
本来sizeof(char)を使って計算すべき部分は read_line関数の60行目の "str=malloc(sizeof(char*)*(current+SIZE));"であってますか?!

またexternについてのご指摘ありがとうございます!
分割コンパイル?に初めて挑戦してみたので,他の部分に少しでもおかしな部分があればご指摘お願いいたします

おーばー

Re: 動的確保について Ⅱ

#8

投稿記事 by おーばー » 7年前

fprintf関数についての厳格な規則?についてもお教えくださりありがとうございます
そういったことは,やはりcを何度も触れることによって覚える?習得する?ものなのでしょうか

おーばー

Re: 動的確保について Ⅱ

#9

投稿記事 by おーばー » 7年前

アドレスのことをすっかり忘れていました,もう自分の中でポインタと文字列の組み合わせ?最強じゃないかとかおもってました(-_-;)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 動的確保について Ⅱ

#10

投稿記事 by みけCAT » 7年前

おーばー さんが書きました:本来sizeof(char)を使って計算すべき部分は read_line関数の60行目の "str=malloc(sizeof(char*)*(current+SIZE));"であってますか?!
いいえ。
提示されたソースコードに該当部分は無いようです。
sizeof(char*)ではなくsizeof(char)を使用するべき部分は
  • 17行目 ST_BUFFER=realloc(ST_BUFFER,(sizeof(char*)*(SIZE+ST_LIM)));
  • 44行目 temp=malloc(sizeof(char*)*SIZE);
です。

一般に、メモリを確保するときは「配列」を確保してその先頭要素へのポインタを代入するので、
代入先が「型hogeへのポインタ」であるとき、確保するべきサイズは「(型hoge1個のサイズ) * (要素数)」です。
例えば、60行目のstr=malloc(sizeof(char*)*(current+SIZE));は、
char*型を要素とする配列を確保し、char**型のstrに代入しているので、適切です。
オフトピック
あーそうか。データが渡されない問題があるREAD_ALLでのサイズ計算は適切だから、現状の例ではsizeof(char)でいいのか。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

おーばー

Re: 動的確保について Ⅱ

#11

投稿記事 by おーばー » 7年前

矢印の示す先の大きさとそれをとる数の合計を教えてあげればいいのですね!
ありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る