ポインタを使った関数のプログラムについて

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

ポインタを使った関数のプログラムについて

#1

投稿記事 by ともみん » 13年前

初めまして。プログラミングを始めて、一カ月弱の初心者です。
言語はC言語を使っています。cygwinでコンパイル、実行しています。
今回出された課題が解けなくて、質問させていただきたいです。
課題の内容は、

「時間の長さ」を表す構造体 TIME を定義する.そのメンバーは次の通りですべて int型とする.

 メンバー  内容  値の範囲
 day  日数  0 以上
 hor  時 0 から 23 まで
 min  分 0 から 59 まで

この構造体 TIME 型の変数 t のメンバーの値が,


(t.day, t.hor, t.min) = (10, 30, 125)

であるとします.これは,「時間の長さ」t が(10日+30時間+125分)であることを意味します.ただし,時間と分が定められた範囲(それぞれ 23,59 以下)を超えていますので,両者をこの範囲に納めて,(11日+8時間+5分)と改めます.この結果 t のメンバーは:


(t.day, t.hor, t.min) = (11, 8, 5)

となります.このように,ある TIME型の変数のメンバーの値を定められた範囲に改めることを,その変数を正規化すると呼ぶことにします.


TIME型のポインタを引数とする int型の関数 time_set() を考えます.この関数は,
1.引数(ポインタ)の対象変数(この場合は TIME型変数)のメンバーのどれかに負の値があれば,ただちに0を返す.
2.メンバーがすべて0以上ならば,対象変数を正規化し,1を返す.

という機能を持ちます.また,この関数 time_set() の第1行を:


int time_set ( TIME *t )

と定めます.関数 time_set() を作成し,この関数を引用して,
1.TIME型変数のメンバー day,hor,および min の値をキーボードから読み込み,少なくともどれかひとつの値が負であれば,文字列 "Input again." を出力してふたたびキーボードからの入力を待つ.
2.読み込まれたすべてのメンバーが0または正ならば,それらのメンバーからなる TIME型変数を正規化し,得られた結果をディスプレイ上に出力する.

という動作をするプログラムを作成しなさい.

というものです。

一応私なりに、プログラムを作ってみたのですが、コンパイルすると、

problem7-4.c: In function `main':
problem7-4.c:30: error: parse error before "TIME"
problem7-4.c:32: error: parse error before "TIME"

と出てきて、実行できません。

どこがおかしいのか、ご指摘お願いしたいと思っています。

どうぞよろしくお願いします。

これは私の作ったプログラムです。

コード:

#include <stdio.h>

typedef struct {int day, hor, min;}TIME;

int time_set(TIME *t){
		TIME time;
		time = *t;

	 	printf("day?");
		scanf("%d",t->day);
 
 		printf("hor?");
 		scanf("%d",t->hor);
 
 		printf("min?");
 		scanf("%d",t->min);
 			
 		if(t->day < 0 || 0 > t->hor || t->hor > 23 || 0 > t->min || t->min > 59){return 0;}
		else {if(t->min > 59){t->min = t->min % 60;
				    t->hor = t->hor + t->min / 60;return 1;}
		      else if(t->hor > 23){t->hor = t->hor % 24;
			   	    t->day = t->day + t->hor / 24;return 1;}
		      else{return 1;}
		}
}

int main(void){
	TIME time,*p;
	int i;
	i = time_set(TIME *p);

	while(i == 0){printf("Input again.\n");i=time_set(TIME *p);}
	if(i == 1){printf("%d %d %d\n",p->day,p->hor,p->min);}
}


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

Re: ポインタを使った関数のプログラムについて

#2

投稿記事 by box » 13年前

ともみん さんが書きました:

コード:

	i = time_set(TIME *p);
	while(i == 0){printf("Input again.\n");i=time_set(TIME *p);}
time_set関数の引数の書き方が正しくありません。
pだけにすれば、とりあえずコンパイルは通ると思います。

例えばprintf関数で、こんな風に引数の型までは書かないですよね?

コード:

    printf(char * "Hello, world!\n");
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

non
記事: 1097
登録日時: 15年前

Re: ポインタを使った関数のプログラムについて

#3

投稿記事 by non » 13年前

day,hor,minの値は、関数time_setの中でscanfせずに、引数で渡すのが課題の題意ではないでしょうか?
non

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ポインタを使った関数のプログラムについて

#4

投稿記事 by softya(ソフト屋) » 13年前

mainのpはポインタですが初期化されていなので実体がありません。これに値を入れて返す事はできませんので実行時にエラーになります。
この場合は、実態のあるtimeを&演算子でポインタ化してtime_set()関数に渡すのが正しいです。

それと既に指摘されていますが、time_set(TIME *p);は宣言の書き方であって呼び出し方ではないです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ともみん

Re: ポインタを使った関数のプログラムについて

#5

投稿記事 by ともみん » 13年前

皆さん、ご指摘ありがとうございます。

私には、まだプログラムの基礎について理解できてない部分が多々あります。

time_set関数を呼び出すときに*pとしてみたのですが、

problem7-4.c: In function `main':
problem7-4.c:30: error: incompatible type for argument 1 of `time_set'
problem7-4.c:32: error: incompatible type for argument 1 of `time_set'

と出てきて、またコンパイルできませんでした。

書き直したプログラムを下に貼りますので、おかしな点とどうしたらいいかを教えて頂けるとありがたいです。

よろしくお願いします。

コード:

#include <stdio.h>

typedef struct {int day, hor, min;}TIME;

int time_set(TIME *t){
		TIME time;
		time = *t;

 		if(t->day < 0 || 0 > t->hor || t->hor > 23 || 0 > t->min || t->min > 59){return 0;}
		      else {if(t->min > 59){t->min = t->min % 60;
				    t->hor = t->hor + t->min / 60;return 1;}
		      else if(t->hor > 23){t->hor = t->hor % 24;
			   	    t->day = t->day + t->hor / 24;return 1;}
		      else{return 1;}
		}
}

int main(void){
	TIME time,*p;
	int i;
	printf("day?");
	scanf("%d",p->day);
 
 	printf("hor?");
 	scanf("%d",p->hor);
 
 	printf("min?");
 	scanf("%d",p->min);
 			
	i = time_set(*p);

	while(i == 0){printf("Input again.\n");i=time_set(*p);}
	if(i == 1){printf("%d %d %d\n",p->day,p->hor,p->min);}
}


アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ポインタを使った関数のプログラムについて

#6

投稿記事 by softya(ソフト屋) » 13年前

私のを読まれていないようですが?
softya(ソフト屋) さんが書きました:mainのpはポインタですが初期化されていなので実体がありません。これに値を入れて返す事はできませんので実行時にエラーになります。
この場合は、実態のあるtimeを&演算子でポインタ化してtime_set()関数に渡すのが正しいです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

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

Re: ポインタを使った関数のプログラムについて

#7

投稿記事 by box » 13年前

ともみん さんが書きました: problem7-4.c:30: error: incompatible type for argument 1 of `time_set'
problem7-4.c:32: error: incompatible type for argument 1 of `time_set'
このコンパイルエラーが出ている理由は、下記のとおりです。
ともみん さんが書きました:

コード:

int time_set(TIME *t){
time_set関数にはTIME型へのポインターを渡すようになっています。
ところが、
ともみん さんが書きました:

コード:

int main(void){
	TIME time,*p;
	i = time_set(*p);
	while(i == 0){printf("Input again.\n");i=time_set(*p);}
time_set関数を呼び出す際、実際に渡しているのは*p(つまりTIME型の実体)であり、
矛盾があります。

このコンパイルエラーが取れたとしても、実行時に不具合を生じそうですが、
まあそれは追々直していくということで…。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ポインタを使った関数のプログラムについて

#8

投稿記事 by softya(ソフト屋) » 13年前

更に書いておくと、宣言・定義における*はポインタである事を表します。
TIME *p; ← これです。
これはTIME構造体ポインタを入れる箱である変数pを用意すると意味です。
ポインタ変数pはTIME構造体の実体を指し示しめすアドレスを入れる為のモノなので無初期化だと、何処のメモリを具体的に指していない大変危険なものです。

【言葉を追加】

コード:

●危険なパターン
TIME *p;
+-----------+
+ TIME *p;	+	→	無差別なメモリ空間を指す(不定)
+-----------+

●正しいポインタの初期化の例。 今回の場合は、そもそもpが不要だと私は思いますので、例として書くだけと断っておきます。
TIME timeData	;
TIME *p=&timeData;
+-----------+		+---------------+
+ TIME *p;	+	→	+ TIME timeData	+
+-----------+		+---------------+
それと宣言・定義における*と計算式や実引数(呼び出し側)に出てくる*は意味が違います。
これは、ポインタの指し示す実体側を使うという意味になります。
なので、time_set(*p);と呼び出してしまうとポインタ受け渡し前提の関数側の定義int time_set(TIME *t){と矛盾します。
time_set(*p); ← ポインタではなく実体を実引数とする。
int time_set(TIME *t){ ← 仮引数としてポインタを受け取る。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

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

Re: ポインタを使った関数のプログラムについて

#9

投稿記事 by box » 13年前

インデント(字下げ)を私のやり方で直してみました。
押しつけるつもりは全くありません。
それから、私の目から見て疑問に思える箇所について
コメントを入れてあります。
なお、ロジックそのものには手を入れていません。コンパイルエラーは残ったままです。

コード:

#include <stdio.h>
 
typedef struct {
    int day, hor, min;
} TIME;
 
int time_set(TIME *t)
{
    TIME time;                              // 不要ではないか?
    time = *t;                              // 不要ではないか?
    
    if (t->day < 0 || 0 > t->hor || t->hor > 23 || 0 > t->min || t->min > 59){
        return 0;
    }
    else {
        if (t->min > 59) {
            t->min = t->min % 60;           // 先にあまりを計算してしまうと
            t->hor = t->hor + t->min / 60;  // t->minは0~59なので、
                                            // 割り算の結果は0。正しい動きか?
            return 1;                       // 時間について考慮する前に
                                            // returnするのは正しいか?
        }
        else if (t->hor > 23) {
            t->hor = t->hor % 24;           // 先にあまりを計算してしまうと
            t->day = t->day + t->hor / 24;  // t->horは0~23なので、
                                            // 割り算の結果は0。正しい動きか?
            return 1;
        }
        else{
            return 1;
        }
    }
}
 
int main(void)
{
    TIME time, *p;                          // pは必要か?
                                            // timeに値を入れ、そのポインターを
                                            // 渡すべきでは?
    int i;
    
    printf("day?");
    scanf("%d", p->day);
 
    printf("hor?");
    scanf("%d", p->hor);
 
    printf("min?");
    scanf("%d", p->min);
            
    i = time_set(*p);
    while (i == 0) {
        printf("Input again.\n");           // 再入力を求めようとしているが
                                            // 実際には再入力できずに
                                            // 無限ループしそうに見える
        i = time_set(*p);
    }
    if (i == 1) {
        printf("%d %d %d\n", p->day, p->hor, p->min);
    }
                                            // main関数の戻り値の型はintなので、
                                            // 正しくreturnすべきではないか?
}
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

ISLe
記事: 2650
登録日時: 15年前
連絡を取る:

Re: ポインタを使った関数のプログラムについて

#10

投稿記事 by ISLe » 13年前

変数などのオブジェクトは固有のID(値)を持っています。
そのIDを保持する変数をポインタと呼びます。
ID自体もポインタと呼びます。
IDはアドレスと呼ばれることもあります。

いま世にあるポインタの説明はややこしいです。
なぜかややこしいまま修正されません。
きちんと区別できるようになって区別してください。

ポインタ変数には、「〇〇へのポインタ」というときの〇〇と同型のオブジェクトのIDを代入できます。

コード:

/* typedef ~ TYPE; */
void type_set(TYPE *p) { /* 欲しいのはID */
}
int main(void)
{
	TYPE type; /* 実体を宣言(定義) */
	TYPE *p;   /* ポインタ変数の実体を宣言(定義) */
	/* ポインタ変数も変数。IDを保持するための領域を持つ */

	/* pにはまだIDがセットされていないのでIDをセットする */
	p = &type; /* &演算子でオブジェクトからIDを取得する */
	type_set(p); /* IDをそのまま値渡し */

	type_set(&type); /* こちらは、&演算子でオブジェクトから取得したIDをそのまま値渡しする */

	return 0;
}

ともみん

Re: ポインタを使った関数のプログラムについて

#11

投稿記事 by ともみん » 13年前

boxさん、softyaさん、返信ありがとうございます。

お二方が丁寧に説明して下さったおかげで、mainのpはポインタですが初期化されていなので実体がなく、これに値を入れて返す事はできない、なのでこの場合は、実態のあるtimeを&演算子でポインタ化してtime_set()関数に渡すのが正しいという意味が分かりました。

softyaさんの前の投稿はもちろん読ませて頂いたのですが、私の理解不足のため、どうしたらいいのかわからなかったのです。

不快な思いをさせてしまい、本当に申し訳ありませんでした。

お二方のアドバイスをもとにプログラムを作り直してみたいと思います。

ともみん

Re: ポインタを使った関数のプログラムについて

#12

投稿記事 by ともみん » 13年前

boxさんにご指摘いただいた点を書き直してみました。

コンパイルして、実行もできました。

一応プログラムを貼っておくので、改善点がちゃんと直っているか確認して頂けますか?

コード:

#include <stdio.h>

typedef struct {int day, hor, min;}TIME;

int time_set(TIME *t){
		

 		if(t->day < 0 || 0 > t->hor || 0 > t->min){return 0;}
		      else {if(t->min > 59){
					t->hor = t->hor + t->min / 60;
					t->min = t->min % 60;
		       				if(t->hor > 23){
			   				t->day = t->day + t->hor / 24;
							t->hor = t->hor % 24;}
							return 1;}
		      else{return 1;}
		}
}

int main(void){
	TIME time;

	int i;
	printf("day?");
	scanf("%d",&time.day);
 
 	printf("hor?");
 	scanf("%d",&time.hor);
 
 	printf("min?");
 	scanf("%d",&time.min);
 			
	i = time_set(&time);

	while(i == 0){printf("Input again.\n");printf("day?");
	scanf("%d",&time.day);
 
 	printf("hor?");
 	scanf("%d",&time.hor);
 
 	printf("min?");
 	scanf("%d",&time.min);
	i = time_set(&time);}

	if(i == 1){printf("%d %d %d\n",time.day,time.hor,time.min);}

	return 0;
	
}


アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ポインタを使った関数のプログラムについて

#13

投稿記事 by softya(ソフト屋) » 13年前

気になるのは分に問題がなくて24時以降の時間を入れられた場合の正規化。つまり、テストが不十分です。
見なおしてくださいね。

インデントが良くないので、インデントだけ補正バージョン。

コード:

#include <stdio.h>

typedef struct {
	int day, hor, min;
} TIME;

int time_set( TIME *t ) {


	if( t->day < 0 || 0 > t->hor || 0 > t->min ) {
		return 0;
	} else {
		if( t->min > 59 ) {
			t->hor = t->hor + t->min / 60;
			t->min = t->min % 60;
			if( t->hor > 23 ) {
				t->day = t->day + t->hor / 24;
				t->hor = t->hor % 24;
			}
			return 1;
		} else {
			return 1;
		}
	}
}

int main( void ) {
	TIME time;

	int i;
	printf( "day?" );
	scanf( "%d", &time.day );

	printf( "hor?" );
	scanf( "%d", &time.hor );

	printf( "min?" );
	scanf( "%d", &time.min );

	i = time_set( &time );

	while( i == 0 ) {
		printf( "Input again.\n" );
		printf( "day?" );
		scanf( "%d", &time.day );

		printf( "hor?" );
		scanf( "%d", &time.hor );

		printf( "min?" );
		scanf( "%d", &time.min );
		i = time_set( &time );
	}

	if( i == 1 ) {
		printf( "%d %d %d\n", time.day, time.hor, time.min );
	}

	return 0;

}
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ともみん

Re: ポインタを使った関数のプログラムについて

#14

投稿記事 by ともみん » 13年前

softyaさん、早速のご指摘ありがとうございます。

またインデントの補正もありがとうございます。

読みにくいプログラムの書き方で申し訳ありません。

ご指摘して頂いた点について直してみました。

どうでしょうか?

コード:

#include <stdio.h>
 
typedef struct {
    int day, hor, min;
} TIME;
 
int time_set( TIME *t ) {
 
 
    if( t->day < 0 || 0 > t->hor || 0 > t->min ) {
        return 0;
    } else {
        if( t->min > 59 ) {
            t->hor = t->hor + t->min / 60;
            t->min = t->min % 60;}
        if( t->hor > 23 ) {
                t->day = t->day + t->hor / 24;
                t->hor = t->hor % 24;}
            return 1;
    }
}
 
int main( void ) {
    TIME time;
 
    int i;
    printf( "day?" );
    scanf( "%d", &time.day );
 
    printf( "hor?" );
    scanf( "%d", &time.hor );
 
    printf( "min?" );
    scanf( "%d", &time.min );
 
    i = time_set( &time );
 
    while( i == 0 ) {
        printf( "Input again.\n" );
        printf( "day?" );
        scanf( "%d", &time.day );
 
        printf( "hor?" );
        scanf( "%d", &time.hor );
 
        printf( "min?" );
        scanf( "%d", &time.min );
        i = time_set( &time );
    }
 
    if( i == 1 ) {
        printf( "%d %d %d\n", time.day, time.hor, time.min );
    }
 
    return 0;
 
}


アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ポインタを使った関数のプログラムについて

#15

投稿記事 by softya(ソフト屋) » 13年前

動作はOKだと思います。

またまた、見やすいようにインデントと{}の位置関係を調整してます。比べてみてださい。
あと最後のif文は不要でしょう。iRtnしたのはiはループ以外でアチコチ使うのは嫌われるからです。

コード:

#include <stdio.h>

typedef struct {
	int day, hor, min;
} TIME;

int time_set( TIME *t ) {

	if( 0 > t->day || 0 > t->hor || 0 > t->min ) {
		return 0;
	} else {
		if( t->min > 59 ) {
			t->hor = t->hor + t->min / 60;
			t->min = t->min % 60;
		}
		if( t->hor > 23 ) {
			t->day = t->day + t->hor / 24;
			t->hor = t->hor % 24;
		}
		return 1;
	}
}

int main( void ) {
	TIME time;

	int iRtn;
	printf( "day?" );
	scanf( "%d", &time.day );

	printf( "hor?" );
	scanf( "%d", &time.hor );

	printf( "min?" );
	scanf( "%d", &time.min );

	iRtn = time_set( &time );
	while( iRtn == 0 ) {
		printf( "Input again.\n" );
		printf( "day?" );
		scanf( "%d", &time.day );

		printf( "hor?" );
		scanf( "%d", &time.hor );

		printf( "min?" );
		scanf( "%d", &time.min );
		iRtn = time_set( &time );
	}
	printf( "%d %d %d\n", time.day, time.hor, time.min );

	return 0;

}
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ともみん

Re: ポインタを使った関数のプログラムについて

#16

投稿記事 by ともみん » 13年前

softyaさん、ありがとうございました。

他にも、こんな私の読みにくいプログラムを読み、アドバイスを下さった方々、ありがとうございました。

まだまだ初めのところですが、これからも頑張って勉強していきたいと思います。

また質問させて頂くことになるかもしれませんが、その時はどうぞよろしくお願いします。

閉鎖

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