いやぁ~、C言語入門の本を見ながらC++でコーディングしてるんですがこれ勉強になりますねw
そんなことやらんでええ、という意見はもっともなんですが、
環境上Visual Studio を使いたのでCそのものではなく上位互換のC++で練習しているわけなんですが、scanfがscanf_sになったり、警告無視するマクロ組まないとまともにコンパイルできないものがおおいですね。
マクロは一行で済むのですが、せっかくなので新しい関数等の使い方も覚えようとC++でコーディングしてるわけなんですが、変数がポインタになったりポインタの指す値になったり、、と、いろいろ意味が変わるものが多いので、変数宣言の際によく考えなければならず、超勉強になってます。
若干ずれた勉強の仕方ですが、関数や引数の型を考える練習にはなるのでむしろいいかも?
今回のプログラム
今回のプログラムはキーボードから打ち込んだ名前、身長、体重を入力してファイルに出力するというプログラムです。
○○_s系の関数をつかってC++でコーディングしました。
#include <iostream>
#define FILE_LENGTH 128
struct person {
char name[128];
double height;
double weight;
};
void input(struct person *p){
printf("名前:"); scanf_s("%s", p->name, 128);
printf("身長:"); scanf_s("%lf", &p->height);
printf("体重:"); scanf_s("%lf", &p->weight);
putchar('\n');
}
void output(FILE *f,struct person p[],int n) {
int i;
for (i = 0; i < n; i++) {
fprintf(f, "%-10s %5.1f %5.1f\n", p[i].name,p[i].height,p[i].weight);
}
}
int main()
{
FILE* fp;
char filename[FILE_LENGTH];
int end_flag;
int p_number=0;
struct person p_data[100];
printf("書き込みを行うファイル名を入力してください。\n");
printf("ファイル名:");
scanf_s("%s", filename, FILE_LENGTH);
//ストリームを指すファイルポインタfpにファイル名+読み込みモード"w"で与える。
fopen_s(&fp,filename,"w");
if (fp == NULL) {
printf("ファイルを開くことができないか、ファイルを作成できません。");
}
else {
printf("名前 身長 体重 を入力してください。\n");
printf("_______________________\n");
do {
input(&p_data[p_number]);
p_number++;
printf("入力を続けますか?(続ける=0/終わる=otherwise):"); scanf_s("%d", &end_flag);
} while (end_flag == 0);
//printf("%d",p_data[1].height); //確認用
output(fp,p_data,p_number);
fclose(fp);
}
}
全体としてはこんな感じになりました。
まだポインタに対して理解が及んでいない部分もあるので解釈が間違ってるかもしれませんが。
関数 input(struct person *p)
typedef宣言を使えばいちいちstructを書かなくてよくなるのでしんぷるなんですが、それ男の方だったっけ。。。となるので最近はいちいち書くことにしてます。
void input(struct person *p){
printf("名前:"); scanf_s("%s", p->name, 128);
printf("身長:"); scanf_s("%lf", &p->height);
printf("体重:"); scanf_s("%lf", &p->weight);
putchar('\n');
}
この関数では引数、というかポインタで指してる呼び出し元の配列の各要素に代入していってます。
scanf_sを使う場合はscanfと違って文字列を入力する際はサイズを指定して引数に追加しなければならないんですが、毎回忘れる、、
コンパイル通っちゃうしなぁ、、、(なお、例外がでる。)
関数を使うときは
input(&p_data[p_number]);
としてアドレスを入れればいいんですね。簡単なポインタの使い方には少々慣れてきた、かな。
一つの値以上の返却はできないんですが、ポインタを使えば関節操作ができる。そのために関数の仮引数の部分で*pとしてポインタ型として呼び出すんですが、関数内では*pは値をもったただの変数のように扱えるんですよね~
この辺が分かってはいても理解するのがめんどかった。
関数 output(FILE *f ,struct person p[], int n)
今度は引数にFILE型の引数(というかポインタ)も来てますねぇ、、、
なんとなく使い方が分かってきた気がします。
void output(FILE *f,struct person p[],int n) {
int i;
for (i = 0; i < n; i++) {
fprintf(f, "%-10s %5.1f %5.1f\n", p[i].name,p[i].height,p[i].weight);
}
}
main関数の部分で
FILE *fp;
と宣言してるので、fpはポインタ型になるようなので、引数としてはポインタ型として宣言してやったわけなんですが、関数内では*を付けると値そのもののようにふるまうのでポインタfpとして扱いたい場合は「*」はつけないで使う、というか使える、という感じなんですかね、、
頭痛が痛い、みたいな意味のような感じがするのでときどき頭がおかしくなりそうになりますが、何とか使い方と意味を理解できて来たような気がしますねぇ、、
コメント
まだまだ慣れてない方なので、もっと積極的に使わねば、と感じますねぇ、、
それでは。