K.Maebashi's BBS

ご自由に書き込んでください。雑談も可。
テスト書き込みの類はテスト用掲示板にどうぞ

[日付順表示] [日付順インデックス] [スレッド順インデックス]

新規投稿 | 開設者ホームページへ戻る | ヘルプ

[1748] Re:Cにおける列挙型の扱いについて
投稿者:yuya
2011/10/10 14:07:56

再度ありがとうございます。 ># 集合論的におかしい 激しく同意。例によって例のごとく、といいますか(^^;) >となり、やはり int となるため問題ないです。 了解です。 >6.7.2.2 の[制約]は[定数の値を定義する式]としか書いてないので、 >enum e_bbb { bbb=2147483647, ccc }; は言語規格書的には合法と考えざるを得ません。 ># gcc ではきっちりコンパイルエラーになりますが。 ですよねぇ。そこも凄く気になってました。 ちなみにLSI-Cで試すとコンパイルが通りました。 (intが2バイトで、bbbを32767とするとcccは-32768)
[この投稿を含むスレッドを表示] [この投稿を削除]
[1747] Re:Cにおける列挙型の扱いについて
投稿者:774RR
2011/10/10 12:54:21

以下 JIS X3010:2003 より抜粋するので C の場合に限定 6.7.2.2 - 列挙型 - 列挙子並びの中の識別子は、型 int を持つ定数として宣言され、 この型の定数が許されるところならばどこに現れてもよい なので元ネタのキャストは要らないと判断してよさそうです。 enum e_aaa 型の underlying type は char であってもよい、のと、 その要素である aaa の型は int である、のとが矛盾してるように見えますが・・・ # 集合論的におかしい enum 型の識別子ではなくて enum 型の変数(オブジェクト)が現れた場合には 6.2.5 - 型 - 列挙体は[整数型]である 6.3.1.1 - 整数型 - 整数拡張 元の型のすべての値が int 型で表現可能な場合 int 型に変換される 6.7.2.2 - 列挙型 - 列挙定数の (既述につき snip) となり、やはり int となるため問題ないです。 6.7.2.2 の[制約]は[定数の値を定義する式]としか書いてないので、 enum e_bbb { bbb=2147483647, ccc }; は言語規格書的には合法と考えざるを得ません。 # gcc ではきっちりコンパイルエラーになりますが。 C++ の場合はこの辺いろいろ規制緩和されている+言語仕様も厳密化されているので 仕様書を読んでいて安心できます。先の [制約] のような不安要素も文書化され済み。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1746] Re:Cにおける列挙型の扱いについて
投稿者:yuya
2011/10/09 18:08:45

774RRさん、ありがとうございます。よく理解できました。 列挙体のメンバを右辺値として使うときに「int型の定数」とみなして実質的に不都合が起こることはなさそうですね。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1745] Re:Cにおける列挙型の扱いについて
投稿者:774RR
2011/10/07 13:32:08

>例えばメンバの値の範囲がcharで収まるなら、 >処理系はcharを使って領域を節約してもよい ですます。たとえば gcc であれば -fshort-enums オプションがあります。 enum e_aaa { aaa=1 }; printf("%zd\n", sizeof (enum e_aaa)); -fshort-enums なしでコンパイルすると 4 -fshort-enums ありでコンパイルすると 1 >あくまで「(charと適合する)列挙型の列挙定数」であって、 >式の中に現れると汎整数拡張されてint型に格上げされる ですます。 ただし sizeof('a') と同様に C と C++ で違うところなので要注意。 printf("%zd\n", sizeof (aaa)); gcc -fshort-enums hoge.c だと 4 g++ -fshort-enums hoge.cpp だと 1
[この投稿を含むスレッドを表示] [この投稿を削除]
[1744] Cにおける列挙型の扱いについて
投稿者:yuya
2011/10/07 09:35:26

C限定の話で、列挙型についての疑問です。 JIS X3010:2003の6.7.2.2 列挙型指定子(p77~p78)に、 > 制約 列挙定数の値を定義する式は、int型で表現可能な値を持つ整数定数式でなければならない (中略) >それぞれの列挙型は、char、符号付き整数型又は符号無し整数型と適合する型とする。型の選択は、処理系定義とする。しかし、その型は列挙型のすべてのメンバの値を表現できなければならない。 とあります。 列挙型が必ずしもint型ではなく上記のような選択の余地を処理系に与えているのは、 例えばメンバの値の範囲がcharで収まるなら、処理系はcharを使って領域を節約してもよい、という意義だと理解してよいのでしょうか? 通常、列挙体のメンバを式中で用いるときには、何も意識せずにint型の定数として (私は)書いているのですが、このメンバ自体はint型とは限らないわけですよね。 ある列挙型に対して例えば「charと適合する型」が選ばれた場合、そのメンバは あくまで「(charと適合する)列挙型の列挙定数」であって、 式の中に現れると汎整数拡張されてint型に格上げされる、という理解でよいのでしょうか? この疑問が生じた直接のきっかけは、(ぱ)さんの「プログラミング言語MIL」の雑誌記事のmini_mvm.cにおいて、 (A)や(B)の箇所でintへのキャストがなされているのを見て、「どんなときにキャストすべきなんだっけ?」と再考したことによります。 相変わらず記事の本筋と関係なくてすみません……。 皆様よろしければご教示ください。 typedef enum { OP_PUSH_INT, OP_ADD, OP_MUL, OP_PRINT } OpCode; int g_bytecode[] = { /* (A) */ (int)OP_PUSH_INT, 10, (int)OP_PUSH_INT, 2, (int)OP_PUSH_INT, 4, (int)OP_MUL, (int)OP_ADD, (int)OP_PRINT, }; int st_stack[STACK_SIZE_MAX]; void mvm_execute(void){ int pc = 0; int sp = 0; // スタックポインタ while (pc < sizeof(g_bytecode) / sizeof(*g_bytecode)) { switch (g_bytecode[pc]) { case OP_PUSH_INT: // 整数をスタックに積む st_stack[sp] = (int)g_bytecode[pc+1]; /* (B) */ sp++; pc += 2; break; case OP_ADD: // 加算 /* 以下略 */ } } }
[この投稿を含むスレッドを表示] [この投稿を削除]
[1743] モチベが~~~
投稿者:
2011/10/05 16:07:12

また、モチベーションが落ちてきた… 根性が居る修正を始めようとして、なぜか横道に逸て、逸れすぎて。 何をしようとしてたか忘れた。 もちろん、大きな内容の修正は忘れてないが、ソースのどの部分を修正しようと してたのか忘れた… 何かしないと復活できない>< (日記ですね;;)
[この投稿を含むスレッドを表示] [この投稿を削除]
[1741] Re:ローカル変数のアドレスをスタックにpushしている?
投稿者:とも
2011/09/11 11:34:04

早速のお返事ありがとうございます。 なるほど、crowbarスタックの構造を勘違いしてました。 Stack構造体のstackメンバがCRB_Value*型だということは、"CRB_Value*"の集合ではなく、"CRB_Value"そのものの集合だということですね。 自分で作成中のものが、スタックを"アドレスの集合"にしているので、それと同じだと思い込んでいました。 参考になりました。 これからも、「プログラミング言語を作る」を参考にオリジナル言語の作成を続けたいと思います。 >こんにちは。ご意見ありがとうございます。 > >>「プログラミング言語を作る」を大いに参考にさせて頂いています。 >>というより、かなりまねをさせてもらってます。 > >そういっていただけると私も嬉しいです。 > >>関数の初頭で、resultというCRB_Value型の変数を宣言していますね。 >>式の評価結果をそれに詰め、最終的にcrowbarスタックにそのアドレスをpushしていると思います。 >>しかし、resultはローカル変数なので、関数を抜けるとその領域は既に使えないのではないか?という心配です。 > >・resultはローカル変数なので、関数を抜けるとその領域は使えません。 >・最終的にcrowbarスタックにresultをpush()する際は、以下のような処理を > 行っています。 > push_value(inter, &result); >・resultのポインタをpush_value()に渡したって、resultの領域は > すぐに開放されてしまうのだから役に立たないのではないか? というのが > ご懸念されていることだと思います。 > >しかし、push_value()の中では、最終的には以下のようにして値をcrowbarスタックに >格納しています。 > >static void >push_value(CRB_Interpreter *inter, CRB_Value *value) >{ >(中略) > inter->stack.stack[inter->stack.stack_pointer] = *value; > >渡されたCRB_Valueは確かにポインタですが、*演算子により >「ポインタの指す先の値」をコピーしてスタックに格納していますので、 >この後でresultが開放されても問題ありません。 > >なお、push_value()の引数として、CRB_Value*ではなくCRB_Valueを受け取るように >することはもちろん可能です。 >現状の実装でなぜポインタを渡しているのかというと…… 構造体は大きいかも >しれないので値渡しをすると効率上の問題が出るかも、という懸念があったのかと >思います。実際問題としては、CRB_Value程度のサイズの型なら値渡しにするという >選択肢は十分考えられると思うのですけれども。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1740] Re:ローカル変数のアドレスをスタックにpushしている?
投稿者:(ぱ)こと管理人
2011/09/11 03:00:48

こんにちは。ご意見ありがとうございます。 >「プログラミング言語を作る」を大いに参考にさせて頂いています。 >というより、かなりまねをさせてもらってます。 そういっていただけると私も嬉しいです。 >関数の初頭で、resultというCRB_Value型の変数を宣言していますね。 >式の評価結果をそれに詰め、最終的にcrowbarスタックにそのアドレスをpushしていると思います。 >しかし、resultはローカル変数なので、関数を抜けるとその領域は既に使えないのではないか?という心配です。 ・resultはローカル変数なので、関数を抜けるとその領域は使えません。 ・最終的にcrowbarスタックにresultをpush()する際は、以下のような処理を  行っています。 push_value(inter, &result); ・resultのポインタをpush_value()に渡したって、resultの領域は  すぐに開放されてしまうのだから役に立たないのではないか? というのが  ご懸念されていることだと思います。 しかし、push_value()の中では、最終的には以下のようにして値をcrowbarスタックに 格納しています。 static void push_value(CRB_Interpreter *inter, CRB_Value *value) { (中略) inter->stack.stack[inter->stack.stack_pointer] = *value; 渡されたCRB_Valueは確かにポインタですが、*演算子により 「ポインタの指す先の値」をコピーしてスタックに格納していますので、 この後でresultが開放されても問題ありません。 なお、push_value()の引数として、CRB_Value*ではなくCRB_Valueを受け取るように することはもちろん可能です。 現状の実装でなぜポインタを渡しているのかというと…… 構造体は大きいかも しれないので値渡しをすると効率上の問題が出るかも、という懸念があったのかと 思います。実際問題としては、CRB_Value程度のサイズの型なら値渡しにするという 選択肢は十分考えられると思うのですけれども。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1739] ローカル変数のアドレスをスタックにpushしている?
投稿者:とも
2011/09/10 21:18:23

始めて投稿します。 趣味で、オリジナルの言語を作成している途中です。 「プログラミング言語を作る」を大いに参考にさせて頂いています。 というより、かなりまねをさせてもらってます。 その中で気になるところがあり、投稿しました。 私自身はC言語をそれなりに習得しているつもりなのですが、誤解があったらご指摘下さい。 気になったのは、書籍の163ページの下部から165ページにわたる、eval_method_call_expression関数の例です。 eval系では、似たようなコードがいくつかあると思います。 関数の初頭で、resultというCRB_Value型の変数を宣言していますね。 式の評価結果をそれに詰め、最終的にcrowbarスタックにそのアドレスをpushしていると思います。 しかし、resultはローカル変数なので、関数を抜けるとその領域は既に使えないのではないか?という心配です。 Cでは関数の戻り値としてローカル変数のアドレスを返そうとすると警告が出ると思います。 書籍の例は、関数の戻り値として返しているわけではないので警告は出ないとは思います。 が、crowbarスタックにpushするということは、後からそれを使用する可能性があると思うのですが。 つまり次のプログラムと本質的には同じ事をしているように思えます。 ---------------------------------------------- #include <stdio.h> typedef struct { int type; char* data; } CRB_Value; CRB_Value *value; void eval_method_call_expression(){ CRB_Value result; result.type = 10; result.data = "Hello!"; value = &result; } int main(){ eval_method_call_expression(); printf("%d\n", value->type); printf("%s\n", value->data); } ---------------------------------------------- コンパイルではエラーも警告も出ないですが、上のプログラムはもちろん不正です。 既に議論済みだったら申し訳ありません。 はたまた、私がとんでもない勘違いをしているかもしれません。 御教示をお願いいたします。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1735] Re:もう忘れられている気が・・・
投稿者:
2011/08/14 16:28:20

返事ありがとうございます。 >これって問題になるほど遅いですかね? 関数パラメータのコピーや、ローカル変数の設定命や、 その初期値の設定等がバイトコードではなく、C++の1関数でまとめて 領域を作成して、定数コピーと参照アドレスの一括変換をする予定なので。 ローカル変数の設定処理に一切のバイトコードが無くなるんです。 その結果、トータルとして関数コールがとても高速になると思いますよ。 >6.5万行というと、言語処理系としては相当なサイズだと思うのですが… いえ、ほとんどは、私が作っている言語以外のシステムのコードです。 言語部分はそれほど多くはないです。 たぶん、前回の出した行数より少し増えただけだと思います。 いまは、画面表示は総てDirectXで表示して、3Dキャラが動き回ってます。 もちろん、コンソール的な機能も持っていて、print関数もDirectX上でコンソール をエミュレートして表示しています。getchar()やgets()関数もある。 これをしたからといってシステムが入力待ちで止まったりはしません。 言語上の1スレッドが入力待ちでシステムにもどる(タスクスイッチのOSに処理が 戻るようなもの)だけです。 いまは、システム全体をセーブして、ロードするとそこから再開できる機能を 作っています。ちょっと苦労してる。 PCで言うところにリジュームみたいな機能です。 今のバイトコードです。 関数コールとリターンを分割したので、void関数コールは速いです 組み込み関数と言語上の関数も分割。 一般的な言語ではなく、組込言語なので、エラー処理のほとんどは システムが受け持って処理するので、ほとんどの関数はエラーリターン がありません。そのため、90%以上の関数はvoid型になり高速処理されます。 そうそう、総ての型にキャスト命令を作っていますが、明示的な型変換の方が いいのか多少悩み中。将来この自動キャストは止めるかもしれません。  ↓ {L"dummy", L"", 0}, {L"push_int_1byte", L"b",8}, {L"push_int_2byte", L"s",8}, {L"push_int_4byte", L"i",8}, // 実際の値を持つ {L"push_double_0", L"", 12}, {L"push_double_1", L"", 12}, {L"push_double_8byte", L"d",12}, // 実際の値を持つ {L"push_string_const", L"s",12}, // 文字列の位置NOを持つ0~ {L"push_null", L"", 8}, /**********/ {L"push_stack_int", L"s", 8}, // intローカル変数をスタックに {L"push_stack_double", L"s", 8}, // {L"push_stack_string", L"s", 12}, // {L"pop_stack_int", L"s", -8}, // スタックのintをローカル変数に {L"pop_stack_double", L"s", -8}, // {L"pop_stack_string", L"s", -12}, // /**********/ {L"push_static_int", L"s", 8}, // int静的変数をスタックに 予約 {L"push_static_double", L"s", 12}, // {L"push_static_string", L"s", 12}, // {L"pop_static_int", L"s", -8}, // スタックのintを静的変数に 予約 {L"pop_static_double", L"s", -12}, // {L"pop_static_string", L"s", -12}, // /**********/ {L"push_sheet", L"s", 8}, // sheet番号をスタックに {L"push_sheet_int ", L"s", 8}, // sheet変数をスタックに {L"push_sheet_double", L"s", 12}, // {L"push_sheet_str ", L"s", 12}, // {L"pop_sheet_int ", L"s", -24}, // スタックからsheet変数に {L"pop_sheet_double", L"s", -28}, // {L"pop_sheet_str ", L"s", -28}, // /**********/ {L"push_sheet_ref", L"s", 8}, // 参照したsheet番号をスタックに {L"push_sheet_ref_int ", L"s", 8}, // 参照sheet変数をスタックに {L"push_sheet_ref_double", L"s", 12}, // {L"push_sheet_ref_str ", L"s", 12}, // {L"pop_sheet_ref_int ", L"s", -24}, // スタックから参照sheet変数に {L"pop_sheet_ref_double", L"s", -28}, // {L"pop_sheet_ref_str ", L"s", -28}, // /**********/ {L"push_array_ref ", L"s", 8}, // 配列参照 {L"push_array_int ", L"s", -1}, // int配列処理 {L"push_array_double", L"s", -1}, // {L"push_array_string", L"s", -1}, // {L"pop_array_int ", L"s", -1}, // スタックからint配列に {L"pop_array_double", L"s", -1}, // {L"pop_array_string", L"s", -1}, // /**********/ {L"and_int", L"", -8}, // 以下は総て算術演算子 {L"or_int", L"", -8}, {L"xor_int", L"", -8}, {L"add_int", L"", -8}, {L"add_double", L"", -12}, {L"add_string", L"", -12}, {L"sub_int", L"", -8}, {L"sub_double", L"", -12}, {L"mul_int", L"", -8}, {L"mul_double", L"", -12}, {L"div_int", L"", -8}, {L"div_double", L"", -12}, {L"mod_int", L"", -8}, {L"mod_double", L"", -12}, {L"minus_int", L"", 0}, {L"minus_double", L"", 0}, {L"increment", L"", 0}, {L"decrement", L"", 0}, // ここまで算術演算子 /**********/ {L"cast_int_to_double", L"", 4}, // 以下はキャスト処理 {L"cast_double_to_int", L"", -4}, {L"cast_string_to_int", L"", -4}, {L"cast_string_to_double", L"", 0}, {L"cast_boolean_to_string", L"", 4}, {L"cast_int_to_string", L"", 4}, {L"cast_double_to_string", L"", 0}, // ここまでキャスト処理 /**********/ {L"eq_int", L"", -8}, // 以下は総て論理演算子 {L"eq_double", L"", -16}, {L"eq_string", L"", -16}, {L"gt_int", L"", -8}, {L"gt_double", L"", -16}, {L"gt_string", L"", -16}, {L"ge_int", L"", -8}, {L"ge_double", L"", -16}, {L"ge_string", L"", -16}, {L"lt_int", L"", -8}, {L"lt_double", L"", -16}, {L"lt_string", L"", -16}, {L"le_int", L"", -8}, {L"le_double", L"", -16}, {L"le_string", L"", -16}, {L"ne_int", L"", -8}, {L"ne_double", L"", -16}, {L"ne_string", L"", -16}, // ここまで論理演算子 {L"logical_and",L"", -8}, // {L"logical_or", L"", -8}, // {L"logical_not",L"", 0}, // /**********/ {L"pop", L"", -8}, // スタックを1つ減らす {L"duplicate", L"", 16}, // スタック内容をコピーしてスタックに {L"jump", L"s", 0}, // 指定ポインターにjump {L"jump_if_true", L"s", -8}, // スタックがtrueならjump {L"jump_if_false", L"s", -8}, // スタックがfalseならjump {L"nop------------",L"", 0}, // nop /**********/ {L"push_function", L"", 0}, // 関数情報をスタック、未使用 {L"call_n_function_v", L"s", 0}, // void 組込み関数コール {L"call_n_function", L"s", 0}, // 組込み関数コール {L"call_function_v", L"ssss", 0}, // void 関数コール {L"call_function", L"ssss", 0}, // 関数コール {L"return_v", L"", -1}, // void 関数戻り {L"return", L"", -1}, // 関数戻り /**********/ {L"set_array_literal_int", L"ss", 0}, // int定数配列を変数にコピー {L"set_array_literal_double",L"ss", 0}, // double定数配列を変数にコピー {L"set_array_literal_string",L"ss", 0}, // string定数配列を変数にコピー
[この投稿を含むスレッドを表示] [この投稿を削除]
[1734] Re:もう忘れられている気が・・・
投稿者:(ぱ)こと管理人
2011/08/10 02:17:58

お久しぶりです。 >・関数コールをVMの再帰ではなく、VM内でそのまま実行に変えました。 > (システムをそっくりセーブロードに必要なために) これはそのほうがよいと思います。そして、そうした場合、 >・関数起動の高速化は、優先順位が低くて保留。 これって問題になるほど遅いですかね? >その時は約3.5万行有ったのが、今は6.5万行になっても、まだ足りません(泣 >いまは、あと半年から1年で初期バージョンをと、またも楽観的な目標を>< 6.5万行というと、言語処理系としては相当なサイズだと思うのですが… javacでも、初代は1万行以内だと聞いたことがあるような気が(記憶違いかも しれませんけど)
[この投稿を含むスレッドを表示] [この投稿を削除]
[1733] もう忘れられている気が・・・
投稿者:
2011/08/04 19:01:43

あまりにもお久しぶりです。 相変わらず作成は続いています。 言語的な変更点は。 ・関数コールをVMの再帰ではなく、VM内でそのまま実行に変えました。  (システムをそっくりセーブロードに必要なために) ・elsif -> else if に変更。(よりCに似せるために><) ・static 宣言の追加、sheetと関数宣言のみ。  (言語上のプログラムを多く書いていますが、やはりスコープが…)  (いっそのこと、継承・多態もないカプセル化だけのクラスを…) その他にもちょこちょこと。 ・関数起動の高速化は、優先順位が低くて保留。 8ヶ月前、半年から1年で初期バージョンができるのを期待していましたが。 その時は約3.5万行有ったのが、今は6.5万行になっても、まだ足りません(泣 いまは、あと半年から1年で初期バージョンをと、またも楽観的な目標を><
[この投稿を含むスレッドを表示] [この投稿を削除]
[1732] 無題
投稿者:test
2011/07/29 22:27:05

test
[この投稿を含むスレッドを表示] [この投稿を削除]
[1730] Re:たちよりました!
投稿者:(ぱ)こと管理人
2011/07/11 01:41:55

>最近、0からphpで掲示板を作ってみようと思ってます。その流れできてみましたー(`・♀・´) はじめまして。 うちのページを参考にしていただけるのは嬉しいのですが、 ・バージョンが古いこと ・magic quoteを前提にしたコードになっていること については留意してください。 こちらも参考にしてみてください。 http://kmaebashi.com/zakki/zakki0042.html
[この投稿を含むスレッドを表示] [この投稿を削除]
[1729] Re: 
投稿者:(ぱ)こと管理人
2011/07/11 01:39:21

>おれは良い意味で良い。 >おれは良い意味で良い。 >お前ら悪い意味で悪すぎ。 また注意書きの読めない人が適当なテスト投稿をしていったのかと思ったら、 はてなのブログのコメント欄まで同様の書き込みが……(消しますが) なんなんだろう。怖い。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1728] たちよりました!
投稿者:p
2011/07/10 19:15:39

最近、0からphpで掲示板を作ってみようと思ってます。その流れできてみましたー(`・♀・´)
[この投稿を含むスレッドを表示] [この投稿を削除]
[1727]  
投稿者: 
2011/07/06 23:40:49

おれは良い意味で良い。 おれは良い意味で良い。 お前ら悪い意味で悪すぎ。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1726] 管理者により削除されました
2011/06/29 02:52:57

テスト投稿はテスト掲示板にお願いします、とでかでかと書いてあるのに、なおここに書き込むような人達にはどう対処すべきなのですかねえ。
[この投稿を含むスレッドを表示]
[1722] 管理者により削除されました
2011/03/31 20:46:15

この掲示板の上部にリンクがありますが、テスト投稿はテスト用掲示板にお願いします。
[この投稿を含むスレッドを表示]
[1721] Re:構造体へのポインタを返す関数
投稿者:512
2011/03/21 20:06:13

おーこんなにたくさんの方が! みなさんどうもありがとうございます。 おかげさまでだいぶ大事なことをいくつか理解できました。 .hと.cの使い分けは正直うすうす悩んでいたことなので、 今回の質問で、間接的ではありますが、すっきりできてよかったです。 とりあえず元のコードの.hは大掃除した方がよさそうですね。 あと、intの場合なぜ通ってしまったかは、 これから聞こうと思っていたのですが、 皆さん先回りありがとうございます(笑 ここまで分かるとだいぶすっきり感がありますねぇ。 tiさんのサンプルコード大変参考になります。 たぶんまた近いうちに壁に当たって戻ってきますので 引き続きよろしくお願いします。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1720] Re:構造体へのポインタを返す関数
投稿者:774RR
2011/03/21 19:58:15

えっと、今は「関数宣言」の話しかしてない、っての大丈夫? 「関数定義」の話は一切出てきてないんだけど。 関数宣言が何回現れても、関数定義がなければリンクエラーにはならないよん。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1719] Re:構造体へのポインタを返す関数
投稿者:774RR
2011/03/21 19:41:51

すばらしい。 typedef を使っているあたりが素敵すぎる。 というわけで第三者読者へのふぉろーを追加。 typedef は新しい型を作るのではなくて、既存の型への別名を作る。 提示の例だと名前のない構造体(便宜上 anonymous_struct_1 と名づける) struct anonymous_struct_1 { int val; }; に対して、別名1 KOUZOU1 別名2 KOUZOU2 をつけているだけ。 なので KOUZOU1 と KOUZOU2 はどちらも struct anonymous_struct_1 であり、 関数宣言のほうもエラーにならない。 typedef struct { int x; } KOUZOU1, *PKOUZOU2; KOUZOU1 *func(); PKOUZOU2 func(); int main() { return 0; } これもエラーにならない。 一方で KOUZOU3 は struct anonymous_struct_2 { int val; }; の別名になるので これは KOUZOU1, 2 とは別のものになり、宣言が矛盾するエラー。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1718] Re:構造体へのポインタを返す関数
投稿者:mano
2011/03/21 19:31:25

へぇ。そうなんだ。問題ないんだ。 遠い昔、Cコンパイラに怒られるのが嫌で、○○の一つ覚えみたいに、2個目以降はexternを付けたような、、、、確かリンカでエラーが、ん!あぁなるほど、今回はstaticだからだいじょーぶなんだ。。。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1717] Re:構造体へのポインタを返す関数
投稿者:yuya
2011/03/21 18:35:08

にゃるほどー。 コンパイラは宣言がダブってる(re-declared)ことに怒ってるんじゃなくて、 型が合わない(conflicting types)から怒ってるんですね。 >static KOUZOU3 *func();の行だけエラーです。 さすがにデータ構造がたまたま同じでも、別の構造体だとダメか(笑)
[この投稿を含むスレッドを表示] [この投稿を削除]
[1716] Re:構造体へのポインタを返す関数
投稿者:ti
2011/03/21 18:16:48

tiです。 >矛盾しない関数宣言は何回行ってもよい(正しいプログラムである)ので、 >この2行があってもエラーにはならない。 >過去に書かれたソースコードとの互換性を維持するために、このコードに対して >警告は出ないのが大多数のコンパイラの挙動だろうね。 ># 俺的には、出たらびっくり。 どうもそうみたいでびっくりです(VC++ 2008で確認)。 実例です。 typedef struct { int val; } KOUZOU1,KOUZOU2; typedef struct { int val; } KOUZOU3; static KOUZOU1 *func(); static KOUZOU2 *func(); static KOUZOU3 *func(); int main(void) { return 0; } static KOUZOU3 *func();の行だけエラーです。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1715] Re:構造体へのポインタを返す関数
投稿者:774RR
2011/03/21 15:56:46

512 氏の [1706] 発言に一言付け加えると理解しやすくなるだろう。 > 構造体ではなくintへのポインタであればエラーは出ません。 「両方を」を追加。 すると main.c 中には static int *func(); // File1.h 由来 static int *func(); // File2.h 由来 と書かれた2行が入ることになる。 矛盾しない関数宣言は何回行ってもよい(正しいプログラムである)ので、 この2行があってもエラーにはならない。 過去に書かれたソースコードとの互換性を維持するために、このコードに対して 警告は出ないのが大多数のコンパイラの挙動だろうね。 # 俺的には、出たらびっくり。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1714] Re:構造体へのポインタを返す関数
投稿者:mano
2011/03/21 12:17:05

[1706]の > 構造体ではなくintへのポインタであればエラーは出ません。 は、どう?エラーじゃなくても警告ぐらい出す気がするんだけど、 こちら、Cが詳しくないのでよく分りません。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1713] Re:構造体へのポインタを返す関数
投稿者:(ぱ)こと管理人
2011/03/21 08:01:42

既に回答がついていますが、File1.hとFile2.hの static KOUZOU1 *func(); static KOUZOU2 *func(); このふたつのプロトタイプ宣言が、main.cの中で両方とも見えてしまって いるのが原因です。 「static KOUZOU1 *func()」という関数を、File1.cの中だけで使うのであれば、 .hファイルではなくFile1.cの中で宣言すればOKです。 関数定義自体を、それを呼び出すところよりも先に書けるのであればそれでも OKです(私はこちらの書き方の方が好みです)。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1712] Re:構造体へのポインタを返す関数
投稿者:774RR
2011/03/21 07:07:27

正解っす。あえてフォローするなら ti 氏が解説を省略したところを。 C のプリプロセッサは単純置換を行う機能であるため、 #include "hogehoge.h" は この行が hogehoge.h の中身に置き換わるだけ である、ということを意識すると理解が早い。 main.c は File1.h と File2.h を両方取り込む 取り込んだ結果のファイルを翻訳単位という main.c 中に static KOUZOU1* func(); と static KOUZOU2* func(); が両方入る 同一関数名が違う機能であると宣言されているのでエラー。 gcc -E hoge.c とか gcc -E -C hoge.c とかしてみると参考になるかもしれない。 次のステップへのヒントを 複数人開発をするようになると、重要度は .h ファイル> .c ファイル になる。 .h ファイルには「他人に使ってもらうため」の宣言・コメントを書く .c ファイルにはその実装を書く だから、 ・他人に使ってもらいたくない static 変数や関数は .h に書かずに .c に書く ・inline 展開される前提の短い static 関数は .h に書くことがある
[この投稿を含むスレッドを表示] [この投稿を削除]
[1711] Re:構造体へのポインタを返す関数
投稿者:ti
2011/03/21 02:28:24

tiと申します。 自分の勉強のため回答させていただきますので、 前橋さん間違いや補足のご指摘お願いします。 main.cの中で >#include "File1.h" >#include "File2.h" となっていて、 >static KOUZOU1 *func(); >static KOUZOU2 *func(); /* ここがエラーになります:error: conflicting types for 'func' */ で 展開後にfunc識別子が重複していることが原因です。 どちらかと関係するところを変えればコンパイルできるはずです。 なお、static宣言はmain.cなど、公開したくないソースに対してのヘッダファイルに 含めるべきでないと思います。 公開用(extern宣言の関数)と非公開用(ある関数だけで使うstatic宣言の関数)で ヘッダファイルは分けるべきです。 また、ヘッダファイルは二重展開防止機能をつけておきましょう。 #ifndef FILE1_H #define FILE1_H ~ #endif 以上 >お返事遅れました。 >(ぱ)さん、yuyaさん、ありがとうございます。 >(ぱ)さんの回答にひもづけて続けます。 > >下記がサンプルコードとエラーメッセージです。 >(環境はMac OSXのXcode3.2.4です。) > >main.c >-------------------------------------------------- >#include "File1.h" >#include "File2.h" > >int main (int argc, const char * argv[]) { > > file1(); > file2(); > > return 0; >} >-------------------------------------------------- > >File1.h >-------------------------------------------------- >typedef struct KOUZOU1_tag { > int var; >} KOUZOU1; > >void file1(); >static KOUZOU1 *func(); >-------------------------------------------------- > >File1.c >-------------------------------------------------- >#include <stdio.h> >#include <stdlib.h> >#include "File1.h" > >void file1() { > KOUZOU1 *kouzou; > kouzou = func(); > > printf("%d\n", kouzou->var); >} > >static KOUZOU1 *func() { > KOUZOU1 *kouzou; > > kouzou = malloc(sizeof(KOUZOU1)); > kouzou->var = 1; > > return kouzou; >} >-------------------------------------------------- > >File2.h >-------------------------------------------------- >typedef struct KOUZOU2_tag { > int var; >} KOUZOU2; > >void file2(); >static KOUZOU2 *func(); /* ここがエラーになります:error: conflicting types for 'func' */ >-------------------------------------------------- > >File2.c >-------------------------------------------------- >#include <stdio.h> >#include <stdlib.h> >#include "File2.h" > >void file2() { > KOUZOU2 *kouzou; > kouzou = func(); > > printf("%d\n", kouzou->var); >} > >static KOUZOU2 *func() { > KOUZOU2 *kouzou; > > kouzou = malloc(sizeof(KOUZOU2)); > kouzou->var = 2; > > return kouzou; >} >-------------------------------------------------- > >エラーは >error: conflicting types for 'func' >というもので >File2.h の static KOUZOU2 *func(); >の所に出ます。 > >ちなみに >error: previous declaration of 'func' was here >というメッセージも同時に出て、開くと >File1.h の static KOUZOU1 *func(); >を指していました。 > >やっぱりstaticを付けているのに >「funcさっき宣言したじゃん!」と言われているようなんですが、 >いかがでしょうか。 > >うーん、どこを勘違いしてるんだろうか。。。 > >よろしくお願いします! > >P.S. >「記憶クラス指定子」「型修飾子」の違いなど、 >自分で間違えて指摘されるとスムーズに理解しやすいですね。 >本で読むときはつい飛ばしてしまいがちですが(笑
[この投稿を含むスレッドを表示] [この投稿を削除]