int (*fp)(int);
と記述すると
intを1つ引数にもち、intを返す関数のポインタ変数fpを
宣言することになりますが、
typedef int (*fp)(int);
と記述すると、
intを1つ引数にもち、intを返す関数のポインタ型名「fp」
を作成することになります。
このtypedefの使用例は構文例外なのでしょうか。
決り文句だと考えていればいいとは思うのですが。
また解説書などでは、
typedef int int_array3[3];
などの記述についても説明されているものをあまり見かけません。
K&Rではtypedefは記憶クラス指定子扱いであるとは
書いてありますが、ここらへんを詳しく説明している
文献などありましたら教えていただけないでしょうか。
<2003112217333...@os.rim.or.jp>の記事において
ing...@os.rim.or.jpさんは書きました。
ingram> typedef int (*fp)(int);
ingram> と記述すると、
ingram> intを1つ引数にもち、intを返す関数のポインタ型名「fp」
ingram> を作成することになります。
ingram>
ingram> このtypedefの使用例は構文例外なのでしょうか。
ingram> 決り文句だと考えていればいいとは思うのですが。
どのような構文に対する例外だとお考えでしょうか?
--
名古屋大学大学院 情報科学研究科 計算機数理科学専攻
小野 孝男
In article <2003112217333...@os.rim.or.jp>
Hyu-ga Hiroya <ing...@os.rim.or.jp> writes:
> こんにちは。お世話になります。
> int (*fp)(int);
> と記述すると
> intを1つ引数にもち、intを返す関数のポインタ変数fpを
> 宣言することになりますが、
> typedef int (*fp)(int);
> と記述すると、
> intを1つ引数にもち、intを返す関数のポインタ型名「fp」
> を作成することになります。
はい。C言語を使っている人でも、なかなか気が付かない所ですよ
ね。ひゅうがさんの記事をみて、ああそういうことだったのかと思っ
た人もたくさんあると思います。私も typedef の話をする時には、
「まず変数宣言をするふりをして、頭に typedef を付けると型に
なる」と説明しています。
> このtypedefの使用例は構文例外なのでしょうか。
例外とは、鋭いですね。C言語の当初の設計には、typedef がなく
て、困っていて、後から泥縄式に付け足したという歴史があります。
> K&Rではtypedefは記憶クラス指定子扱いであるとは
> 書いてありますが、ここらへんを詳しく説明している
> 文献などありましたら教えていただけないでしょうか。
typedef は、明らかに記憶クラス指定子(staticとかautoとか
registerとか)ではないです。ただ、コンパイラを作る時の都合上、
何か入れないといけないと思ってどうしようかと考えて、「構文上
は記憶クラス指定子入れてしまえ」と入れちゃったんだと思います。
一種の手抜きとも言えますが、他に入れる余地も少なかったのでしょう。
> また解説書などでは、
> typedef int int_array3[3];
> などの記述についても説明されているものをあまり見かけません。
ホント、分かりにくい所です。つまり、解説書を書く人も実はよく
わかっていないか、説明すると面倒なので書いていないとか、そう
いう事情はあると思います。
\\ 新城 靖 (しんじょう やすし) \\
\\ 筑波大学 電子・情報 \\
In article <YAS.03No...@kirk.is.tsukuba.ac.jp>, y...@is.tsukuba.ac.jp (Yasushi Shinjo) writes
> 「まず変数宣言をするふりをして、頭に typedef を付けると型に
> なる」と説明しています。
cast よりはtypedefが分かりやすいですよね。
typedef int function();
の方が、
(int ())function
よりは自然なわけだから...
> 例外とは、鋭いですね。C言語の当初の設計には、typedef がなく
> て、困っていて、後から泥縄式に付け足したという歴史があります。
たしかに、ちょっとださいかも。ポインタが無ければいらないとい
う気もするし。実際、Java とかには無くても困らないわけだし。
---
Shinji KONO @ Information Engineering, University of the Ryukyus,
河野真治 @ 琉球大学工学部情報工学科,
On Mon, 24 Nov 2003 13:47:44 +0900
ta...@hirata.nuee.nagoya-u.ac.jp (Takao Ono) wrote:
> どのような構文に対する例外だとお考えでしょうか?
普通のポインタの宣言構文に対して、
関数へのポインタの宣言構文が例外のように感じました。
(例外というよりは別物であるという認識なのでしょうか。)
僕が最初に <2003112217333...@os.rim.or.jp> の記事で書いた
typedefはあまり関係なかったかもしれません。
そもそもなんでこのような疑問を抱いたかというと
あるプログラムの中で
typedef int (*fp)(int);
のような、関数へのポインタの型名を作成してる部分を見て、
このようなtypedefの使い方を知らなかったため調べ始めた中で出てきたもの
なのですが、、、
typedefの使い方としては、
typedef 型名 新型名;
というのがまず基本にあると思います。
この程度ならシンプルでわかりやすいのですが、
# というかこれだけの機能であれば#defineで事足りてしまいますが
上の、「型名」と同義語である「新型名」を作成するという認識があったとき
typedef int (*fp)(int);
のような記述で混乱を生じてしまいました。
本当は最初、
typedef int (*fp)(int);
という記述は
# intを1つ引数にもちintを返す関数へのポインタ型名fpを作成するという
イメージで、
typedef int(*)(int) fp;
とか、
typedef int *int_ptr;
のような記述にならうなら、
typedef int()(int) *fp;
見たいな感じで記述したい気持ちでした。(ちょっと無理やりですが。)
いろいろ考えたのですが、次のように考えると理解も苦しくないのではないか
と勝手に結論付けてしまいました。(ぜんぜん間違っているかもしれませんが。)
関数へのポインタは、
普通のポインタと同じような「変数」的なイメージではなく、
関数のスタート位置(アドレス)の変更が可能な関数プロトタイプ宣言の
イメージである。
# スタート位置が変更可能であるため、関数定義も存在しない。
# また左辺値になり得る
普通の関数プロトタイプ宣言と
スタート位置(アドレス)変更可能な関数のプロトタイプ宣言
の記述の違いは、関数名の前に「*」をつけるかつけないかで
区別する。(演算子優先順位の関係で“()”が必要)
このプロトタイプ宣言の前にtypedefをつけると、、、?
ここでよくわからなくなってしまいました。
いろいろ調べたのですが、
typedef int F(int);
なんてのも構文的には正しいようです。しかもこれは関数定義扱いになるようです。
もうすでに別の話になってしまいますが、
具体的な、このtypedefの用例なんかも知りたいのですが、
文献等あれば教えていただけませんでしょうか。
In article <2003112421444...@os.rim.or.jp>, Hyu-ga Hiroya <ing...@os.rim.or.jp> writes
> 普通のポインタと同じような「変数」的なイメージではなく、
> 関数のスタート位置(アドレス)の変更が可能な関数プロトタイプ宣言の
> イメージである。
つうか~
int a;
int *f();
char *c[];
とかが、a とかf とか c という名前のの実体と型を定義しているわけですけど、
それに、typedef をつけると...
typedef int a;
typedef int *f();
typedef char *array[];
見た目そのまんま、対応する名前に型が結びつけられるわけですよね。
なんとなく、
> typedefの使い方としては、
> typedef 型名 新型名;
> というのがまず基本にあると思います。
って考えたくなるけど、違うんだよね。
typedef 定義式
ですよね。コンパイラの実装を考えると、
typedef が出て来たら、mode を tyedef にして、
そのまま、普通に構文解析して、
identifier が来たら、変数とかを定義するかわりに、
今の型を、その名前に定義する
ってわけで、簡単なんですよね。実際、同じ構文解析プログラム使えるし。
ストレージクラスじゃないとかいってた人もいるけど、実装的には、
ストレージクラスと同じ部分に実装します。
だから、
typedef struct hoge {...} hoge,*hogeptr;
とかの並列のtypedefも、
typedef 型 hoge,*hogeptr;
ってよりは、
struct hoge {...} hoge,*hogeptr;
と同じ名前の定義式なんだよな。
> typedef int()(int) *fp;
> 見たいな感じで記述したい気持ちでした。(ちょっと無理やりですが。)
この C のidentifier抜きの型名ってすごくわかりずらいですよね。
なので、それを解決するために typedef を導入したのだと思います。
(int (*)())(*)()
なんて、何がなんだかわからんものも書けるし...
typedef int (*fnptr)();
typedef fnptr (*fnptr_fn)();
だったら、まだ、なんか許せるっつうか...
> 具体的な、このtypedefの用例なんかも知りたいのですが、
> 文献等あれば教えていただけませんでしょうか。
やっぱり規格書なんじゃないかなぁ。 僕は、K&R の後は、
いろんなソースを読んで勉強しました。
In article <3989208...@insigna.ie.u-ryukyu.ac.jp>
ko...@ie.u-ryukyu.ac.jp (Shinji KONO) writes:
> cast よりはtypedefが分かりやすいですよね。
> typedef int function();
> の方が、
> (int ())function
> よりは自然なわけだから...
関数のポインタが入ってしまうと、typedef は必須でしょう。
typedef なしでは、人の知恵を越えてしまいます。
> > 例外とは、鋭いですね。C言語の当初の設計には、typedef がなく
> > て、困っていて、後から泥縄式に付け足したという歴史があります。
> たしかに、ちょっとださいかも。ポインタが無ければいらないとい
> う気もするし。実際、Java とかには無くても困らないわけだし。
構造体で、s.x と s->x の違いが書けなくなるんだけど、困る人は
今や小数派という話はあるんでしょう。
In article <3989209...@insigna.ie.u-ryukyu.ac.jp>
ko...@ie.u-ryukyu.ac.jp (Shinji KONO) writes:
> つうか~
> int a;
> int *f();
> char *c[];
> とかが、a とかf とか c という名前のの実体と型を定義しているわけですけど、
Cの難しい所は、名前にたいして修飾語が前にも後にも置けるとい
うことです。前に置くのは、「*」。後ろに置くのは、「[]」とか
「()」。Pascal は後ろにしか置けないから紛れはないけれど、C
は前にも後ろにもおけて、どちらの結合が強いかかわらなくなるわ
けです。たとえば、下の3つのうち、2つは同じなんですけど、ど
れだかわかりますか。
int *f();
int (*f)();
int *(f());
関数の括弧と結合どの括弧が混じっているので、さらにめちゃめちゃ
なんだけど。次の方がまだ優しいかな。
char *c[];
char (*c)[];
char *(c[]);
> (int (*)())(*)()
> なんて、何がなんだかわからんものも書けるし...
> typedef int (*fnptr)();
> typedef fnptr (*fnptr_fn)();
> だったら、まだ、なんか許せるっつうか...
1個だけなら、まだ、一度変宣言したフリをして、変数の名前を消
せばいいけど、2個になると駄目です。たとえば、int (*fnptr)()
に全体に括弧をつけて、名前を消すと(int (*)())となります。
> > 具体的な、このtypedefの用例なんかも知りたいのですが、
> > 文献等あれば教えていただけませんでしょうか。
>
> やっぱり規格書なんじゃないかなぁ。 僕は、K&R の後は、
> いろんなソースを読んで勉強しました。
Cの規格書って、まともなものはあるんですか。規格書が長い間な
くて、「K&Rの例題が全部通る」みたいな記述もありました。
Unix の Portable C compiler のフロントエンドをそのまま使えば
その点では互換性の問題はなくてそんなに困らなかったという話も
あるし。
<YAS.03No...@kirk.is.tsukuba.ac.jp>の記事において
y...@is.tsukuba.ac.jpさんは書きました。
yas> Cの規格書って、まともなものはあるんですか。
10年以上も前に ISO で規格になってるんですけど, これが「まとも」じゃ
ないとすると何をもって「まとも」と呼べばいいのか....
--
名古屋大学大学院 情報科学研究科 計算機数理科学専攻
小野 孝男
P.S.
日本なら JIS を使うのがベストだと思うのだが, なぜか「ANSI C」と書
かれる.
しょうもない事を聞くようですが、上のは、
(int (*)())(*)()
と
typedef int (*fnptr)();
typedef fnptr (*fnptr_fn)();
は、同じことである。と言った内容なのでしょうか。それとも、両者は何の関係もな
く、単なるtypedefの用法を説明したものなのでしょうか。
--
******************************
keizi kounoike
******************************
こーのさんがいいたいのは
(int (*)())(*)()
というキャストを使うよりは
typedef int (*fnptr)();
typedef fnptr (*fnptr_fn)();
と宣言しておいて
(fnptr_fn)
を使うのであれば「まだ、なんか許せる」ということで
しょう。この2つのキャストは同じです。
(int (*)())(*)()
によってキャストされる先の型は
[1] intを返す関数へのポインタ → (int (*)())
[2] …[1]…を返す関数へのポインタ → (…[1]…)(*)()
になります。typedefを使うことで、(1)と書くところで
はかわりにfnptrと書け、(2)と書くところではfnptr_fn
と書けるわけですね。
--
太田純(Junn Ohta) (株)リコー/新横浜事業所
oh...@sdg.mdd.ricoh.co.jp
On 25 Nov 2003 16:54:29 GMT
y...@is.tsukuba.ac.jp (Yasushi Shinjo) wrote:
> たとえば、下の3つのうち、2つは同じなんですけど、ど
> れだかわかりますか。
>
> int *f();
> int (*f)();
> int *(f());
intへのポインタを返す関数fのプロトタイプ宣言
int *f();
int *(f());
intを返す関数へのポインタfの宣言
int (*f)();
でしょうか。
さんざん関数ポインタのことを調べた直後だったのでこっちはすんなり
わかりましたが、下のほうがわかりませんでした。
> 関数の括弧と結合どの括弧が混じっているので、さらにめちゃめちゃ
> なんだけど。次の方がまだ優しいかな。
>
> char *c[];
> char (*c)[];
> char *(c[]);
調べたところ、
charへのポインタを5つ格納できる配列cの宣言
char *c[5];
char *(c[5]);
charを5つ格納できる配列への、ポインタcの宣言
char (*c)[5];
でしょうか。
char (*c)[5];
char c5[5] = {'a', 'b', 'c', 'd', '\0'};
char c6[6] = {'1', '2', '3', '4', '5', '\0'};
c = &c5;
は正しいですが、
c = &c6;
はできませんね。
c = c5;
もできないですね。
配列の先頭要素のアドレスと、
配列そのもののアドレス(?)は違うものみたいですね。
いまいちよくわからないです。
多次元配列(配列の配列...?)と関係あるような気がしますが。
やはりそういう意味でしたか。fnptr_fnの場合は理解できるのですが、もう一方の
(int (*)())(*)()の方の使い方がどうもよく分かりません。私のやり方がまずい(理
解できていない)のだと思いますが、どうしてもコンパイラーが受け付けてくれませ
ん。どういう風に使用するのか教えて頂ければ幸いです。
こんなふうでいいじゃん...
int (*(*ptr1)())();
char *(*ptr2)();
ptr1 = ((int (*)())(*)())ptr2;
とか思ったらだめですね。あれ? (^^;
勉強しなおしてきます。(^^;
In article <2003112623504...@os.rim.or.jp>
Hyu-ga Hiroya <ing...@os.rim.or.jp> writes:
> > たとえば、下の3つのうち、2つは同じなんですけど、ど
> > れだかわかりますか。
> > int *f();
> > int (*f)();
> > int *(f());
> intへのポインタを返す関数fのプロトタイプ宣言
> int *f();
> int *(f());
>
> intを返す関数へのポインタfの宣言
> int (*f)();
> でしょうか。
はい。結局、f という名前に、* と () の2つが左右から付いた時
* には、右の () の方が結合度が強いということです。
> > 関数の括弧と結合どの括弧が混じっているので、さらにめちゃめちゃ
> > なんだけど。次の方がまだ優しいかな。
> > char *c[];
> > char (*c)[];
> > char *(c[]);
これも同じですよ。結合度という意味では。f という名前に、* と
[] の2つが左右から付いた時 * には、右の [] の方が結合度が強
いということです。
> char (*c)[5];
> char c5[5] = {'a', 'b', 'c', 'd', '\0'};
> char c6[6] = {'1', '2', '3', '4', '5', '\0'};
> c = &c5;
> は正しいですが、
> c = &c6;
> はできませんね。
> c = c5;
> もできないですね。
この場合、配列の要素数まで見てエラーチェックするんでしたっけ?
あと、cdecl というプログラムがあって、英語で説明してくれます。
------------------------------------------------------------
% ./cdecl
Type `help' or `?' for help
cdecl> explain char *c[];
declare c as array of pointer to char
cdecl> explain char (*c)[];
declare c as pointer to array of char
cdecl> explain char *(c[]);
declare c as array of pointer to char
cdecl>
cdecl> explain int *f();
declare f as function returning pointer to int
cdecl> explain int (*f)();
declare f as pointer to function returning int
cdecl> explain int *(f());
declare f as function returning pointer to int
cdecl>
------------------------------------------------------------
cdecl も、日本語(逆ポーランド)ものもがあると、結合に関して
はもっと分かりやすいと思うんだけど、どこかにないですかね。
char (*c)[5]とc5やc6では型が違うので、c = c5やc = &c6がうまく行かないのは当
然のような気がします。
単純なケースとして
char * cc;
char c5[5] = {'a', 'b', 'c', 'd', '\0'};
とした場合、
cc = &c5; //間違い
cc = &c5[0]; //正しい
cc = c5; //正しい
になると思います。&c5[0]とc5は、同じ意味ですが、&c5は違う型を指すからです・
・かな。
上の例の
char (*c)[5];
char c5[5] = {'a', 'b', 'c', 'd', '\0'};
場合で、
c = &c5;
とした時、cを用いて、c5[]の中身の 'a', 'b', 'c', 'd'を1つずつ表示さすには、
どうすればよいか考えればその違いが分かるようになのではと思いますが。
In article <bq71cp$nhf$1...@news511.nifty.com>
<koun...@mbh.nifty.com> writes:
> 単純なケースとして
> char * cc;
> char c5[5] = {'a', 'b', 'c', 'd', '\0'};
> とした場合、
> cc = &c5; //間違い
> cc = &c5[0]; //正しい
> cc = c5; //正しい
> になると思います。&c5[0]とc5は、同じ意味ですが、&c5は違う型を指すからです・
> ・かな。
どうかなあ。
私の感覚だと、「配列の名前だけ書いたら先頭要素のポインタにな
る」という仕様は、仕様のバグだと思います。歴史的に、配列のよ
うに(普通は)大きいメモリのコピーを禁止したかったというのは、
わかります。たとえば、関数呼出しの時に
------------------------------------------------------------
char c5[5];
f( c5 );
------------------------------------------------------------
と書くと、配列全体ではなくてその先頭番地(ポインタ)が送られ
ます。しかし、これを本当は、
f( &c5 );
と書くようにした方がよかったと思います。それで、f( c5 ) を意
味として禁止すると。
構造体も最初は代入もできなければ、引数で渡すのもできなかった
ですよね。配列も構造体と同じ扱いにすれば良かったと思います。
> 上の例の
> char (*c)[5];
> char c5[5] = {'a', 'b', 'c', 'd', '\0'};
> 場合で、
> c = &c5;
> とした時、cを用いて、c5[]の中身の 'a', 'b', 'c', 'd'を1つずつ表示さすには、
> どうすればよいか考えればその違いが分かるようになのではと思いますが。
そう言われてもよく分かりません。今のCコンパイラは、c5 も
&c5 も同じ値になります。
------------------------------------------------------------
% gcc -v
Using builtin specs.
gcc version 2.95.2 19991024 (release)
% cat array-c5.c
main()
{
char c5[5];
printf("%d, %d\n",c5,&c5);
}
% gcc array-c5.c
% ./a.out
-4262664, -4262664
%
------------------------------------------------------------
こういう話なのでは?
% gcc -v
Reading specs from /usr/local/GNU/lib/gcc-lib/sparc-sun-solaris2.7/2.95.2/specs
gcc version 2.95.2 19991024 (release)
% cat array-c5.c
main()
{
char c5[5];
printf("%d, %d\n",sizeof(c5),sizeof(&c5));
}
% gcc array-c5.c
% ./a.out
5, 4
%
どうもです。新城さんのように大学で先端的な研究をされている方(これは、以前論
文等をここで紹介されたのをちらっと覗いたのですが、まるきりチンプンカンプンで
したので、こりゃレベルが違うわと思ったからです。)とやり取り出来るほどの知識
も素養も持ち合わせていないので、参考として読ませて頂きます。全くのゴミ記事で
すが、わざわざコメントをもらたので、何もなしもどうかと言うことで。
In article <bq7c4h$kgm$1...@ns.src.ricoh.co.jp>
oh...@src.ricoh.co.jp (Junn Ohta) writes:
> こういう話なのでは?
> main()
> {
> char c5[5];
> printf("%d, %d\n",sizeof(c5),sizeof(&c5));
> }
> % gcc array-c5.c
> % ./a.out
> 5, 4
> %
sizeof() は、話が別でしょう。普通は、sizeof() には型の名前
(struct なんたらも含む)か、変数名を書くんじゃないですか。変
数名でもなく型名でもなく &c5 のような式を書くのは、変な感じ
がします。*ならいいけど。
うちの若いものですが、sizeof() で配列の大きさが分かると思っ
てプログラムを書いたりします。配列をコピーしたい時に、
sizeof() して、malloc() しちゃうんでうよね。
q = malloc( sizeof(*p) );
memcpy( q, p, sizeof(*p) );
これが、p が配列ではなくて、構造体とか単なる int のポインタ
ならいいんだけれど、配列ではダメです。なぜダメか、難しいみた
い。うまい説明の仕方は、ないでしょうか。「sizeof() はコンパ
イラが解釈する」とかいっても、なかなか通じないこともあります。
必要なメモリのバイト数を返す魔法の関数だと思っていたみたい。
配列にも & を付けてポインタにする方法なら、説明が少しは楽だっ
たような気もするけど。
> 私の感覚だと、「配列の名前だけ書いたら先頭要素のポインタにな
> る」という仕様は、仕様のバグだと思います。
そりゃあなたの感覚がバグってるからでしょう。
> char c5[5];
> f( c5 );
> と書くと、配列全体ではなくてその先頭番地(ポインタ)が送られ
> ます。しかし、これを本当は、
> f( &c5 );
> と書くようにした方がよかったと思います。それで、f( c5 ) を意
> 味として禁止すると。
んじゃ
char c; c = c5[0];
は禁止で
char c; c = (&c5)[0];
ですか~~~
神田敏広 <ca...@kgc.co.jp>
件のtypedefと同じ型になるのが
> int (*(*ptr1)())();
なので、
> ptr1 = ((int (*)())(*)())ptr2;
じゃ、キャストする型が違ってますよね。(^^;
# ptr1 = (int (*(*)())())ptr2;
(int (*)())(*)() だと、(*)() を int (*)() で
キャストしようとしているのでわ?
いいえ。c5 と書くと「配列全体」を意味するようにしようという
意味です。別に関数呼出しで配列全体を渡すことを許可してもいい
けれど。
Pascal というプログラミング言語では、そうなっています。配列
の長さが違うと型が違うといって通らないという規格のプログラミ
ング言語。さすがにそれはないでしょうということで、配列の長さ
が違うのはOKということにしようというコンパイラも、結構あり
ました。実用的なプログラムでは必須の機能です。規格違反なんだ
けどね。
> char c; c = c5[0];
> は禁止で
これは、もちろん OK にします。
> char c; c = (&c5)[0];
> ですか~~~
これは、構文エラーにします。c5 で配列全体で、&c5 で、配列全
体の番地。配列全体の番地に [] を付けるのは意味がないとします。
それで、
char c; c = (*(&c5))[0];
これは、ok にします。*&で打ち消されるのは、普通ですよね。
c5 が配列だから、変に思うかもしれないけれど、構造体なら別に
普通でしょ。
------------------------------------------------------------
struct
{
char c0, c1, c2, c3, c4;
} s5;
char c; c = s5.c0; // ok.
char c; c = (&s5).c0; // syntax error.
char c; c = (*(&s5)).c0;// ok.
------------------------------------------------------------
この構造体の考え方を配列にするとこうなります。
------------------------------------------------------------
char c5[5];
char c; c = c5[0]; // ok.
char c; c = (&c5)[0]; // syntax error.
char c; c = (*(&c5))[0];// ok.
------------------------------------------------------------
C言語で、構造体の代入ができたり、関数の引数で渡せるようになっ
たのは、いつ頃でしたっけ? その時、配列も同じようにすべきだっ
たんだと思います。
Yasushi Shinjo wrote:
> いいえ。c5 と書くと「配列全体」を意味するようにしようという
> 意味です。別に関数呼出しで配列全体を渡すことを許可してもいい
> けれど。
c5はあくまでも、配列を表す識別子です。
ただ、ほとんどの構文で暗黙のうちに先頭要素へのポインタに変換されるということです。
sizeofと&が例外。他にも例外はあるかな。
だから、&c5[0]とc5はまったく同じわけではありません。
関数名も似たようなもんですかね。
--
┏━━━━━┓ /\
┃ (^^)/ ┃ /\/ \
□――――――□ / / \/\
□=[W]==□ 熊岡 忍(Kumaoka Shinobu)
== IIII == tito...@jcom.home.ne.jp
■━━●━━■ / / \
■ ■
/ ̄\/\_/ ̄\/\/ ̄\/ ̄\/ ̄ ̄\/\/
Followup-To: fj.net.words です。
2003年11月28日(金)20時07分発信の
<YAS.03No...@kirk.is.tsukuba.ac.jp>すなわち
"Array and pointer" と題した記事より
> 私の感覚だと、「配列の名前だけ書いたら先頭要素のポインタにな
> る」という仕様は、仕様のバグだと思います。歴史的に、配列のよ
> うに(普通は)大きいメモリのコピーを禁止したかったというのは、
> わかります。たとえば、関数呼出しの時に
仕様の不具合は「バグ」ではなく、敢えて英語で表現するならば
「エラー」だと思うんですが、どうでしょうか。
========================================================================
「クリスマスとハロウィンは正確に一致するんですよ」 -- A.Asimov --
--
中川 恒雄 ( T.Nakagawa ) mailto:yae...@kikansha.jp
http://www.kikansha.jp/~yaemon/
> > char c; c = c5[0];
> > は禁止で
> > char c; c = (&c5)[0];
> > ですか~~~
>
> いいえ。c5 と書くと「配列全体」を意味するようにしようという
> 意味です。
c5 と書くと「配列全体」。てことは
c5 + 1 と書くと、「配列全体 + 1」ですか??
どういう意味です?????
c5 の各要素に 1 を足す?
# それはそれで確かに有用かも
# c5 * 2 もできるとなお良いかも
# c5 * c5 もできるともっと良いかも
神田敏広 <ca...@kgc.co.jp>
In article <3FC7EEC6...@jcom.home.ne.jp>
Shinobu Kumaoka <tito...@jcom.home.ne.jp> writes:
> 熊岡です。
> c5はあくまでも、配列を表す識別子です。
> ただ、ほとんどの構文で暗黙のうちに先頭要素へのポインタに変換されるということです。
その「暗黙」をやめて、明示的に「&」を書こうという話をしてい
るわけです。
> sizeofと&が例外。他にも例外はあるかな。
「ほとんど」と「例外」をどう考えるか。
> 関数名も似たようなもんですかね。
そうですね。関数へのポインタも、* 付けた時と付けない時で同じ
動きをしたりして、分かりにくいです。
------------------------------------------------------------
int (*f)();
(*f)( 10 ); // ok.
f( 10 ); // ok. 上と同じ。
------------------------------------------------------------
In article <s7fad6f...@xxx.kgc.co.jp>
ca...@xxx.kgc.co.jp writes:
> c5 と書くと「配列全体」。てことは
> c5 + 1 と書くと、「配列全体 + 1」ですか??
いいえ。
> どういう意味です?????
普通の言語では、そんなの意味はないんじゃないのかなあ。だいた
い配列の個々の要素に「+」というオペレータが効くという保証は
ないし。構造体に「+」演算子は効かないでしょ。そんな機能を構
文レベルでコンパイラに入れる気はしません。
C++で自分で定義する人はいるかもね。class なら許してもいい
けど、そんな class はあんまり使いたいとは思わないだろうなあ。
いくらC++でも、syntax error になるのは無理なんでしたっけ?
> c5 の各要素に 1 を足す?
> # それはそれで確かに有用かも
APL とか、そういう意味だっかかも。いくらでも変な言語はあるか
ら。配列とスカラの演算をC言語標準にして有用とは思えません。
In article <bqa97m$mes$1...@newsserv.ics.es.osaka-u.ac.jp>
SAITOH akinori <sai...@ist.osaka-u.ac.jp> writes:
> 大阪大学の齊藤です
> 高級アセンブラ派からいわせると、 = で、配列全体が代入
> できるのは改悪(複雑/CPUを喰うことは、ソースコードでみても
> 行数が多くなる、という原則が曲げられてるから)(笑)。
禁止してもいいけど、構文レベルというよりは、意味レベルで禁止
したらいいんじゃないかという話をしているわけです。
Linux の fork のコードで、プロセス構造体を *new = *old のよ
うにコピーしているんですけど、最初見落としちゃって。
> Cを高級言語として捉えたい人は古今後を絶たず、
CPU も速くなったから、それに合わせて考え方を変えてはどうか、
という話です。
In article <bqaava$n...@utogw.gssm.otsuka.tsukuba.ac.jp>
ku...@gssm.otsuka.tsukuba.ac.jp writes:
> 久野です。
> でもstructだと=で代入できる、というのと整合性が… 久野
内部に配列を含む構造体が、= で代入できるのに、構造体単独なら
代入できないんですよ。今は。
\\ 新城 靖 (しんじょう やすし) \\
\\ 筑波大学 電子・情報 \\
----------------------------------------------------------------------
From sai...@ist.osaka-u.ac.jp Sat, 29 Nov 2003 23:07:21 +0900
From: SAITOH akinori <sai...@ist.osaka-u.ac.jp>
Newsgroups: fj.net.words
Subject: Re: 仕様のバグ
Date: Sat, 29 Nov 2003 23:07:21 +0900
Message-ID: <bqa97m$mes$1...@newsserv.ics.es.osaka-u.ac.jp>
大阪大学の齊藤です
T.P.S.Nakagawa wrote:
> パイプ喫いの中川と申します。
>> 私の感覚だと、「配列の名前だけ書いたら先頭要素のポインタにな
>> る」という仕様は、仕様のバグだと思います。歴史的に、配列のよ
>> うに(普通は)大きいメモリのコピーを禁止したかったというのは、
>> わかります。たとえば、関数呼出しの時に
> 仕様の不具合は「バグ」ではなく、敢えて英語で表現するならば
> 「エラー」だと思うんですが、どうでしょうか。
ポリシーの違いでしょう。
Cを高級言語として捉えたい人は古今後を絶たず、
そういう観点からはバグですが、Cは高級アセンブラだと
考えるならば当然の仕様です。所詮変数名はロケーション
に対するラベルの変種なのだから。
高級アセンブラ派からいわせると、 = で、配列全体が代入
できるのは改悪(複雑/CPUを喰うことは、ソースコードでみても
行数が多くなる、という原則が曲げられてるから)(笑)。
From ku...@gssm.otsuka.tsukuba.ac.jp 29 Nov 2003 14:37:30 GMT
From: ku...@gssm.otsuka.tsukuba.ac.jp
Newsgroups: fj.net.words
Subject: Re: 仕様のバグ
Date: 29 Nov 2003 14:37:30 GMT
Message-ID: <bqaava$n...@utogw.gssm.otsuka.tsukuba.ac.jp>
久野です。
sai...@ist.osaka-u.ac.jpさん:
> 高級アセンブラ派からいわせると、 = で、配列全体が代入
> できるのは改悪(複雑/CPUを喰うことは、ソースコードでみても
> 行数が多くなる、という原則が曲げられてるから)(笑)。
でもstructだと=で代入できる、というのと整合性が… 久野
> > c5 と書くと「配列全体」。てことは
> > c5 + 1 と書くと、「配列全体 + 1」ですか??
>
> いいえ。
>
> > どういう意味です?????
>
> 普通の言語では、そんなの意味はないんじゃないのかなあ。
c5 + 1 に意味が無い
てことは
*(c5 + 1)
にも
c5[1]
にも意味が無いのですね。
神田敏広 <ca...@kgc.co.jp>
> > c5 + 1 と書くと、「配列全体 + 1」ですか??
> > c5 の各要素に 1 を足す?
> > # それはそれで確かに有用かも
>
> APL とか、そういう意味だっかかも。いくらでも変な言語はあるか
> ら。
全然関係ありませんが、
LabVIEW ではそういう意味になります。
c5 * 2 はスカラ倍。
c5 op c5 は各要素同士の op (四則演算)。
LabVIEW が「変な言語」というのは、
まあ、その通りです。
神田敏広 <ca...@kgc.co.jp>
んん。この推論過程は、理解していません。
c5[1] は、意味があります。配列なんだから。配列の1番目の要素。
*(c5 + 1) は、意味がないでしょう。
*(&c5[0]+1) は、意味があるとしても。
*(c5[0]+1) は、int に * を付けたのでエラー。
「c5[1]と*(c5 + 1)は、未来永劫、どんなコンテキストでもまった
く同じ意味である」という話は、ないわけです。
In article <s7f7k1j...@xxx.kgc.co.jp>
ca...@xxx.kgc.co.jp writes:
> > > c5 + 1 と書くと、「配列全体 + 1」ですか??
> 全然関係ありませんが、
> LabVIEW ではそういう意味になります。
配列に演算子「+」が効かないものが入っていたら、どうなります
か? 構造体とか。
> c5 * 2 はスカラ倍。
> c5 op c5 は各要素同士の op (四則演算)。
配列と配列の演算は、まあ普通だと思います。スカラ倍は、2 * c5
のように書く流儀が好きなんだけど。どっちもあるみたいね。日本
の高校では、2 * c5 なんだけど。
納得です。河野さんが挙げられた例は、typedefを使用する理由の1つには可読性を
よくするためのものであるが、無条件にtypedefを使用すると無用な混乱を起こすと
いうよい例を示されたということですね。
typedef int (*fnptr)();
typedef fnptr (*fnptr_fn)();
から、fnptr_fnの型は、一見すると (int (*)())(*)()ように見えてしまうが、実際
はint (*(*)())()である。だから上の場合は、ふたつのtypedefを使用するのではな
く最初から
typedef int (*(*fnptr_fn)())();
とすべきではないかと。
最初(int (*)())(*)()を見たときに、なんか違和感を感じてましたが、はっきりとお
かしいと言えるほど確信がありませんでした。
char c[5];
char* pc1;
とあった場合、
pc1 = c;
がだめ、という論理であるのはわかるのですが、
pc1 = &c;
もだめということになるんですよね?
これを認めると、
*pc1 は*(&c) すなわち cそのもの(配列全体)ということになるので。
とすると、配列を扱うためのポインタは常に
char (*pc2)[5];
のように宣言しなければならないということになるのですよね?
同様に、pc1[0]も言語仕様上あってはいけない、という論理展開に
なるのですよね?
なんだかすごく不便になるだけなんじゃないかという気がしますが。
--
General Shadow <gene...@mail.goo.ne.jp>
General Shadow wrote:
> とすると、配列を扱うためのポインタは常に
> char (*pc2)[5];
> のように宣言しなければならないということになるのですよね?
そうじゃなくて、
pc1=&c[0];
と、明示的に書きましょうということでは。
> なんだかすごく不便になるだけなんじゃないかという気がしますが。
不便というより、少々面倒になる程度ですね。
その代わり、配列のコピーや演算が違和感無く記述できるのがメリットでしょうか。
歴史のifを検証するのは面白いですが、当時の事情も理解できますし、
今の仕様がそれほどダメなものだとも私は思いません。
In article <20031130120626...@mail.goo.ne.jp>
General Shadow <gene...@mail.goo.ne.jp> writes:
> char c[5];
> char* pc1;
> pc1 = &c;
> もだめということになるんですよね?
はい。そのままではダメ。キャストかければ許してもいいんじゃな
いかな。
pc1 = (char *)&c ;
配列全体の番地は、配列の先頭の要素の番地と値としては同じとい
うことで。
> とすると、配列を扱うためのポインタは常に
> char (*pc2)[5];
> のように宣言しなければならないということになるのですよね?
配列へのポインタと配列の要素へのポインタは別物です。
構造体へのポインタと構造体の要素へのポインタが別物のように。
「配列を扱うためのポインタ」って、どちらですか?
構造体の場合は、こんな感じ。
struct
{
char c0,c1,c2,c3,c4;
} s5 ;
pc1 = (char *)&s5 ;
pc1 = &s5.c0 ;
> 同様に、pc1[0]も言語仕様上あってはいけない、という論理展開に
> なるのですよね?
いいえ。それは ok でいいんじゃないですか。
int i;
c = *(pc1+i); // ok.
c = pc1[i]; // ok. 上と同じ意味。
つまり、ポインタ変数があった時に、[]というオペレータをどうい
う意味にするかという話です。今のCと同じで上のような意味でい
いんじゃないですか。
In article <3FC984FF...@jcom.home.ne.jp>
Shinobu Kumaoka <tito...@jcom.home.ne.jp> writes:
> pc1=&c[0];
> と、明示的に書きましょうということでは。
はい。それも ok。上に書いたようにキャストも ok。
> 歴史のifを検証するのは面白いですが、当時の事情も理解できますし、
> 今の仕様がそれほどダメなものだとも私は思いません。
私は未来の話が好きです。
main() の引数の argv ですが、いろいろな宣言の仕方があります。
char **argv ;
char *argv[] ;
char argv[][] ;
あ、最後のもの駄目なんだけど。これがきちんと説明できれば、大
したものだと思います。誰か説明してみませんか?
> 納得です。河野さんが挙げられた例は、typedefを使用する理由の1つには可読性を
> よくするためのものであるが、無条件にtypedefを使用すると無用な混乱を起こすと
> いうよい例を示されたということですね。
それは逆でしょう。
> typedef int (*fnptr)();
> typedef fnptr (*fnptr_fn)();
typedefを使ってこう書くと判りやすく
> typedef int (*(*fnptr_fn)())();
使わずにこう書くとわかりにくい……と。
で、(int (*)())(*)() は単に錯覚の産物なのでしょう。
ポロ,ドタ。(注,目から鱗が落ちた音とずっこけた音)
確かにそうですね。間違っていました。
>
> > typedef int (*fnptr)();
> > typedef fnptr (*fnptr_fn)();
>
> typedefを使ってこう書くと判りやすく
>
> > typedef int (*(*fnptr_fn)())();
>
> 使わずにこう書くとわかりにくい……と。
そうですね。最初から,fnptr_fnの方はどんな型かイメージできたのですが,(int
(*)())(*)()との関係で,頭の中が逆転してなんか分けのわからん文章になってし
まったようです。
> で、(int (*)())(*)() は単に錯覚の産物なのでしょう。
ですね。ところで,(int (*)())(*)()って表記は意味的には通じそうですが,文法的
にはエラーですよね。(また,間違ったことを言っているかも知れませんが。)
> > int (*(*ptr1)())();
から、ptr1 を消せば、int (*(*)())(); となるわけなので、間違
えようがないわけなんですが... そういう仕組みだと気付くまでは、
さっぱりこんでした。「*より()の方が結合が強いので()がいる」
とか言われるわけだけど。
In article <bqfftt$f3n$1...@news511.nifty.com>, <koun...@mbh.nifty.com> writes
> ですね。ところで,(int (*)())(*)()って表記は意味的には通じそうですが,文法的
> にはエラーですよね。(また,間違ったことを言っているかも知れませんが。)
どこに識別子をおけるかってことなんですけどね...
% gcc -c tmptmp.c
tmptmp.c:1: error: parse error before "int"
とかいうなぁ。before なのかよ。
int j;
int (*(*k)())();
k = (int (*(*)())()) j;
は、通るんだけど、
int (*(*)())() j;
は通らないですね。良く分からん。キャストと型名は違うものみたい。
---
Shinji KONO @ Information Engineering, University of the Ryukyus,
河野真治 @ 琉球大学工学部情報工学科,
K&RのP136にも,”二次元配列を関数に渡す場合は,関数の引数宣言には列の
数がなければならない。”とはっきり書かれているので,最後のchar argv[][] は駄
目と私も上を見たとき疑問も無く納得してましたが,OKのようですね。ということ
は,char argv[][] は二次元配列のように見えるが,単なるポインタのポインタのと
いうことで,配列ではないということになるのかな。これまた錯覚の一種?。
<bqgtse$bas$1...@news511.nifty.com>の記事において
koun...@mbh.nifty.comさんは書きました。
kounoike> K&RのP136にも,”二次元配列を関数に渡す場合は,
kounoike> 関数の引数宣言には列の数がなければならない。”とはっき
kounoike> り書かれているので,最後のchar argv[][] は駄目と私も上
kounoike> を見たとき疑問も無く納得してましたが,OKのようですね。
え? OK ってどういう意味ですか?
Java じゃないので, ちゃんとエラーになるはずですが.
# C# でもいいんだっけ?
main の宣言として ISO C で規定されているのは
int main(void)
と
int main(int argc, char **argv)
の 2つだけです (char **env を追加できる処理系もあり). ここで
char **argv と char *argv[] が実は互換なので
int main(int argc, char *argv[])
とも書けるというだけの話ですよね.
# で, char *argv[] と char argv[][] は (コンパイルエラーになる点
# を差し引いても) 互換じゃない.
kounoike> ということは,char argv[][] は二次元配列のように見える
kounoike> が,単なるポインタのポインタのということで,配列ではな
kounoike> いということになるのかな。これまた錯覚の一種?。
宣言において, 「配列」と「ポインタ」は別ものです. これが原則.
但し, 関数の仮引数リストにおいては (配列を関数に渡すときにポイン
タに変換されるため) 配列の形式で書いてもポインタとして解釈されま
す.
--
名古屋大学大学院 情報科学研究科 計算機数理科学専攻
小野 孝男
うっ。またしても,変なことを書いてしまったのか。まあ,お陰で色々勉強になりこ
ちらは有り難いのですが,周りには迷惑ことと思いますが。
で,OKと書いたのは,単に
int hoge(char argv[][]);
がコンパイルできたので単純にOKではと思った次第ですが,違うのかな。
gcc version 3.1.1を使用しました。
> Java じゃないので, ちゃんとエラーになるはずですが.
> # C# でもいいんだっけ?
>
> main の宣言として ISO C で規定されているのは
上の話は、mainに限った話ではないですよね。単に、関数の引数にchar argv[][]と
言った類の表記がOKかどうかの話と思ってたのですが、なんか勘違いしているのか
な。
しまった.
<bqhkot$m9r$1...@news511.nifty.com>の記事において
koun...@mbh.nifty.comさんは書きました。
kounoike> で,OKと書いたのは,単に
kounoike> int hoge(char argv[][]);
kounoike> がコンパイルできたので単純にOKではと思った次第ですが,違うのかな。
「ISO C」が変わったんだった.
C89 (前のバージョン) ではアウトですが, C99 (新しいバージョン) で
はできるようになった... のかな? 規格を読んでもよくわからん.
T [] は imcomplete type だから, それを使った T [][] はアウトのよ
うな気がするんだが.... どうも使わないとエラーにならないようだ.
下に示す例では gcc でもエラーになるんだけど....
kounoike> > main の宣言として ISO C で規定されているのは
kounoike> 上の話は、mainに限った話ではないですよね。単に、関数の引数にchar argv[][]と
kounoike> 言った類の表記がOKかどうかの話と思ってたのですが、なんか勘違いしているのか
kounoike> な。
C99 になって, gcc の拡張機能がかなり取り込まれたんですよね....
# でも機能が大きくなりすぎたので, gcc でも完全には対応できてない.
例:
-------------------- ここから --------------------
#include <stdio.h>
int main(int argc, char argv[][])
{
for (int i = 0; argv[i] != 0; i++) {
printf("argv[%d] = `%s'.\n", i, argv[i]);
}
return 0;
確かに、argvを関数の中で変化させるものはダメですね。
まあ、当然のような気がします。小野さんが前の記事の中で,” char *argv[] と
char argv[][] は (コンパイルエラーになる点を差し引いても) 互換じゃない.”と
言われているし、当然、ポインタのポインタが2次元配列と同じなるわけでもない
し。
しかし、配列の不完全タイプの引数でもその引数を変化させなければ不思議とエラー
にならないのが、また不思議な気がします。例えば、次のやつはエラーなく動きまし
た。
(混乱しているので、この問題の例として適切なのかどうかよく分かりません)
#include <stdio.h>
int main()
{
char p[][10]={"abc","def"};
char (*pp)[10];
char * hoge(char[][]);
pp = p;
pp++;
printf("p[0][10] = %s\n", hoge(p));
printf("p[1][10] = %s\n", hoge(pp));
return 0;
}
char * hoge(char s[][])
{
return *s;
う~ん, なんかちょっと見えてきたような....
<bqhut3$d2b$1...@news511.nifty.com>の記事において
koun...@mbh.nifty.comさんは書きました。
kounoike> char * hoge(char s[][])
kounoike> {
kounoike> return *s;
kounoike> }
ここ,
return *s;
を
return s[0];
にするとエラーになります. どうも単項の * だけ特別視しているもよう.
# 頭痛が....
独り言:
引数にchar a[][]みたいなものが使用できても,aの値を変更できなければなんのメ
リットもなく,なんのためにあるのかなぁと今も疑問に思っていますが,次のような
使い方もできるのかなと。ただ,醜いですが。
#include <stdio.h>
int main()
{
char p[][10]={"abc", "def"};
char pp[][5]={"ABC", "DEF", "HIJ"};
int i;
char * hoge(char[][], int, int);
for(i = 0; i < 2; i++)
printf("p[%d][10] = %s\n", i, hoge(p, i, 10));
for(i = 0; i < 3; i++)
printf("pp[%d][5] = %s\n", i, hoge(pp, i, 5));
return 0;
}
char* hoge(char s[][], int m, int n)
{
char (*pp)[n];
pp = s;
pp = pp + m;
return (char *)pp;