質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

2回答

844閲覧

mallocについて

seiyouakadanuki

総合スコア25

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2020/01/11 17:14

c

1#include<stdio.h> 2#include<stdlib.h> 3#include<errno.h> 4#include<string.h> 5#include<ctype.h> 6#include<unistd.h> 7#include <dirent.h> 8#include<sys/types.h> 9#include<sys/stat.h> 10#include<fcntl.h> 11#include<setjmp.h> 12#include<signal.h> 13 14char* gettoken(char** ptr) 15{ 16 if(strlen(*ptr) == 0)return NULL; 17 char *tmp = *ptr; 18 int idx = 0; 19 int first,end; 20 while(tmp[idx]==' '&&idx<strlen(tmp))idx++; 21 first = idx; 22 while((tmp[idx])!=' '&&idx<strlen(tmp))idx++; 23 end = idx; 24 char* token = malloc(sizeof(char)*(end-first)); 25 for(int i = 0;i<end-first;i++)token[i] = tmp[i+first]; 26 *ptr=&tmp[idx]; 27 if(strlen(token)==0)return NULL; 28 return token; 29} 30 31int main(int argc, char* argv[]) 32{ 33 char *s = "I have a dream", *token; 34 35 while ((token = gettoken(&s)) != NULL) 36 { 37 printf("%s\n", token); 38 free(token); 39 } 40 return 0; 41}

このプログラムは与えられた文字列をスペースごとに区切って表示するためのもので、このmain関数では "I" "have" "a" "dream"となるのが望ましいです。
しかし"a"を取り出すときになぜかaaveと表示されます(前回のhaveが由来のようです)。
mallocで"a"を確保する際にはchar型1つ分しか借りていないはずなのですが、なぜかhaveを格納するだけのサイズを確保しているようです。何が良くなかったのか教えていただけるとありがたいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

printf関数の書式指定で「%s」を使用して文字列出力をさせるなら対象文字列の末尾にはヌル文字「\0」を入れるべきだが、提示されてたプログラムでは考慮されていないことが問題だと思います。

具体的に言うと、
"a"という文字列に対する処理の場合は「printf( "%s\n", token )」に与えるtokenとしては"a\0"のように末尾に'\0'文字を追加する必要があります。この際malloc関数で2バイト確保するような処理になってなければいけません。(提示プログラムでは1バイトしか確保していません。)

上記を考慮したコードは次のようになります。変更行にはコメントをつけています。
変更したのは2行で、後1行は自分の環境ではゴミ文字列が付いてしまう現象が再現しなかったためにつけた
もので最終的には消すべき処理です。

#include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<ctype.h> #include<unistd.h> #include <dirent.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<setjmp.h> #include<signal.h> char* gettoken(char** ptr) { if(strlen(*ptr) == 0)return NULL; char *tmp = *ptr; int idx = 0; int first,end; while(tmp[idx]==' '&&idx<strlen(tmp))idx++; first = idx; while((tmp[idx])!=' '&&idx<strlen(tmp))idx++; end = idx; char* token = malloc(sizeof(char)*(end-first+1)); /* 変更: '\0'を考慮して1バイト多く確保する */ for(int i = 0;i<end-first+1;i++)token[i] = 'X'; /* 最終的にこの行は消してください: 確認のためにmallocで確保した領域にゴミを入れています */ for(int i = 0;i<end-first;i++)token[i] = tmp[i+first]; token[ end - first ] = '\0'; /* 追加: 文字列の末尾に'\0'を入れる。 */ *ptr=&tmp[idx]; if(strlen(token)==0)return NULL; return token; } int main(int argc, char* argv[]) { char *s = "I have a dream", *token; while ((token = gettoken(&s)) != NULL) { printf("%s\n", token); free(token); } return 0; }

投稿2020/01/11 18:43

hidezzz

総合スコア1248

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hidezzz

2020/01/11 18:45

以下は問題点の本質ではない部分ですが、なぜ"a"を処理する際に"aave"となってしまうかについて説明します。 正しくない文字列になってしまう現象はmalloc関数の実装によって起こったり起こらなかったりします。 実際手元のLinxMint19.2の環境で試したところmalloc関数で取得されている領域が0クリアされているようで "a"が表示されました。 malloc関数で取得される配列は0クリアされているとは限らないので、"a"の後ろがその他のゴミ文字列になってしまう環境もありうると思います。("aave"となってしまうのもそういう現象の一種です。) malloc関数の代わりにcalloc関数を使用して処理を置き換えれば一応"a"が表示されるようにはなりますが、これは正しい対処方法ではありません。
seiyouakadanuki

2020/01/11 23:07

丁寧に回答していただきありがとうございます。
guest

0

printf("%s\n", token);

%sで参照される文字列は'\0'で終わっていなければなりません。しかしgettoken内では、単語の文字に必要なだけのメモリしか確保されておらず、'\0'が最後に追加されていないので、printfは、確保されたメモリの範囲を超えて'\0'を探しに行っています。

例に即して説明すれば

  1. "I"と"have"の場合は、確保したメモリの範囲を超えた場所に、たまたま'\0'が入っていたので、成功しているように見える。
  2. "a"のとき、"have"用に確保されて返却されたばかりの同じメモリが確保された。確保した分の先には、前の"have"の分が残っているので"aave"となった。

わけです。

gettokenで、'\0'の分もメモリを確保して追加すれば、うまくいくと思います。

投稿2020/01/11 17:51

Bearded-Ockham

総合スコア430

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問