ページ 1 / 1
動的確保について Ⅱ
Posted: 2016年8月10日(水) 10:39
by おーばー
コード:
#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関数内で出力できて、メイン関数内で出力出来ないのはなぜなのですか?
Re: 動的確保について Ⅱ
Posted: 2016年8月10日(水) 11:31
by box
おそらく、
おーばー さんが書きました:コード:
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関数でその戻り値を使うようなコードを書く必要があると思います。
Re: 動的確保について Ⅱ
Posted: 2016年8月10日(水) 12:09
by box
簡単に言うと、質問者さんの現状はこうなっているように見えます。
コード:
#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;
}
こうする必要があるのではないかな?と思います。
戻り値を捨てている件はさておき。
Re: 動的確保について Ⅱ
Posted: 2016年8月10日(水) 22:11
by box
ついでだから、戻り値を使うバージョンを。
コード:
#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;
}
Re: 動的確保について Ⅱ
Posted: 2016年8月10日(水) 23:17
by みけCAT
box さんが書きました:簡単に言うと、質問者さんの現状はこうなっているように見えます。
呼び出し先関数の中での値の変更が呼び出し元に伝わらないという問題以外に、
本来sizeof(char)を使って確保する領域の大きさを計算するべきと思われる場所でsizeof(char*)を使って計算をしているという問題もありますね。
sizeof(char)は1と定義されており、sizeof(char*)はsizeof(char)以上になるはずなので、メモリが足りずに範囲外にアクセスするという不都合にはならないでしょうが、
数倍(典型的には2~8倍程度)の無駄なメモリが確保され、気持ち悪いです。
オフトピック
前はちゃんとsizeof(char *)を使ったコードを現状として提示していたのに、潰してしまいましたか…
しかも「こうする必要があるのではないかな?と思います。」という方のコードではsizeof(char *)を使った計算のままだし。
さらに、
- マクロでない普通の変数や関数に大文字とアンダースコアだけの名前が使われているのが気持ち悪い
- ヘッダで変数を定義し、ソースで変数をextern宣言しているのが気持ち悪い
という問題もあります。
特に後者はこのヘッダを複数のソースコードから利用すると多重定義エラーになってしまうので、
ヘッダでextern宣言をしてソースコードで実体の定義をする方がいいでしょう。
Re: 動的確保について Ⅱ
Posted: 2016年8月10日(水) 23:25
by みけCAT
オフトピック
ちなみに、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になりました。
Re: 動的確保について Ⅱ
Posted: 2016年8月11日(木) 00:29
by おーばー
お二方またまたありがとうございます!
順番が逆になるのですがNo.5のコメントについてで
本来sizeof(char)を使って計算すべき部分は read_line関数の60行目の "str=malloc(sizeof(char*)*(current+SIZE));"であってますか?!
またexternについてのご指摘ありがとうございます!
分割コンパイル?に初めて挑戦してみたので,他の部分に少しでもおかしな部分があればご指摘お願いいたします
Re: 動的確保について Ⅱ
Posted: 2016年8月11日(木) 00:34
by おーばー
fprintf関数についての厳格な規則?についてもお教えくださりありがとうございます
そういったことは,やはりcを何度も触れることによって覚える?習得する?ものなのでしょうか
Re: 動的確保について Ⅱ
Posted: 2016年8月11日(木) 00:38
by おーばー
アドレスのことをすっかり忘れていました,もう自分の中でポインタと文字列の組み合わせ?最強じゃないかとかおもってました(-_-;)
Re: 動的確保について Ⅱ
Posted: 2016年8月11日(木) 00:56
by みけCAT
おーばー さんが書きました:本来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)でいいのか。
Re: 動的確保について Ⅱ
Posted: 2016年8月18日(木) 00:42
by おーばー
矢印の示す先の大きさとそれをとる数の合計を教えてあげればいいのですね!
ありがとうございました。