トップページ⇒tech
1001コメント269KB

C言語なら俺に聞け! Part 123

■ このスレッドは過去ログ倉庫に格納されています
0001デフォルトの名無しさん2006/02/26(日) 13:58:25
まず問題を冷静に吟味してCの話か否かをはっきりさせてから質問しましょう。
質問する前には最低限検索を。

GUIなどの標準Cではできない事の質問,ソース丸投げ、宿題、書籍 は専門の別スレッドがあるのでそこへさようなら。

エラー(警告含む)が起きたのならばエラーメッセージを書きましょう。

C FAQ 日本語訳
http://www.kouno.jp/home/c_faq/
Cプログラマ必読 ・プログラミング言語C(通称 K&R)
http://www.amazon.co.jp/exec/obidos/ASIN/4320026926/

他の過去ログはここに
http://nssearch.hp.infoseek.co.jp/clang/
前スレ
C言語なら俺に聞け! Part 122
http://pc8.2ch.net/test/read.cgi/tech/1139570025/
0655デフォルトの名無しさん2006/03/10(金) 13:19:27
>>653
最適化を切っているとか度合いを低く抑えているとか、
或いはお前が末尾再帰だと思い込んでいるだけで実際には末尾再帰でないという可能性もある。
0656・∀・)っ-●○◎- ◆Pu/ODYSSEY 2006/03/10(金) 13:43:19
>>644
クィックソートあたりの実装を参考にするといいお。

こっちが再帰コール版
http://www.comp.dit.ie/rlawlor/Alg_DS/sorting/quickSort.c
再帰コールせず自前スタック管理とループで実現
http://www.d.umn.edu/~gshute/C/examples/quicksort.C.html

まぁ俺ならこんなコードにするかな。細かい部分は端折るが。

int depth = 0; //現在の階層; 0がトップ
LPNODE p = tree->head;
LPNODE *stack = (LPNODE*) malloc( sizeof (LPNODE) * 100 ) ;
for ( ;; ) {
  if ( p->child ) { //ネストあり
    開始タグ出力
    stack[depth++] = p; //push, 下の階層に逝く
    p = p->child;
  }

  出力とか

  if ( !p->next ) { //同階層の終端に達した
    p = stack[depth--]; //pop , 上の階層に戻る
    終了タグ出力
    if (depth == -1) break;
  }
  p = p->next; //同階層の次の要素を参照
}
free(stack);
0657デフォルトの名無しさん2006/03/10(金) 13:48:49
>>656
ちょうど団子先生がいるんで聞きたいんだが
末尾再帰とそのループ展開について解説してるいいサイト無い?
0658デフォルトの名無しさん2006/03/10(金) 13:50:53
全くわかりません><
0659デフォルトの名無しさん2006/03/10(金) 13:52:24
定数入れて最適化されただろって言ってるの笑える
0660デフォルトの名無しさん2006/03/10(金) 13:55:34
>>656
その糞コードで'|'や' 'をどうやってはさむつもりだ?wwwww
馬鹿も休み休み言えよwwwww
0661・∀・)っ-●○◎- ◆Pu/ODYSSEY 2006/03/10(金) 13:58:59
>>660
関数再帰コールベースなら簡単にできるとでも?馬鹿も休み休み言えよwwwwww
0662デフォルトの名無しさん2006/03/10(金) 14:01:59
>>657
俺団子じゃないけどいいかな。
http://d.hatena.ne.jp/yaneurao/20051008
http://d.hatena.ne.jp/yaneurao/20051025
0663デフォルトの名無しさん2006/03/10(金) 14:03:25
クイックソートの場合
スタックオーバーフローだけを心配するなら2分割したうちの短い方だけを再帰にして、長い方をループにするのが簡単
末尾再帰が最適化されることを前提にするなら短い方を先に呼び出す
自前でスタックを用意するなら長い方を先に積む

これでスタックの深さは確実に O(log n)以下になる
0664デフォルトの名無しさん2006/03/10(金) 14:04:42
>>659
gccとかなら-Sでも指定してアセンブラ吐き出せば
末尾再帰になってるのが一目瞭然ですが( ´_ゝ`)
06656522006/03/10(金) 14:06:31
>>659
g++で-Sを付けて出てきたコードを見ればループになっているのが分ると思いますよ。
0666デフォルトの名無しさん2006/03/10(金) 14:09:23
vcだとループにならない
0667デフォルトの名無しさん2006/03/10(金) 14:10:06
喪前らコンパイラのバージョンくらい書け
0668デフォルトの名無しさん2006/03/10(金) 14:12:54
おまいら俺にも理解できるように解説してくれ。
0669デフォルトの名無しさん2006/03/10(金) 14:13:28
>>661
ループでは簡単に出来ないことを認めたな?wwwww
0670デフォルトの名無しさん2006/03/10(金) 14:14:25
>>662
サンクス、ちょっと読んでくる
0671デフォルトの名無しさん2006/03/10(金) 14:17:19
末尾再帰はループになるかどうかがコンパイラ依存になる時点で糞
0672デフォルトの名無しさん2006/03/10(金) 14:18:41
せっかくだから>>654のコードをg++ -S -O3で(gcc 3.4.4)通してみた
関数の部分を見ると確かにループになってる。
gccって末尾再帰を最適化してくれるのか。知らんかった

__Z4factii:
    pushl   %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %edx
    movl    12(%ebp), %eax
    jmp L4
    .p2align 4,,7
L7:
    imull   %edx, %eax
    decl    %edx
L4:
    testl   %edx, %edx
    jne L7
    popl    %ebp
    ret
0673デフォルトの名無しさん2006/03/10(金) 14:19:40
C、C++の最適化について語るスレ
http://pc8.2ch.net/test/read.cgi/tech/1084676298/
0674・∀・)っ-●○◎- ◆Pu/ODYSSEY 2006/03/10(金) 14:29:25
>>669
端折ってるだけですが。単純化したほうが解りやすいだろ?

とりあえず関数の再帰を使うと簡単に書けることを証明してくれ。
インデント量をいちいちグローバル変数やパラメータで渡す必要がある時点で論外なのだが。

出力も厳密にはキューに溜め込んで、遅延処理することを想定してる。
入れ子になってる要素数に応じて、複数行に分けたり分けなかったりしたいからね。
0675デフォルトの名無しさん2006/03/10(金) 14:31:23
さすがに構文解析とかの多段?再帰をループで書こうとは思わんな。
目的と労力との天秤じゃね?
0676デフォルトの名無しさん2006/03/10(金) 14:39:01
>>674
>>656�dクス
そういえば>>621でも言ってるけど関数の引数を追加するとなんかまずいの?
0677デフォルトの名無しさん2006/03/10(金) 14:43:06
どう考えても再帰の方が書くのも楽で分かりやすいのだが...
0678デフォルトの名無しさん2006/03/10(金) 14:43:13
あーあ。腐れ団子が居付いちゃったじゃねーか。
0679・∀・)っ-○○◎- ◇Pu/ODYSSEY2006/03/10(金) 14:52:10
楽ってなんだよ。 要は手抜きってことだろ。このカス
0680・∀・)っ-●○◎- ◆Pu/ODYSSEY 2006/03/10(金) 14:53:29
>>676
関数のプロトタイプが変わっちゃうでしょ。仕様変更に柔軟に対応しにくい。
何より、渡す引数が増えればその分だけ関数コールスタックの消費も増える。

>>675
XMLの解析なら、さすがにXercesやMSXML使うかな。
労力の少なさ重視ならCじゃなしにインタプリタ言語でも使いますわ。

実際のコンパイラやインタプリタのソース見れば再帰コールなんて、まず使ってませんわ。
バグの根源っす。
0681デフォルトの名無しさん2006/03/10(金) 15:01:51
おいおいLALR(1)文法を再帰を使わずに書けるもんなら書いてみろよ
俺はfindの実装すらも再帰以外では書けないヘタレだから無理だけどな
0682デフォルトの名無しさん2006/03/10(金) 15:03:04
>>680
??変わっちゃうってどういうこと?
そもそも再帰コールで書くつもりで始めたのなら追加も何もないんじゃないの?
0683デフォルトの名無しさん2006/03/10(金) 15:08:40
>>671
末尾再帰を最適化できないようなコンパイラを選定してる時点でダメ。
0684デフォルトの名無しさん2006/03/10(金) 15:12:58
いまどき再帰がだめって、貧民プログラミングここに極まれりってかんじだな。

0685デフォルトの名無しさん2006/03/10(金) 15:16:29
yacc を使うほど仰々しくなくて自前で独自の文法のミニインタープリタを
ちゃちゃっと書いてテストするときって 先に再帰のほうを想像しないか?
いきなりループで思考するとかえってドツボ&バグの温床のような…
0686デフォルトの名無しさん2006/03/10(金) 15:19:01
末尾再帰をループに変換したコードを生成してくれるコンバータがあるから無問題
0687デフォルトの名無しさん2006/03/10(金) 15:20:48
カスどもがwwwww

void showTree(TREE* tree)
{
  static char indent[INDENT_LENGTH] = "";
  showTreeBody(tree->head, indent, 0, 0);
}

void showTreeBody(LPNODE current, char *indent, int above, int below)
{
  static const char *nextIndent[] = {"  ","| "};
  if(current) {
    showTreeBody(current->child, strncat(indent, nextIndent[above], INDENT_LENGTH), 0, 1);
    indent[strlen(indent)-2] = '\0';
    printf("%s*-+(%d)\n", indent, current->data);
    showTreeBody(current->next, strncat(indent, nextIndent[below], INDENT_LENGTH), 1, 0);
    indent[strlen(indent)-2] = '\0';
  }
}
0688デフォルトの名無しさん2006/03/10(金) 15:20:48
そもそも、BNF記法が再帰的だし、そのとおり実装しようとしたら再帰になるのが自然
ループだと不自然だろ?
0689デフォルトの名無しさん2006/03/10(金) 15:31:46
>>688
ループで非再帰化したBNF記法のようなもの登場か!!!
…イラネ
0690デフォルトの名無しさん2006/03/10(金) 15:39:03
1関数内だけで処理を済ませる団子先生なら何とかすんじゃね?
0691・∀・)っ-●○◎- ◆Pu/ODYSSEY 2006/03/10(金) 15:56:29
>>681
LRLRと自由文脈で英文法チェックルーチン(もどき)作るの講義でやったなぁ。懐かしい。
普通に自前スタックに退避で実現できるんじゃね?
Bisonあたりのソース参考にすればいい?ソースとってきてトレースして関数コールツリー
みてるけど、さて、再帰コールしてる箇所ってどこにあ(ry

findって要するにディレクトリツリーの中身全部展開して取得するだけでいいっしょ。
つい最近ディレクトリの中身を無駄なファイル除いてまとめてパックするプログラムとか
書かされたいたけど普通に再帰コールなしで出来たよ。いちおう参考までに。

>>682
共有しないといけないパラメータが、初期に想定してた分より変わるかもわからんね。

>>684
組み込みは無視ですか><
0692デフォルトの名無しさん2006/03/10(金) 17:35:36
再帰不能
0693デフォルトの名無しさん2006/03/10(金) 19:04:37
>>691
組み込みでも再帰は使うよ
案件によっては禁止の場合もあるってだけ
使う場合のノウハウはちゃんとある
0694デフォルトの名無しさん2006/03/10(金) 19:07:57
int confirm()
{
 char buf[256];
 printf("Y/N?");
 gets(buf);
 if(strcmp(buf, "Y")==0) return 1;
 if(strcmp(buf, "N")==0) return 0;
 return confirm();
}
0695デフォルトの名無しさん2006/03/10(金) 19:11:32
ループで思考しそうなところを再帰で表現 か。
やっぱ思考の過程をそのまま記述するのが自然でよいな。
その上で問題があるなら ループ⇔再帰 の置換を行えばいいし。
0696デフォルトの名無しさん2006/03/10(金) 19:17:32
>>694
うわー懐かし
新米の頃、似たようなこと書いて怒られたっけ(恥
0697デフォルトの名無しさん2006/03/10(金) 20:52:21
マルチスレッドについて調べてたら、マルチタスクを解説してるページも
ヒットしたので試しに読んだらスレッドとタスクの違いが良く分からなくなってきますた。
そんなC言語を習得中の漏れに解説して頂けるもっさエロイ人キボン。
0698デフォルトの名無しさん2006/03/10(金) 20:55:38
>>697
スレ違い。
0699デフォルトの名無しさん2006/03/10(金) 21:18:16
ここの人たちが色々な関数を作ってライブラリ化したら面白いかな?
prg2ch.libとか?
0700デフォルトの名無しさん2006/03/10(金) 21:20:33
lib配布で使う強者はおらんやろ
0701デフォルトの名無しさん2006/03/10(金) 21:54:35
もちろんソースつきだよな
0702デフォルトの名無しさん2006/03/10(金) 22:36:27
10進数を2進数に変換したいのですが、どうにもできません。
何が間違っているのかご指摘を本当にお願いします。

#include <stdio.h>

void change(int *data);
int main(void)
{
  int input;

  puts("値の入力");
  scanf("%d",&input);
  change(&input);
  printf("%d",input);

  return 0;
}
void change(int *data)
{
  int *temp;

  while(*data / 2 != 0){
    *temp++ = *data % 2;
    *data = *data / 2;
  }
}
0703デフォルトの名無しさん2006/03/10(金) 22:38:45
実行結果では123と入力すると0しか帰って来ません。
と言いますか、全ての数字において0が帰ってきます。助けて下さい。

コンパイラはgccです。
0704デフォルトの名無しさん2006/03/10(金) 22:42:12
内部的には10進数も2進数も違いはない
2進数で表示したいというのなら文字列にしないと
changeについては殆ど意味のない関数になってる
単にdataが0か1になるまで割り算していってるだけ
tempもまったく意味がない
0705デフォルトの名無しさん2006/03/10(金) 22:42:27
あなたには2進数への変換の前にもっとやるべきことがある。
0706デフォルトの名無しさん2006/03/10(金) 22:44:49
>>704
ほんとですか・・・文字列で書き直して再度聞きに来ます。ありがとうございます!!

>>705
それをぜひ教えて頂きたいです。
0707デフォルトの名無しさん2006/03/10(金) 22:50:11
>>706
こんな感じで良いよ。

void change(int *data) 
{ 
    int temp=0;
    int i=1;
    while(temp = temp+*data%2*i,*data / 2 != 0){ 
        i*=10;
        *data = *data / 2; 
    }
    *data = temp;
}
0708デフォルトの名無しさん2006/03/10(金) 22:56:04
>>707
dataでかいとあふれるぜ
0709デフォルトの名無しさん2006/03/10(金) 22:58:29
>>707
うぁぁ・・・むずかしい・・・orz。

少しwhile式内を考えてみます。わざわざ解答書いてくださりありがとうございます!
0710デフォルトの名無しさん2006/03/10(金) 22:59:20
>>708
仕様ですw

>>709
>>707は急いで書いたから変になったけど、こう書いたほうが読みやすいかもしれない。
void change(int *data)
{
    int temp=0,i=1;
    while(*data != 0){
        temp = temp + *data%2 * i;
        *data = *data / 2;
        i *= 10;
    }
    *data = temp;
}
0711デフォルトの名無しさん2006/03/10(金) 23:04:39
ビット表現ではなく単に2進数で
表示だけしたいのならこういう手もある

void print_bin(int n)
{
    if(n < 0){
        putchar('-');
        print_bin(-n);
    } else {
        if(n/2 != 0)
            print_bin(n/2);
        printf("%d",n%2);
    }
}

int main(void)
{
    int n;
    scanf("%d",&n);
    print_bin(n);
    putchar('\n');
    return 0;
}
0712・∀・)っ-○●◎- ◆Pu/ODYSSEY 2006/03/10(金) 23:07:18
>>700
C:\Documments and Settings\だんご 食べたい\My Documents\Visual Studio Projects\
ってパス名が含まれるからですか?><
0713デフォルトの名無しさん2006/03/10(金) 23:11:16
F:\Document and Settings\Administrator\My Documents\Visual Studio Projects\
じゃないのか。
0714デフォルトの名無しさん2006/03/10(金) 23:11:32
>>711
-2147483648
0715デフォルトの名無しさん2006/03/10(金) 23:13:17
>>714
仕様で(ry
まじごめん
0716デフォルトの名無しさん2006/03/10(金) 23:24:32
>>710
式内の仕組みがやっと分かりました!!ありがとうございます!
これを参考にして16進数とかに挑戦してみます。
07177072006/03/10(金) 23:35:49
>>716
特に変換処理をしなくても
printfで%xを指定したら自動的に16進数で表示してくれるよ

printf("%x",input); 
0718デフォルトの名無しさん2006/03/10(金) 23:36:03
ちょっと話を戻してすいませんが、Cで末尾再帰の最適化を
期待して書くのってやっぱり(仕事でやる場合)良くないこと
なんですかね? 組織のポリシー次第と言えばそれまでですが…

自分は再帰の回数が十分少ないと断言できる場合のみ
使っています。楽ですし。
0719デフォルトの名無しさん2006/03/10(金) 23:42:52
>>717
書式指定子の変更で16進に変換可能できるんですか!凄いですね(^ω^;)
ではいろいろネットで違う基本を学んできます。
0720・∀・)っ-○●◎- ◆Pu/ODYSSEY 2006/03/10(金) 23:43:29
Ackermann関数の洗礼を受けたので基本的に再帰コール使わずに書くのはポリシーです。
0721デフォルトの名無しさん2006/03/10(金) 23:45:39
>>719
仕事なら、性能要件を満たす限りはなるべく分かりやすいコードがよい。
0722デフォルトの名無しさん2006/03/10(金) 23:46:25
×>>719
○>>718
0723デフォルトの名無しさん2006/03/10(金) 23:47:46
>>718
最適化に限ったことじゃないが「保証されている」かどうかが仕事では超重要
0724デフォルトの名無しさん2006/03/10(金) 23:49:37
gccには-foptimize-sibling-callsというオプションが
あるけど、これって末尾再帰最適化してくれるの?
0725・∀・)っ-○●◎- ◆Pu/ODYSSEY 2006/03/10(金) 23:51:00
末尾再帰のループ展開を言語仕様で保障してるのってSchemeとかあの手の言語くらいじゃね?
0726デフォルトの名無しさん2006/03/11(土) 00:31:06
>>718
Cの規格でぜったい保障されてる範囲でしかコードを書かないってことになったら、
まともに実用のコードなんてかけないよ。
0727デフォルトの名無しさん2006/03/11(土) 00:37:04
再起末尾の最適化がなしだとしても、再起の深さ数十回程度なら、まったく問題なし。
0728・∀・)っ-○●◎- ◆Pu/ODYSSEY 2006/03/11(土) 00:46:36
http://www.db.is.kyushu-u.ac.jp/adp/

ここは俺的に要ブックマーク
0729デフォルトの名無しさん2006/03/11(土) 00:46:50
>>726
おめー、厳密規格合致プログラムのことを言っているのか?
処理系定義だって立派に保証されている動作だし、
void hoge(int); だって、それを出荷しているベンダーが保証している場合もあるぞ?

自分の仕事の成果に対する責任範囲と「移植性」を近藤しとるんちゃうか?
金もらう時点でここで引っかかる奴はいないはずだが・・・
0730デフォルトの名無しさん2006/03/11(土) 00:54:04
>>729
この話の流れで、なんでそういう方向に行くのか分かりません。
0731デフォルトの名無しさん2006/03/11(土) 01:15:14
intのサイズも決めていない規格に何を求めているのやら...
0732デフォルトの名無しさん2006/03/11(土) 08:21:19
実行効率
0733デフォルトの名無しさん2006/03/11(土) 10:57:05
あまりにも移植性ばかり考えると開発しづらくない?
あまりにもトリッキーで環境依存なコードも問題あるが。

・・・とここのスレを見て感じました。
0734デフォルトの名無しさん2006/03/11(土) 10:58:22
意味もなくコストをかけて移植性を確保する奴はアホ。
意味もなくノーコストで得られる移植性を損なう奴もアホ。
0735デフォルトの名無しさん2006/03/11(土) 11:02:36
関数の引数にダイレトで式を書いても問題
ないでしょうか? 以下は例です。

for (i=1;i<=10; i++) {
malloc( i*4096 );
}

0736デフォルトの名無しさん2006/03/11(土) 11:05:25
式の値が引数として渡されるから問題ないけど
その例は別の意味で問題ある
0737デフォルトの名無しさん2006/03/11(土) 11:15:10
> 736 ありがとうございます。
別の問題とは、どういう問題でしょうか?
0738デフォルトの名無しさん2006/03/11(土) 11:15:51
どんな簡略化したサンプルにも必ず難癖つけてしまう病気だから気にするな。
0739デフォルトの名無しさん2006/03/11(土) 11:17:18
違和感を感じないなら、気にしなくていいよ
0740デフォルトの名無しさん2006/03/11(土) 11:37:24
malloc で確保したメモリのアドレスを受け取っていないから、free できないってことじゃね
0741デフォルトの名無しさん2006/03/11(土) 11:40:06
> 740 ご指摘ありがとうございます。
確かにそうですね。例の誤りですね。
0742デフォルトの名無しさん2006/03/11(土) 11:45:14
本に書いてあるプログラムで

static char a[M+1][N+1];と宣言してあって
その後、a[i][j]++;とかすぐにインクリメントしたりしてるんですが
この宣言方法の場合、最初に0で初期化しなくても0が挿入されてるんですか?
0743デフォルトの名無しさん2006/03/11(土) 11:46:29
staticはゼロで初期化される
0744デフォルトの名無しさん2006/03/11(土) 11:52:01
>>743
ありがとうございます
流れ見ていく限り、どうもすべてに0が入っているのを
前提とした感じだなぁっていうのはわかったんですが
static使ったことなくて、調べても次に呼び出されたとき
値が変化しない静的としか書かれてなかったんでわかりませんでした
0745デフォルトの名無しさん2006/03/11(土) 20:28:13
>>744
規格ではゼロ初期化ってことになってるけど
このルールには従っていない処理系が結構あるから
ちゃんと確認したほうがいいぞ
0746デフォルトの名無しさん2006/03/11(土) 20:29:36
例えば?
0747デフォルトの名無しさん2006/03/11(土) 20:31:35
>>745
そんな処理系無いだろ
少なくとも普通に手に入るであろう処理系ではの話だが

とゆうかstatic変数がゼロクリアされるのはC89で既に規格化されてるんだから
C89にすら準拠してないような糞処理系は窓からry
0748デフォルトの名無しさん2006/03/11(土) 20:48:43
まあまあ。
結構あると言い切ってるんだから
まずは話を聞いてみようじゃないか。
0749デフォルトの名無しさん2006/03/11(土) 21:06:39
俺が直接くらったのは通算2つ
言っていいのはここまで

特に 747 みたいなのがいるところで言ったらシャレにならん
0750デフォルトの名無しさん2006/03/11(土) 21:16:54
>言っていいのはここまで
何か厨臭いな
0751デフォルトの名無しさん2006/03/11(土) 21:23:55
標準C以外の話は一切するなといってみたり
標準Cなんて信用するなといってみたり
なかなか初心者に辛いスレだな。
0752デフォルトの名無しさん2006/03/11(土) 21:35:30
組み込み向けコンパイラで、自分でスタートアップで初期化してねというものはあったが
ANSIなどと書かれてるコンパイラならクリアされてるはずだわな
0753デフォルトの名無しさん2006/03/11(土) 21:56:17
>>736と>>745は同一人物なのかね。不毛な突っ込み方がそっくりだな。
07547362006/03/11(土) 22:03:17
ん?一応違うけど
736=743=754
■ このスレッドは過去ログ倉庫に格納されています