パスワードを忘れた? アカウント作成
この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。

「情報処理」教員免許の試験問題」記事へのコメント

  • by Anonymous Coward on 2001年12月28日 19時34分 (#50629)
    うちの大学でも void main って教えてますよ。
    教科書にそう書いてあるから。
    もう、どうにでもなれって、感じですね。
    情報処理学会のどこかの部会かなんかが出した、
    情報教育の教科書でも、void main ってなってますよ。
    動きゃいいんじゃないの?
    もう、知らんって感じ。
    • by Oliver (4) on 2001年12月28日 20時43分 (#50646) ホームページ 日記
      数学由来な情報科学的にはvoid mainが正しいらしい。なぜなら、

      メソッドは副作用があり、戻り値がない。エラーはExceptionなどの別の方法で通知。

      ファクション(関数)は副作用がなく、戻り値がある。戻り値の算出に引数以外に依存すべきではない。

      というのが「正しい」姿だからだそうだ。で、mainはメソッドなので、voidでなければいけないそうだ (うちのオブジェクト指向論教授談)。

      exit(1)の1ってmain()の戻り値やん、と反論したら、いやー
      returnじゃないから、それはthrowと同等だ。とか云々で現実と理想のギャップについて教授と喧嘩してしまった。関数型言語だったら、ここいらは美しいんだけど、imperativeだとね。

      Javaの静的タイピング らぶ な教授なんだけど、java.lang.Objectにcastしまくりな現状とRubyみたいな完全動的タイピングについても意見を求めたら.... ってそれは別の話。
      親コメント
      • by oku (4610) on 2001年12月28日 20時52分 (#50649) 日記
        すみませんが、出来れば
        で、mainはメソッドなので、voidでなければいけないそうだ (うちのオブジェクト指向論教授談)。
        の「main がメソッドである」理由を当の教授がどのように説明していらしたか知りたいのですが。
        親コメント
        • by oku (4610) on 2001年12月28日 21時27分 (#50658) 日記

          自己フォロー。

          教授氏の言わんとするところは、main() が関数であるためには、

          int main(int argc, char **argv)
          {
              return argc;
          }
          のように「return の値は引数だけに依存すべき」だという事ですね?

          # じゃあ isprint(3) は locale の状態に依存するから
          # void じゃないといけないって事?

          親コメント
          • by Anonymous Coward
            mainの第3引数envpってのがあるけど…
            • by oku (4610) on 2001年12月29日 8時28分 (#50724) 日記
              envp は UNIX (とその係累) の方言ですね。
              差し支えなければ extern char **environ か getenv(3) を使ったほうがいいでしょう。
              親コメント
              • by Anonymous Coward
                いや、引数だけでなんとかしろっていうから。
                つーか、C言語に「UNIXの方言」っていう表現もなー。
              • by oku (4610) on 2001年12月29日 11時42分 (#50756) 日記
                いや、引数だけでなんとかしろっていうから。
                そういう意味だと私は argv も使っちゃいませんよ。:-)
                (「引数だけ」に依存した return の例示なら argc で十分でしょう?)
                つーか、C言語に「UNIXの方言」っていう表現もなー。
                まあ気持ちは分からなくもないんですが これだけ C/C++ が普及 (特に Windows) するとねぇ...。

                # Win な若人に「この bzero() って何ですか?」って
                # 聞かれたときには「時代は変わった」と思いましたです。
                # まあ書いた人にも「今時なら memset() 使えよ」とか
                # 思いましたけど。

                親コメント
      • UNIX 系の C の場合,そもそも main() って,なんとかcrtかんとか.o というスタートアップルーチン(?)からコールしている関数に過ぎないのだけれど.というわけで,引数と戻り値については,単になんとかcrtかんとか.o との摺合わせ,という話になるのではないか,と思います.

        あと,ライブラリ関数 exit(3) は (linux では) システムコール _exit(2) を呼び出すようです. このシステムコールは,二度と呼び出し元の関数へは制御は戻らないので,「戻り値」というのはまた別だと思います.

        親コメント
        • サブジェクトの通り
          • サブジェクトの通り :-)

            ま,この試験の場合は規格の方に従うべきで,特定の実装に依存するのは好ましくないとは思いますが. ま,UNIX 系 OS ではそういう仕組みになってることが多いので,void main(void) でも矛盾しない実行ファイルが生成できる,というだけの話.

            親コメント
            • UNIXなら、現実の実装も int main です。
              あくまでも。
              そもそも、最初のC言語にvoidはないし。
              • by oku (4610) on 2001年12月29日 15時42分 (#50784) 日記

                更に言えば、DOS/Windows だって int main ですね。 但し「void でも構わない」事が公式に認められているだけで。

                void でも構わないとベンダが言明している UNIX のコンパイラは寡聞にして聞いたことがありません (探せば見つかるのかも知れませんが)。

                # ちなみに C# では main の戻り値は int でも void でも
                # 構わないことになっていたと思います。
                # この辺は Java の void な main の向うを張ったのか
                # Microsoft の C 方言の名残なのかは意図不明。

                親コメント
              • 問 ?
                ----------
                #include <unistd.h>

                void Main ( void );

                void _start (int argc, char *argv[])
                {
                        write ( 1, "_start\n", 7 );
                        Main();
                        _exit ( 0 );
                }

                void Main ( void )
                {
                        write ( 1, "hello,world\n", 12 );
                }
                ----------

                (1)Linux において,このプログラムを
                    $ gcc -Wall hello.c -S -o hello1.s
                    $ gcc hello1.s -c
                    $ ld hello1.o -Bstatic -lc -o hello
                と,コンパイルして,実行してみましょう.
                (Linux でなくても,gcc を使っている処理系なら動作するとは思いますが,未確認.)

                (2)あと,
                    $ gcc -DMain=main -Wall hello.c -S -o hello2.s
                    $ gcc hello2.s
                    $ ld hello2.o -Bstatic -lc -o hello
                のコンパイルでは,どのようなワーニングが表示されるでしょうか.
                また,このプログラムを実行してみましょう.

                (3) (1) で生成される hello1.s と (2) で生成される hello2.s を比較してみましょう.
                例えば
                    $ diff -u hello1.s hello2.s
                こんな感じ.

                ま,普通にプログラミング・コンパイルしてる分には int main ( int, char ** ) が正しいし,こんなことやっても何のご利益もないのですが.
                単にこれが _start から実行されるスタートアップルーチンとの「決まりごと」であって,カーネルインターフェースとは無関係である,というのはわかるとは思います.
                親コメント
              • 動く動かないの話だけだったら、
                char main[] = { 機械語... };
                だって、多くのマシン/OSで動くでしょ。
                DOSでも動くし。
              • 1984 年の「醜い C コンテスト」入賞作品 [ioccc.org]

                ...というだけではナンなので。 :-)

                今回のトピックは試験問題が遡上にあがっています。 試験というからには前提となる「ルール」があるはずで 日本国の教員資格の試験であれば、それは JIS 規格であるべきでしょう。 gcc とか (以下略) の個別の実装の話を出すのは (カルマを減らす or ちゃちゃいれ or 他人をおちょくる or so on が目的でなければ) そろそろ潮時かな~と愚考する次第です。

                # もっともこの話題の発端は Oliver 氏の伝えるところの
                # 教授氏のmath-out [tuxedo.org]な話から始まっているので
                # 結局はこの教授氏を説き伏せるのにどちら (規格 or 実装) が
                # 有効かという話に帰結したりするのかも。

                親コメント
              • 「現実の実装」の例の1つを示したつもりだったのだけれど,何を聞こうとしていたのでしょうか?

                char main[] = { 機械語... };

                機械語…の部分が,値を返さずに return するプログラムだとしても,これでは,「main が値を返さないルーチン」として矛盾の無い実行プログラムを生成することはできません. 普通にコンパイルした場合,コンパイラの標準のスタートアップルーチンがリンクされるので,呼び出し側との整合性が取れません. その結果,void main(void) と宣言してコンパイル・実行した時と同じように,「終了ステータスにゴミが返される」という現象が生じるでしょう. そもそも,「動く動かない」だけのの問題だったら,スタートアップルーチンがうんぬん,と言い出さなくても void main(void) でも動きますね.

                なんか話がそれてしまってる気もするので,今までのところをまとめてみると

                • C の規格では int main(int,char**) が正しい
                • 多くの UNIX 系 OS では,execve() と main() の間にはスタートアップのルーチンが存在する
                  普通はここでシェアードライブラリの実行時リンクと,カーネルと main() のインターフェースを行っている
                • というわけで,実装の観点から「なぜ int main(int,char**) なのか」と聞かれれば,「スタートアップルーチンでの呼び出し側と呼び出される側のインターフェースの問題」ということになる
                • このため,スタートアップルーチンに細工を施せば,OS やツールチェインの変更無しに void main(void) で矛盾の無い実行プログラムを作成することもできる
                • この際,gcc が警告を出すが,これは規格に対して義理立てしているだけで,OS・ツールチェインの実装上の問題からではない
                というあたりでしょうか.

                親コメント
              • by Anonymous Coward
                >「現実の実装」の例の1つを示したつもりだったのだけれど,何を聞こうとしていたのでしょうか?

                どっちも、規格にない「ブラックボックス」部分
                の実装を利用しているだけでしょう。
                ブラックボックスの部分の機能に依存したり、
                ブラックボックス自体を書き換えたりして、
                動くって言っても、そりゃ動くでしょうけど。

                スタートアップルーチンを書き換えちゃうのが、
                現実の実装にのっ
              • どっちも、規格にない「ブラックボックス」部分
                の実装を利用しているだけでしょう。
                ブラックボックスの部分の機能に依存したり、
                ブラックボックス自体を書き換えたりして、
                動くって言っても、そりゃ動くでしょうけど。

                そのとおり. C 言語の規格においては,int main(int,char**) の呼び出し元については何も規定されていません. ということは,int main(int,char**) というのは,規格の内と外の境界線に接しているわけです. 現実の実装で int main(int,char**) がどのように反映されているか見てみるには,その境界の外に踏み出す必要があるわけです.

                なに? return値が保障されない?
                そりゃ、return文相当のものを書かない
                プログラマがいけないんでしょう。
                main() { printf("Hello world\n"); } と同じ。

                これもそのとおり. main をマシン語で直書きしても,呼び出し元と呼び出し先の引数と戻り値の受渡しについては,状況はなにも変わりません. void main(void) を,このインターフェースに矛盾が生じないよう実装するには,前に書いたように呼び出し元に手を入れる必要があります. つまり,標準的な実装ではないわけです.

                で,話をこのスレッドの私の最初の書き込みに戻すと,「main() の引数と戻り値の問題は,実装の面から見ると,スタートアップルーチンとのインターフェースの問題である」というあたりは御理解いただけたようですね. あと,「実際にはどうなってるんだろ.知りたいな」と横から眺めてる,スケベ心満載の御仁にも満足いただけたと思います :-)

                親コメント
      • by yu_raku (419) on 2001年12月29日 2時10分 (#50695)
        >ファクション(関数)は副作用がなく、戻り値がある。戻り値の算出に引数以外に依存すべきではない。

        仮にこの定義を受け入れたとすると、現在のコンピュータでは、関数を表現することは不可能だと思います。
        単に引数から戻り値を得るだけの用途でも、時間と空間(記憶域)を消費するという副作用をもちますから。

        なので数学的な意味での関数と、コンピュータ用語の関数では、名前が似てるだけで異なるものであるとして扱ったほうがいいように思います。
        親コメント
        • by Anonymous Coward
          >ファクション(関数)は副作用がなく、戻り値がある。戻り値の算出に引数以外に依存すべきではない。

          仮にこの定義を受け入れたとすると、現在のコンピュータでは、関数を表現することは不可能だと思います。
          A Purely Functional Languageと名乗る言語についてはどう思われますか?
          http://haskell.org/
      • by G7 (3009) on 2001年12月29日 9時17分 (#50728)
        ところで、メソッドであることとファンクションであることとは
        排他的事象なのでしょうか?

        どっちにせよ、Cの関数の返し値が、数学でいう関数の返し値と
        同じものかどうか怪しいっすね。
        たんに、数学のほうの返し値の猿真似(???)な書式をCは使える、意味が同じかどうかはさておき、というだけじゃないかと。
        親コメント
      •  数学(集合論?)的な関数定義には、

        >ファクション(関数)は副作用がなく、戻り値がある。戻り値の算出に引数以外に依存すべきではない。

        プラス、

         同じ値の引数で呼び出されたときは、同じ戻り値を返す。

        なんてのもあったと思います。

         ですから、ランダム関数は、本当は関数ではないと教えられた覚えがあります。(笑)

         しかし、コンピュータの数学的モデルであるチューリングマシーン自体が、入力集合と出力集合以外に内部状態とその状態遷移関数を内包しているわけですし、一般的なプログラミング言語の関数を純然たる数学的関数のみで定義するのは、至難の業なのではないでしょうか。

        # もっとも、チューリングマシーンは、初期状態と状態遷移関数が固定されてますので、初期状態から全く同じ入力記号列がインプットされれば、必ず同一の出力記号列が得られます。
        # そういう意味では、シード値を与えた擬似ランダム関数と同じくらいには、まっとうな“関数”です。(^^;

         いまどきのコンピュータ言語の関数の定義って、「戻り値を返せるサブルーティン」くらいの意味であって、数学的な意味合いとは別物と割り切るべきなんでしょうね。

         個人的には、コンピュータ言語の関数定義は、ギリギリがんばっても、「同一の内部状態( <-これがクセモノ)と、同一の入力値を与えられたら、同一の出力値を返す」くらいの定義かなと思います。

         ちなみに、この定義に則ると、引数で渡されたオブジェクトの内部状態は関数内で変更してはいけないことになります。CONST付き引数の関数のイメージですね。
         こんな関数ばかりだったら、使い難くって仕方ないですね。(笑)

        <おふとぴ>
         そういえば、C言語が一般に広まるときに、「C言語もPascalもAlgol-60系の構造化プログラミング言語だけれど、Pascalは(関数も使える)手続き型言語で、C言語は(手続き的な関数も使える)関数型言語だよ」と云う説明がまかり通っていた記憶があります。
         当時、こういう風なツッコミを入れる人が周りに居なかったのが、不思議です。
        </おふとぴ>
        --
          --- Melloques Les Covdrasey ---
        親コメント
      • by Anonymous Coward
        メソッドというより、プロシージャといったほうが
        しっくりくるけど、int main は、ISO規格だからなぁ。
        自分の理想のために、互換性を犠牲にするわけにはいかん。
        • by G7 (3009) on 2001年12月29日 9時09分 (#50726)
          >自分の理想のために、互換性を犠牲にするわけにはいかん。

          理想は理想ですが、
          数学的(なのですね?)な主張に対して「自分の」主張だと言えるのかどうかは、
          また別の問題かと。

          #ただ、名前の定義を間違っているような気がするんで、
          #数学的に正しい主張かどうか?がまず心配ですが。
          親コメント
          • by Anonymous Coward
            手続き型言語で、戻り値を持つ手続きのことを「関数手続き」 と呼びます。通常はこれを縮めて単に「関数」としか呼びませんが。というわけで、C言語の関数と数学(や関数型言語)の関数と混同するのはそもそものモノ知らず。いや素人さんがたなら無理もないのですが、情報系の学科の大学教
            • 情報系の学科の大学教官が
              > 数学由来な情報科学的にはvoid mainが正しい
              なんて言っちゃ困りますねぇ(あるいはすべて承知の上での確信犯的発言なのか?)。C言語を対象とせずにそう言ったのならべつにOKなんですが。
              それは多分に、 int main()なんて設計すんじゃねーよという意味を込めて言っているんだと思う。
              ただ、それは受け入れがたいんですよね。
              • ANSI制定時に、世の中には既にint main()で設計された処理系がうじゃうじゃあった。
              • 更に遡るに、UNIX用ではリターンコードの処理を取り入れた関係で、シェルから呼び出されるプログラムは戻り値を持つ必要があった(どう見てもexit(3)はリターンコードの設計が完成しないと設計{できない|されない}…ですよねぇ…。)、つまりint main()は必然の設計だった。
              • int main()void main()が両立するような処理系の設計を規格の名の下で強要することは許されることではない。(「受容可能にするかどうかは処理系設計者が自分で決めて下さい」になってしまってもしょうがない。MSの処理系はselfstanding readyと見るべきなんでしょう:-))
              だからこそ、hosted C環境は「出ていくときはきちんと後始末をつけましょう」という設計をしているのでしょう。

              #当方に間違い・勘違いがあったらつっこんでください。
              親コメント
    • by Anonymous Coward
      void main ってかくと、gcc がウォーニングを出す。
      「教科書の通り打ったら、なんかエラー出るんですけど」

にわかな奴ほど語りたがる -- あるハッカー

処理中...