こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

ポインタ

#include "stdafx.h"
#include <ctype.h>
#include <string.h>

#include <stdlib.h>

typedef struct {
char number[6];
char class_type[20];
char name[8];
char subject[5];
} my;

my data[100];
int main(int argc, char* argv[])
{
FILE *fp;
int field = 0, line = 0;
char buf[1000], *str;
char bufG[1111];
int i;
if((fp=fopen("test.txt","r"))==NULL){
printf("ファイルが開けません");
}

while(fgets(buf,1000,fp) !=NULL){
str=buf;

while(*str != '\0'){
if(*str != ','){
for(i = 0; *str != ',' && *str != '\0' ; i++){
if(*str == '\n'){
}
else{
bufG[i] = *str;
}
str++;
}
bufG[i] = '\0';
//printf("%s", bufG);

switch(field){
case 0:
strcpy(data[line].number, bufG);
break;
case 1:
strcpy(data[line].class_type, bufG);
break;
case 2:
strcpy(data[line].name, bufG);
break;
case 3:
strcpy(data[line].subject, bufG);
break;
}
field++;

}
else{
str++;
}

}
line++;
field = 0;



}
printf("%s", data[2].subject);


fclose(fp);

return 0;

}

このプログラムをベースにしてメモリの無駄を省けるような
プログラムに修正したいのですが、
ポインタほんとできなくて困ってます。

教えていただいてメモリを取る位置とかは大体わかりました。
まず構造体のメモリをとります。しかしこのままでは固定長になってるので
構造体を少しいじくりますよね。

構造体の中身なのですが
typedef struct{
int number;
char *class_type;
char *name;
char *subject;
} my;
my *data;
にして
data = malloc(100);
このような形でとります。

文字列の型ですがchar *class_typeのようにポインタで宣言しないと
bufGを代入して値を入れるときに型が合いませんので
配列にしないのであればポインタ型宣言でいいと思います。

しかしポインタで宣言してstrcpy(・・)の所を
data[line].class_type = bufG
にするとエラーはでませんが*strの値が変わる度に
data[line].class_typeの値が変動するのでdata[line].class_typeが
国語 とかになったりします。

なんかもうさっぱりわからないんですが
どうすればいいのでしょうか?
変換したソースがほしいです。

投稿日時 - 2009-07-02 16:43:12

QNo.5092628

すぐに回答ほしいです

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

いまのままでも、十分問題ないとおもいますが、上司がNGというのであれば、その都度メモリをとる方法は以下のようになります。
------------------------------------
//#include "stdafx.h"//linux(gcc)で確認なのでコメントにした
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

typedef struct{
int number;
char *class_type;
char *name;
char *subject;
} my;

my *data;
int main(int argc, char* argv[])
{
FILE *fp;
int field = 0, line = 0;
char buf[1000], *str;
char bufG[1111];
int i;
data = malloc(sizeof(my)*100);//最大100人分とる。それを越えたケースは今は考慮しない。ここを変更
if((fp=fopen("test.txt","r"))==NULL){
printf("ファイルが開けません");
}

while(fgets(buf,1000,fp) !=NULL){
str=buf;
while(*str != '\0'){
if(*str != ','){
for(i = 0; *str != ',' && *str != '\0' ; i++){
if(*str == '\n'){
}
else{
bufG[i] = *str;
}
str++;
}
bufG[i] = '\0';
//printf("%s",bufG);

switch(field){
case 0:
data[line].number=atoi(bufG);//ここを変更
break;
case 1:
data[line].class_type = malloc(strlen(bufG)+1);//これを追加
strcpy(data[line].class_type, bufG);
break;
case 2:
data[line].name = malloc(strlen(bufG)+1);//これを追加
strcpy(data[line].name, bufG);
break;
case 3:
data[line].subject = malloc(strlen(bufG)+1);//これを追加
strcpy(data[line].subject, bufG);
break;
}
field++;

}
else{
str++;
}
}
line++;
field = 0;
}
//ここはおまけ
for (i =0; i < line;i++){
printf("%d:%s:%s:%s\n", data[i].number,data[i].class_type,data[i].name,data[i].subject);
}

fclose(fp);

return 0;

}
---------------------------
linux(gcc)で動作確認済みです。
不明点があれば補足して下さい。

投稿日時 - 2009-07-03 10:27:29

補足

ありがとうございました。この解答をもとに
色々変えてエラーチェックとかして理解を深めます。

投稿日時 - 2009-07-03 11:06:00

ANo.8

このQ&Aは役に立ちましたか?

4人が「このQ&Aが役に立った」と投票しています

回答(9)

ANo.9

zwi

回答側としても何処で分からないか理解したいので、#2の問題をプログラム化して答えてほしいですね。あの問題は必要最小限のポインタ問題ですのであれが出来ないと質問者さんに完全回答を見せても理解できない可能性が高いです。
皆さんが回答を書かないのは、そういう理由だと思いますが。

投稿日時 - 2009-07-03 10:32:56

ANo.7

zwi

#2ですが、俺のだけ華麗にスルーされてるのが気になるけどポインタ関係の本を持ってて理解できないのが不思議。
持っている本の名前を教えてもらえますか?

投稿日時 - 2009-07-03 00:41:44

補足

http://www.merveilles.co.jp/book/03programming/c/c_13.html
とかです。
というよりその場で学んで覚えたいんですよね。
勉強してこい と一蹴されたらQ&Aの意味もないし・・

投稿日時 - 2009-07-03 08:08:54

ANo.6

何となく、掲示板でのやりとりには限界があるような気がします。
ポインタについて理解したいのであれば、例えば

「詳解C言語ポインタ完全攻略」ISBN978-4-7973-5457-7
柴田望洋著 ソフトバンククリエイティブ \2499

あたりの本を読んで、基礎からしっかり勉強することをおすすめします。

投稿日時 - 2009-07-02 20:42:06

補足

ポインタというよりもポインタとメモリの関係
実体とかそのへんがまだあやふやです。
ポインタの本3冊あるけどそういうのは載ってませんね。

投稿日時 - 2009-07-03 00:01:57

ANo.5

以前の質問からの続きなら、せめて以前の質問へのリンクを貼っておいて欲しいな。
誰もが全てのQ&Aをチェックしている訳ではないのだから、以前のやり取りを探す手間が省けたほうが回答者には助かる。

投稿日時 - 2009-07-02 20:19:10

補足

ですので基本的に問題文を毎回載せています。
初めて見た人でもお答えできるように質問してはいるつもりなんです。私の質問をよく見ていただける人は質問しめてからとよく
言われるので敢えて説明しました。

投稿日時 - 2009-07-03 00:04:05

ANo.4

このような変更では、期待する動作はしない場合があるように思います

テストして変更前と同じような動きをしたとしてもそれはたまたまっ偶然が重なって動いただけです

動的にデータの保存場所を確保したいのであればその根本を学習しましょう

構造体の中身なのですが
typedef struct{
  int number;
  char *class_type;
  char *name;
  char *subject;
} my;
にしたいのであれば class_type/name/subjectのデータを格納する実体をどこかに確保しないといけなくなりますよ
この点の理解がちゃんと出来ているのでしょうか?

class_typeなどの最大長が決まっているなら素直に配列で構造体を定義した方がいいように思います

また
data = malloc(100);
では 構造体myを 100個分確保したことにはなりません
これは単に 100Byte分のメモリーを確保しただけになります

32Bitコンパイラの場合構造体myが 16Byteになるので
6個分と少しの余りしか確保していませんね ・・・

投稿日時 - 2009-07-02 20:17:24

補足

可変長じゃなければメモリの無駄で却下されました
配列で定義するなら
問題文に載せてあるソースで動くんですよ。
そうなるとポインタで宣言するしかないとおもうんです。

ところでメモリとポインタの関係とか詳しく載っている本などあれば
教えてほしいです。サイトでもだいじょぶです。

メモリに撮り方に関してはサンプルを提示してくれた方のを参考に
するので大丈夫です。

投稿日時 - 2009-07-03 00:06:10

ANo.3

No.1です。
「以前の質問から拝見していますが」と書いていますよね?
そして、感じたことも書いていますよね?

>過去の質問といいますが結局今まで質問したほとんどがこの質問と同じなんですよ。
>要は同じ質問をちょっと方向をかえて質問してるにすぎません。

>過去の問題の一部を締め切ってないのもまだこの問題は終わってないので残しといた方がいいと思った所は解決するまで残してるだけです。
について、
どうやら考え方が違いがあるようです。
「同じような質問」だからこそ、閉め切られていない状態では「理解せずに投げ出して次に進んだ」と解釈しますよ。
なぜ、一つの質問で継続して行わないのでしょうか?
行わないのであれば、一度「質問をとじて、再質問する際に以前の質問のURLを提示」すれば流れははっきりします。

>一旦解答が欲しいです ということです。
「解答」はありません。
過去の質疑から「推測される解法」の提示ならできる「かもしれません」。
なぜなら、行わなければいけない「前提条件(仕様)」が不明確だからです。

No.2氏の回答に同意します。
まず提示ソースに、どこで何を行っている(行いたい)かコメントをつけることはできますか?
それで質問者さんの理解度がある程度わかると思いますから。

投稿日時 - 2009-07-02 18:10:47

補足

>なぜ、一つの質問で継続して行わないのでしょうか?

下の方にいくと見る人が減るからですよ。
それとコメントは断片的でわからない箇所やわかりにくい箇所もあります。
それゆえ部分部分理解してそれを合わせて全体を把握するんです。
ある質問では固定長ではいけないことがわかった。
実体がないとコピーできないことがわかった
メモリのとりかたがわかった
それらを合わせて全体的流れが掴めた。
しかし完全じゃなく曖昧でさらにポインタというわからない箇所もでてきて
収拾がつかなくなってきたのが現在です。

仕様は「ファイルから読み込んだデータを構造体に格納する」
というものです。メンバは
number//名前
class_type//クラスタイプ
name//名前
subject//好きな教科

でファイル自体
1,A,山田,数学
2,B,河合,国語
3,A,福田,国語

みたいにCSVファイルですからカンマで区切っています。

それで問題文のソースがそこまでできています。
そして無駄なメモリの使用を避けるという仕様を追加したい
ということです。

投稿日時 - 2009-07-02 18:24:29

ANo.2

zwi

1つ言える事はいったん理解できるレベルまで機能を落とす必要があるんじゃないでしょうか?
私の提案としては、まず不定サイズの文字列を読み込む所から始めてはどうでしょうか?簡単にするために必要行数分(例えば1000行)の文字列ポインタ配列は最初から用意しておきます。
(1)1行をバッファに読み込んでその文字列サイズを調べる。
(2)1行分のメモリをmallocで確保する。
(3)文字列ポインタ配列にmallocしたメモリのポインタを代入。
(4)バッファからmallocしたメモリに内容をコピー。
(5)データがなくなるまで(1)から(4)を繰り返す。
(6)確認のため読み込んだ全データを表示する。
まず、これが出来ないと話しになりません。出来ますでしょうか?
これが出来たら行数も不定にするプログラムを作成します。→リスト構造などの知識が必要。
今質問している内容は更にその先の目標とすべきです。

投稿日時 - 2009-07-02 17:54:44

ANo.1

以前の質問から拝見していますが、過去の質問を理解せずにその場だけの理解で突き進みすぎです。
まず、過去の放置された質問をすべて解決してから次の質問をしてください。
放置されたままの為、なおさら理解していない印象を強く受けます。
もっと単純なところからポインタについて再度学習することをお勧めします。

>文字列の型ですがchar *class_typeのようにポインタで宣言しないと
(中略)
>国語 とかになったりします
上記のようになる問題については、過去の質疑ですでに説明されています。

>変換したソースがほしいです。
とりあえず、プログラムをそのまま書くと今までと同じことになりそうなので、(今までの質疑から推測する)やらなければいけないことだけいておきます。

1.何かを入れている「typedef struct {}my」について必要なメモリを確保。
 →この時点では、上記構造体のメンバに対応したメモリ領域は存在していない
2.ファイルからデータを読み込み
3.データを分割(判断?)して、格納に必要なメモリ領域を確保
 →確保したメモリを構造体のメンバに関連付ける
4.データを3.に確保したメモリに代入
5.終了する(もしくは不要になった時点で)場合には、確保したメモリは解放する。

投稿日時 - 2009-07-02 17:01:48

補足

過去の質問といいますが結局今まで質問したほとんどが
この質問と同じなんですよ。要は同じ質問をちょっと方向をかえて
質問してるにすぎません。わかったことは
固定長ではいけないのと

1.何かを入れている「typedef struct {}my」について必要なメモリを確保。
 →この時点では、上記構造体のメンバに対応したメモリ領域は存在していない
2.ファイルからデータを読み込み
3.データを分割(判断?)して、格納に必要なメモリ領域を確保
 →確保したメモリを構造体のメンバに関連付ける
4.データを3.に確保したメモリに代入
5.終了する(もしくは不要になった時点で)場合には、確保したメモリは解放する

こういう一連の流れがわかったんです。今まで同じ問題をずっと質問して
考えましたが1つに結び付けれないのでギブアップなので
一旦解答が欲しいです ということです。
過去の問題の一部を締め切ってないのもまだこの問題は終わってないので
残しといた方がいいと思った所は解決するまで残してるだけです。

投稿日時 - 2009-07-02 17:04:33