←検索窓の楽しみ方
  ショッピングモール  掲示板ランキング


【掲示板ご利用上の注意】

 ※題名は具体的に!
 ※学校の課題の丸投げ禁止!
 ※ソースの添付は「HTML変換ツール」で字下げ!
 ※返信の引用は最小限に!
 ※環境(OSとコンパイラ)や症状は具体的に詳しく!
 ※返信付き投稿の削除は禁止!
 ※マルチポスト(多重投稿)は慎んで!

 詳しくはこちら


 本当はこんなに大きく書きたくはないのですが、なかなか守っていただけなくて…。
 守ってくださいね。お願いします。(by管理人)

C言語ソース⇒HTML形式ツール   掲示板1こちら


管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧

No.4080

関数の利用について
投稿者---神崎(2005/06/25 18:17:46)


皆様初めまして。最近C言語を始めた初心者です。

さて、今、「再帰関数を用いて、マイナスが入力されるまで入力待ちとし、入力された整数値を正順に表示するプログラム」を考えています。但し、変数の定義は一つという制約付きでです。

自分で以下の、「マイナスが入力されるまで入力された整数値を逆順にするプログラム」を作りましたが、どうしても正順に表示させることができず、3日で15時間くらい悩んだのですが、それでも良い解決方法が思いつきませんでした。
どなたか、ご教授お願いできないでしょうか?



――入力された整数値を逆順に表示するプログラム――

#include<stdio.h>
void nyu(void);
main()
{
nyu();
}
void nyu(void)
{
int n;
scanf("%d",&n);
if(n>=0)
nyu();
printf("%d\n",n);
}



この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:関数の利用について 4083 まきじ 2005/06/25 18:57:31
<子記事> Re:関数の利用について 4085 iijima 2005/06/25 22:56:29
<子記事> Re:関数の利用について 4086 かずま 2005/06/26 00:08:59
<子記事> Re:関数の利用について 4093 かずま 2005/06/26 15:20:18


No.4083

Re:関数の利用について
投稿者---まきじ(2005/06/25 18:57:31)


>どうしても正順に表示させることができず

n だけでは無理だと思います。
配列を使うしかないと思いますが?


この投稿にコメントする

削除パスワード

No.4087

Re:関数の利用について
投稿者---神崎(2005/06/26 01:12:44)


やはり配列を使用した方が簡単ですか……。
レスありがとうございました!


この投稿にコメントする

削除パスワード

No.4094

Re:関数の利用について
投稿者---まきじ(2005/06/26 16:06:55)


#include<stdio.h>

void nyu(int[]);

int main(void){

    int n[100] = {0};
    int i;
    
    nyu(n);
    
    for(i = 1; i < n[0]; i++)
        printf("%3d",n[i]);
    
    return 0;
}

void nyu(int n[]){

    scanf("%d",&n[++n[0]]);
    if(n[n[0]]>=0) nyu(n);
    
}



この投稿にコメントする

削除パスワード

No.4096

Re:関数の利用について
投稿者---神崎(2005/06/26 22:21:40)


配列を利用したプログラムですね。
やはりこちらの方が実用的でしょうか。

配列の方は自分でも一度作ってみようと思うので、まだ詳しく検証しませんが、答え合わせとして利用させて頂きます。

ソースも組んで頂き、ありがとうございました。



この投稿にコメントする

削除パスワード

No.4097

Re:関数の利用について
投稿者---かずま(2005/06/26 22:33:05)


配列を使う方法で、変数を一つにしてみました。
LSI C-86 でも動くはずです。
#include <stdio.h>
#include <stdlib.h>

int *p;

void nyu(void)
{
    if (scanf("%d", p + ++*p) == 1 && p[*p] >= 0)
        nyu();
    else
        p[*p] = -1;
}

int main(void)
{
    p = malloc(1000 * sizeof *p);
    *p = 0;
    nyu();
    for (*p = 1; p[*p] >= 0; ++*p)
        printf("%d\n", p[*p]);
    return 0;
}



この投稿にコメントする

削除パスワード

No.4199

Re:関数の利用について
投稿者---pom(2005/07/05 14:02:58)


動的確保について勉強中のものです。

>p = malloc(1000 * sizeof *p);

ポインタ変数を確保した場合は、領域の解放free()はしなくてもよいのですか?

p = malloc(1000 * sizeof *p);

は、int型へポインタを1000個確保する?

結果的に

int *p[1000];

と同じなんでしょうか?

また、以下のものと異なるのでしょうか?

p = (int*)malloc(1000 * sizeof(int));
これは、結果的に
int p[1000];
と考えてもよいですか?


mallocの戻り値は *void ですが、

キャストする必要があるのは、場合によるのでしょうか?





この投稿にコメントする

削除パスワード

No.4201

Re:関数の利用について
投稿者---YuO(2005/07/05 16:15:55)


以下,pはint *型という前提です。


>p = malloc(1000 * sizeof *p);
>は、int型へポインタを1000個確保する?

pの型はint *型ですから,*pの型はint型。

つまり,int型の領域を1000個確保することになります。


>結果的に
>int *p[1000];
>と同じなんでしょうか?

違います。

int p[1000];
の方が近いでしょう。


>また、以下のものと異なるのでしょうか?
>p = (int*)malloc(1000 * sizeof(int));

全く同じです。


>mallocの戻り値は *void ですが、
>キャストする必要があるのは、場合によるのでしょうか?

Cにおいてはキャストする必要はまったくありません。
C++においては必須です……が,mallocを直接使うこと自体が珍しいでしょう。



この投稿にコメントする

削除パスワード

No.4202

Re:関数の利用について
投稿者---まきじ(2005/07/05 17:14:38)


>ポインタ変数を確保した場合は、領域の解放free()はしなくてもよいのですか?

原則として、malloc() などで確保された領域は、
free() で解放しないと駄目だと思います。

#残りの質問は、YuOさんのが正しいと思います。
#間違えてたので、再投稿(^^;


この投稿にコメントする

削除パスワード

No.4203

Re:関数の利用について
投稿者---かずま(2005/07/05 20:19:37)


> 原則として、malloc() などで確保された領域は、
> free() で解放しないと駄目だと思います。
なぜ、駄目だと思うのか、説明できますか?


規格書には次のように書かれています。

: 7.20.3.2  free関数
: 形式
:         #include <stdlib.h>
:         void free(void *ptr);
: 機能  free 関数は、ptr が指す領域を解放し、その後の割付けに使用できる
: ようにする。
: 
: 7.20.3.3  malloc関数
: 形式
:        #include <stdlib.h>
:        void *malloc(size_t size);
: 機能  malloc関数は、大きさが sizeであるオブジェクトの領域を割り付ける。

すなわち、free のあとに malloc を使用することがなければ free する必要
はありません。

malloc や free の実装は処理系依存ですが、ほとんどの処理系では次のように
なっています。

malloc(120) で 120バイトの領域を要求すると、呼ばれた malloc がそれだけ
の領域を持っていない場合、システムから、例えば 4096バイトをまとめて
確保し、その中から 120バイトを切り出し、その先頭アドレスを返します。
次の malloc(160) に対しては、すでにメモリを持っているので、その中から
160バイトを切り出し、その先頭アドレスを返します。最初の 120バイトの領域
を free で返しても、その領域は次の malloc に再利用されるためにライブリ
が持っていて、システムに返却されるわけではありません。

free するかしないかで、その領域がユーザプログラムの管理下にあるのか、
ライブラリの管理下にあるのかの違いがあるだけで、システムから見れば、
同じことです。

そして、ユーザプログラムが終了すれば、malloc のためにシステムが割付けた
メモリは、コード領域や静的データ領域や、スタック領域とともにシステムに
回収されてしまいます。

なぜ、多くの人が、malloc で確保した領域は free しないと駄目だと思うのか
というと、たいていのプログラムは、malloc を何度も行うので、使わなくなっ
た領域は free したほうがよいのに、その free を忘れることが多いので、
malloc した領域は常に free しなければならないと教育されてしまうからで
しょう。



この投稿にコメントする

削除パスワード

No.4204

Re:関数の利用について
投稿者---まきじ(2005/07/05 20:59:44)


>なぜ、駄目だと思うのか、説明できますか?

正直、説明できません。かずまさんの説明を読んでから、
「聞いた(見た)ことあるなぁ〜」って感じです。

以前に、http://www.st.rim.or.jp/~phinloda/cqa/cqa4.html
の「freeしてもメモリが増えない」を見たことがあるので、
なんとなく覚えていたのだと思います(^^;


この投稿にコメントする

削除パスワード

No.4236

Re:関数の利用について
投稿者---pom(2005/07/07 14:55:35)


>>p = malloc(1000 * sizeof *p);
>>は、int型へポインタを1000個確保する?
>
>pの型はint *型ですから,*pの型はint型。
>
>つまり,int型の領域を1000個確保することになります。

私は見当違いをしていました。
変数 p はint型へのポインタ変数ですが、間接演算子 * が付くと
ポインタが指している変数の内容になるので、
sizeof *p だと、確かに *p はint型でした。


>>mallocの戻り値は *void ですが、
>>キャストする必要があるのは、場合によるのでしょうか?
>
>Cにおいてはキャストする必要はまったくありません。
>C++においては必須です……が,mallocを直接使うこと自体が珍しいでしょう。

とある本で調べたのですが、void 型のポインタは、
「任意の型のポインタ、どのような型のポインタにも交換可能な汎用ポインタ」
「void型のポインタはアドレス情報を持つだけなので、いかなる型変換にも耐えられる」
とありました。
ポインタはアドレス情報であって、値の型を心配する必要が無いんですね。
よって、受け取るポインタ変数がどの型であろうとキャストする必要は無いんですね。



free()するかしないかについても、テキストにはないことを教えられて
勉強になりました。

どうもありがとうございました。



この投稿にコメントする

削除パスワード

No.4239

Re:関数の利用について
投稿者---YuO(2005/07/07 15:48:09)


> ポインタはアドレス情報であって、値の型を心配する必要が無いんですね。
>よって、受け取るポインタ変数がどの型であろうとキャストする必要は無いんですね。

任意の型Tがあった時に,void *とT *の間の変換は,コンパイラが勝手に行ってくれます。
そのために,void *とT *の間では自由に変換できます。

内部のデータの持ち方は,何の型のポインタかによって異なる可能性があります。
void *とchar *とint *が,すべて同じオブジェクトを指すポインタであっても,
内部表現が異なる可能性はあります。

ただし,内部表現が異なっていても,コンパイラは内部表現を知っていますから,
void *とchar *やchar *とint *の間ではコンパイラが適切な変換コードを出力します。


あくまでコンパイラが自動的に変換を入れている,ということを知らないと,
qsortの比較関数に
int comp (const char * x, const char * y);

のような関数を間違って使うことになります。
# 正しくはcharでなくvoid。



この投稿にコメントする

削除パスワード

No.4250

Re:関数の利用について
投稿者---あきき(2005/07/09 00:22:26)


>配列を使う方法で、変数を一つにしてみました。
>LSI C-86 でも動くはずです。
><pre>
#include <stdio.h>
#include <stdlib.h>

int *p;

void nyu(void)
{
if (scanf("%d", p + ++*p) == 1 && p[*p] >= 0)
nyu();
else
p[*p] = -1;
}

int main(void)
{
p = malloc(1000 * sizeof *p);
*p = 0;
nyu();
for (*p = 1; p[*p] >= 0; ++*p)
printf("%d\n", p[*p]);
return 0;
}
</pre>
分からないコーディングがあり、投稿しました。

scanf("%d", p + ++*p) == 1

この部分で、p + ++*pの意味がぜんぜん分かりません。これって、scanf関数の仕様書を見たらわかるものですか?


この投稿にコメントする

削除パスワード

No.4251

Re:関数の利用について
投稿者---RiSK(2005/07/09 00:28:14)


# 引用多すぎ

>scanf("%d", p + ++*p) == 1
>
>この部分で、p + ++*pの意味がぜんぜん分かりません。これって、scanf関数の仕様書を見たらわかるものですか?

分かりません。scanfは関係ありません。
  1. *p を +1
  2. p と *p を加えた値を返す(%dで使われる)



この投稿にコメントする

削除パスワード

No.4252

Re:関数の利用について
投稿者---あきき(2005/07/09 00:51:16)


>p と *p を加えた値を返す(%dで使われる)
この部分なんですが、確かに、pはint型のポインタであるため、++*pは理解できます。しかし、pはそもそもポインタ変数ですから、p + ++*pが可能がどうか不可解な点が多いです



この投稿にコメントする

削除パスワード

No.4254

Re:関数の利用について
投稿者---RiSK(2005/07/09 13:21:40)


>pはそもそもポインタ変数ですから、p + ++*pが可能がどうか不可解な点が多いです
ポインタ型 + 整数型
ポインタ型 - 整数型 は可能。
#include <stdio.h>
int main(void) {
    int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int * p = a;
    p++;
    printf("%d\n", *p);
    p += 5;
    printf("%d\n", *p);
    p--;
    printf("%d\n", *p);
    p -= 5;
    printf("%d\n", *p);
    return 0;
}



この投稿にコメントする

削除パスワード

No.4085

Re:関数の利用について
投稿者---iijima(2005/06/25 22:56:29)


>「再帰関数を用いて、マイナスが入力されるまで入力待ちとし、
> 入力された整数値を正順に表示するプログラム」を考えています。
> 但し、変数の定義は一つという制約付きでです。

問題の意図するところかどうかは分かりませんが、構造体による(双方向)連結リストを定義し、そのポインタを再帰関数に渡して入力データを追加していき、最後にリストの先頭から順番にデータを出力するということでできます。

// 例
000: #include <stdlib.h>
001: #include <stdio.h>
002: 
003: typedef struct node_type {
004:     int n;
005:     struct node_type* prev;
006:     struct node_type* next;
007: } Node;
008: 
009: void nyu( Node* p );
010: 
011: int main( void )
012: {
013:     Node* p; // 唯一の変数
014: 
015:     p = (Node*)malloc( sizeof( Node ) );
016:     p->prev = p->next = NULL;
017:     nyu( p );
018:     while( p->next != NULL ){
019:         printf( "%d\n", p->n );
020:         p = p->next;
021:         free( p->prev );
022:     }
023:     free( p );
024:     return 0;
025: }
026: 
027: void nyu( Node* p )
028: {
029:     scanf( "%d", &(p->n) );
030:     if( p->n < 0 ){
031:         return;
032:     }
033:     else{
034:         p->next = (Node*)malloc( sizeof( Node ) );
035:         p->next->prev = p;
036:         p->next->next = NULL;
037:         nyu( p->next );
038:     }
039: }





この投稿にコメントする

削除パスワード

No.4088

Re:関数の利用について
投稿者---神崎(2005/06/26 01:16:16)


>問題の意図するところかどうかは分かりませんが、〜〜
ありがとうございます! まさにこの実行結果を期待していました。
う〜む、ただ、自分には少々難解な部分が多いのですが……(苦笑)。

このソースを参考に、自分でももう一度考えてみたいと思います。本当にありがとうございました!




この投稿にコメントする

削除パスワード

No.4086

Re:関数の利用について
投稿者---かずま(2005/06/26 00:08:59)


【掲示板ご利用上の注意】

 ※ソースの添付は「HTML変換ツール」で字下げ!
#include <stdio.h>
#include <stdlib.h>

void nyu(void)
{
    int n;
    if (scanf("%d", &n) == 1 && n >= 0) {
        printf("%d\n", n);
        nyu();
    }
}

int main(void)
{
    setvbuf(stdout, malloc(65536), _IOFBF, 65536);
    nyu();
    return 0;
}



この投稿にコメントする

削除パスワード

No.4089

Re:関数の利用について
投稿者---神崎(2005/06/26 01:21:49)


> ※ソースの添付は「HTML変換ツール」で字下げ!
あっ……、も、申し訳ございません。初めて利用したものでよく理解しておりませんでした。以後注意します。


かずまさんのソースは、自分にも分かりやすく良かったのですが、入力と同時に入力値が表示されてしまい、マイナス入力まで入力待ちにするという意図が汲めませんでした。
それでも、すごく参考になりました。ありがとうございました!



この投稿にコメントする

削除パスワード

No.4090

Re:関数の利用について
投稿者---あかま(2005/06/26 01:26:22)


>かずまさんのソースは、自分にも分かりやすく良かったのですが、入力と同時に入力値が表示されてしまい、マイナス入力まで入力待ちにするという意図が汲めませんでした。
BCCではきちんと動いてますが、あなたの環境は?




この投稿にコメントする

削除パスワード

No.4091

Re:関数の利用について
投稿者---まきじ(2005/06/26 01:27:28)


>かずまさんのソースは、自分にも分かりやすく良かったのですが、入力と同時に入力値が表示されてしまい、マイナス入力まで入力待ちにするという意図が汲めませんでした。

かずまさんが提示されたソースを、そのままコンパイルし実行すれば、
期待通りの動作しますが。

# nyu() だけコピーしたんでは?


この投稿にコメントする

削除パスワード

No.4092

Re:関数の利用について
投稿者---かずま(2005/06/26 01:27:56)


> かずまさんのソースは、自分にも分かりやすく良かったのですが、
> 入力と同時に入力値が表示されてしまい、マイナス入力まで入力待ちに
> するという意図が汲めませんでした。

コンパイラは何ですか?

LSI C-86 なんかだと、65536 という数値は扱えませんから、これを
8192 などにしてみてください。


この投稿にコメントする

削除パスワード

No.4095

すみませんです!
投稿者---神崎(2005/06/26 22:15:10)


ああ、なるほど!
はい、LSI C-86です。確かに、数値を変更したら期待通りに作動しました。
>※環境(OSとコンパイラ)や症状は具体的に詳しく!
とあるのに、申し訳ありませんでした。

再度レスも下さいまして、感謝しております。
他の皆さまも、ご指摘ありがとうございました。




この投稿にコメントする

削除パスワード

No.4093

Re:関数の利用について
投稿者---かずま(2005/06/26 15:20:18)


#include <stdio.h>

typedef struct Node { int val; struct Node *prev; } Node;

void print(Node *p)
{
    if (p) {
        print(p->prev);
        printf("%d\n", p->val);
    }
}

void nyu(Node *p)
{
    Node node;

    if (scanf("%d", &node.val) == 1 && node.val >= 0) {
        node.prev = p;
        nyu(&node);
    } else
        print(p);    
}

int main(void)
{
    nyu(NULL);
    return 0;
}
print() の p、nyu() の p, node と 3つの変数を使っているからだめですね。


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧




掲示板提供:Real Integrity