例として次のような行列計算をする関数を考えます。
void
mult_m33xv3(double a[3][3],
double b[3],
double c[3])
{
int i;
for(i = 0; i < 3; i++){
c[i] = (a[i][0] * b[0] +
a[i][1] * b[1] +
a[i][2] * b[2]);
}
return;
}
ここで、引数aおよびbは関数内で変更されないので、明示的に宣言を const
double におきかえます。すると、
呼ぶ側の関数で、a および b にデータをセットして呼びますので、呼ぶ側の
関数内では当然a およびb にあたる変数は const ではありません。なぜかコ
ンパイルおよびlint 時に a についてのみ const のあるなしの warning が
出ます。
これはなぜなのでしょうか。また、このwarningを制止するためにはどのよう
な(キャストをして呼ぶなどの)方法があるのでしょうか。
-------
吉見隆
電子技術総合研究所知能システム部
mailto: yos...@etl.go.jp
**** 2001年1月から組織名、メールアドレスが変更になります。
呼ぶほうで値をセットするかどうかは関係ないでしょう。
> なぜかコ
> ンパイルおよびlint 時に a についてのみ const のあるなしの warning が
> 出ます。
>
> これはなぜなのでしょうか。また、このwarningを制止するためにはどのよう
> な(キャストをして呼ぶなどの)方法があるのでしょうか。
単に「const」を書く位置を間違っただけでは?
(ポインターなどではありがち)
ヘ_ヘ ____________________________
ミ・・ ミ vo...@merope.pleiades.or.jp
( ° )~ 日下部陽一
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
恐らく const double a[3][3] の引数宣言の部分で、aの扱いが
配列からポインタへの扱いとみなされているせいのような気が
します。
const double a[3][3] → const double **a
のように構文解析されているのではないかと。仮にそうだとす
ると、const double **aは
[const double型へのポインタ]へのポインタになります。
引数として渡しているのは
[double型へのポインタ]へのポインタです。
修飾型へのポインタへのポインタと、無修飾型へのポインタへ
のポインタは別物として扱われると私は認識しているのですが
もし間違いがあればどなたかご指摘ください。
で、「じゃあどうすれば解決するの?」といわれると、const
をやめましょうというみもふたもないことになるのですが。
> これはなぜなのでしょうか。また、このwarningを制止するためにはどのよう
> な(キャストをして呼ぶなどの)方法があるのでしょうか。
仮引数を
void
mult_m33xv3(const double a[][3],
const double b[3],
double c[3])
と書いたとき、なぜaだけwarningが出るかは私も知りませんが、とりあえず呼
ぶ側で (const double (*)[3]) にキャストしてやればwarningはおさまるみた
いです。
前田敦司
Kenichiro Ueda wrote: in <39E69D09...@tsjpn.co.jp>
・
・
> 修飾型へのポインタへのポインタと、無修飾型へのポインタへ
> のポインタは別物として扱われると私は認識しているのですが
> もし間違いがあればどなたかご指摘ください。
ちょっと惜しかったですね。
関数の仮引数中の配列がポインタに置き換えられるのは、トップレベル
だけです。
ですから、「double a[M][N]」(「doubleの要素をN個持つ配列」を、M個
要素として持つ配列 a)が仮引数中で宣言されると、「double (*a)[N]」
(「doubleの要素をN個持つ配列」を指すポインタ)に置き換えられます。
# M, N は当然、 適当な定数に置き換えられるマクロです。
# 以下、M = N = 3 として、記事を続けます。
これに「const」が付くと、「constなdoubleの要素を3個持つ配列」を指す
ポインタになります。
一方、実引数の方は、「doubleの要素を3個持つ配列」を指すポインタに
なっていますので、ご指摘通り型の不一致が発生する訳です。
「double b[3]」の方にwarningが出ない理由は、私にも良く分かりません。
現象的には、
・ポイントする対象がスカラーなら、仮引数側にconstがあって実引数
側にconstがなくても、無視する。
・ポイントする対象が配列なら、配列の型が完全に一致していないと
warningの対象とする。
となっているようです。
この辺が、規格に照らして妥当な実装なのかどうかは、私の手に負える
レベルでありませんので、識者の方にお任せします。
> で、「じゃあどうすれば解決するの?」といわれると、const
> をやめましょうというみもふたもないことになるのですが。
別にフォローされている、前田さんの記事にある通り
(const double (*)[3]) a
とキャスティングすればOKです。
# 註)(const double *[3]) a では駄目です。これでは、「constなdouble
# を指すポインタを3個要素に持つ配列」へのキャスティングになって
# しまいます。
--
関@神奈川
Masao Seki <ma-...@gb3.so-net.ne.jp>
Masao Seki wrote:
> 関数の仮引数中の配列がポインタに置き換えられるのは、トップレベル
> だけです。
ありがとうございます。ご説明で理解できました。
> 「double b[3]」の方にwarningが出ない理由は、私にも良く分かりません。
これは正しいです。
const double b[3] → const double *b という形になるはず
ですので、char *ポインタをconst char *の引数を持つ関数に
渡してもwarningが出ないことでわかると思います。
Masao Seki wrote in message <39E716AE...@gb3.so-net.ne.jp>...
>関@神奈川です。
...
>ですから、「double a[M][N]」(「doubleの要素をN個持つ配列」を、M個
>要素として持つ配列 a)が仮引数中で宣言されると、「double (*a)[N]」
>(「doubleの要素をN個持つ配列」を指すポインタ)に置き換えられます。
># M, N は当然、 適当な定数に置き換えられるマクロです。
># 以下、M = N = 3 として、記事を続けます。
>
>これに「const」が付くと、「constなdoubleの要素を3個持つ配列」を指す
>ポインタになります。
>
>一方、実引数の方は、「doubleの要素を3個持つ配列」を指すポインタに
>なっていますので、ご指摘通り型の不一致が発生する訳です。
>
>「double b[3]」の方にwarningが出ない理由は、私にも良く分かりません。
>現象的には、
> ・ポイントする対象がスカラーなら、仮引数側にconstがあって実引数
> 側にconstがなくても、無視する。
> ・ポイントする対象が配列なら、配列の型が完全に一致していないと
> warningの対象とする。
>となっているようです。
ええと、この手の話は、少し前にも出ていたような気がするのですが、
まず、引数の受け渡しの際の制約は、代入の時の制約と同じなので、
代入で考えると、例えば、
int * を、const int * に代入するのは可能でも、
int ** を、const int ** に代入することは出来ません。
X3010の6.3.16.1には、
・両オペランドが適合する型の修飾版または非修飾版へのポインタであり、
かつ左オペランドで指される型が右オペランドで指される型の修飾子を
すべてもつ。
と定められています。
const int * ← int *
の場合、「左オペランドで指される型」は「const な int 」で、
「右オペランドで指される型」は「int」ですから、左オペランドで指される型は
右オペランドで指される型の修飾子をすべてもっていることになり、
これは制約に適合します。
const int ** ← int **
の方は、
「左オペランドで指される型」は、「(const な int) へのポインタ」
であり、
「右オペランドで指される型」は、「intへのポインタ」
です。
そして、「左オペランドで指される型」も「右オペランドで指される型」も、
最終的な型(型分類)は「ポインタ」です。ここの const int **のconstは、
intの方にかかっているのであって、「ポインタ」にかかっているのでは
ないので、上記の規則には当てはまりません。規格では、「かつ左オ
ペランドで指される型が右オペランドで指される型の修飾子をすべて
もつ」とされていますが、ここのconstは、「左オペランドで指される型」
を修飾しているのではなく、「左オペランドで指される型が指す型」を
修飾しているからです。
今回の配列の話もそれと同じで、
const double (*)[3] ← double (*)[3]
という代入の場合、
「左オペランドで指される型」は、「(const な double) の配列(要素数3)」
であり、
「右オペランドで指される型」は、「doubleの配列(要素数3)」
ですから、やはり一致しません。
この辺のことは、「エキスパートCプログラミング」のp.48~に書いてあります。
------------------------------------------------------------
前橋 和弥 maeb...@cse.co.jp
http://member.nifty.ne.jp/maebashi/
------------------------------------------------------------
Kazuya Maebashi wrote: in <8s9tgb$7k$1...@nw042.infoweb.ne.jp>
・
・
> int * を、const int * に代入するのは可能でも、
>
> int ** を、const int ** に代入することは出来ません。
れれ? VC++5.0だと警告レベルを4(最高)にしても、何も言いません。
gccだと、確かに「not compatible type」の警告が出ます。
(逆に、VC++が文句を言っていた、自動配列のアドレスを使った初期化
については、何も言いません。)
ポイントする先が、配列かスカラーかで分かれる訳ではないのですね。
>
> X3010の6.3.16.1には、
>
> ・両オペランドが適合する型の修飾版または非修飾版へのポインタであり、
> かつ左オペランドで指される型が右オペランドで指される型の修飾子を
> すべてもつ。
>
> と定められています。
> const int * ← int *
>
> の場合、「左オペランドで指される型」は「const な int 」で、
> 「右オペランドで指される型」は「int」ですから、左オペランドで指される型は
> 右オペランドで指される型の修飾子をすべてもっていることになり、
> これは制約に適合します。
ポインタの時を規定する規格が、単純変数の時とは別に有る訳ですか。
ポインタの時は、ポインタが指す実体の型を比較して良く、実体の
最終的な型を直接修飾する「const」が、受け取り側にのみあっても、
文法上許容されると云う訳ですね。
# 意味不明な単語を持ち出してすみません。ポインタ(や配列)でない、
# intやdoubleの変数と云う意味です。
# 以下で、「TYPE」は適当な型表現の文字列に置き換えられるマクロです。
# typedefされた型では、ありません。
私は、
「const TYPE x」に「TYPE a」を渡して良い
と云う規則をポインタに適用して言える事は、
「TYPE *const x」に「TYPE *a」を渡して良い
だけで、
「const TYPE *x」に「TYPE *a」を渡して良い
は、出てこない。
「const TYPE (*x)[3]」に「TYPE (*a)[3]」を渡しては駄目
なら、
「const TYPE *x」に「TYPE *a」を渡しては駄目
としないと一貫しない、と考えてしまいました。
きちんとした規格書を身近に置いて、常に参照できるようにして、
このような無駄な考察をしないで済むようにしないなといけないな
と感じました。
# 何を、当たり前の事を言ってるのだと言われそう。(^_^;;
> そして、「左オペランドで指される型」も「右オペランドで指される型」も、
> 最終的な型(型分類)は「ポインタ」です。ここの const int **のconstは、
> intの方にかかっているのであって、「ポインタ」にかかっているのでは
> ないので、上記の規則には当てはまりません。規格では、「かつ左オ
> ペランドで指される型が右オペランドで指される型の修飾子をすべて
> もつ」とされていますが、ここのconstは、「左オペランドで指される型」
> を修飾しているのではなく、「左オペランドで指される型が指す型」を
> 修飾しているからです。
配列かスカラーかは関係なく、あくまでも型が一致しているかどうかだけで
判断すれば良いと。 スッキリしました。
ご教授、有り難うございました。
> この辺のことは、「エキスパートCプログラミング」のp.48~に書いてあります。
随分昔に会社に買わせて、斜め読みした事はあるのですが、当時の(今も)
知識レベルが低かった所為か、印象に残っていません。これを機会に自腹を
切って購入して、自宅に置いておこうと思います。
K & R (第二版)も。
Masao Seki wrote in message <39E96E03...@gb3.so-net.ne.jp>...
>
>関@神奈川です。
>
>Kazuya Maebashi wrote: in <8s9tgb$7k$1...@nw042.infoweb.ne.jp>
> ・
> ・
>> int * を、const int * に代入するのは可能でも、
>>
>> int ** を、const int ** に代入することは出来ません。
>
>れれ? VC++5.0だと警告レベルを4(最高)にしても、何も言いません。
ええっ? そりゃ変なのでは...
>> X3010の6.3.16.1には、
>>
>> ・両オペランドが適合する型の修飾版または非修飾版へのポインタであり、
>> かつ左オペランドで指される型が右オペランドで指される型の修飾子を
>> すべてもつ。
>>
>> と定められています。
...
>ポインタの時を規定する規格が、単純変数の時とは別に有る訳ですか。
はい。5つある制約のうちひとつだけを引用したので、わかりにくかったかも
しれません。
代入に関する制約は、6.3.16.1に書いてあるものをすべて挙げると、
・左オペランドの型が修飾または非修飾の算術型であり、かつ右オペランド
の型が算術型である。
・左オペランドの型が右オペランドの型に適合する構造体型又は共用体型の
修飾版または非修飾版である。
・両オペランドが適合する型の修飾版または非修飾版へのポインタであり、
かつ左オペランドで指される型が右オペランドで指される型の修飾子を
すべてもつ。
・一方のオペランドがオブジェクト型又は不完全型へのポインタであり、かつ
他方がvoidの修飾版又は非修飾版へのポインタである。更に、左オペラン
ドで指される型が、右オペランドで指される型の修飾子をすべてもつ。
・左オペランドがポインタであり、かつ右オペランドが空ポインタ定数である。
となっています(もちろん、どれかひとつが成立していればOKです)。
3つめ以降が、ポインタに関する制約ですね。
> れれ? VC++5.0だと警告レベルを4(最高)にしても、何も言いません。
>
> gccだと、確かに「not compatible type」の警告が出ます。
g++ だと、 -Wall にしても何も言いませんね。
調べていないので推測に過ぎないのですが、
C と C++ とで違うのではないでしょうか。
少し違う状況で実験をしてみました。
---- "a.c" ここから ----
int ** a;
const int ** b;
void f(void) {
a = b;
}
void g(void) {
b = a;
}
---- "a.c" ここまで ----
% gcc -c a.c
a.c: In function `f':
a.c:5: warning: assignment from incompatible pointer type
a.c: In function `g':
a.c:9: warning: assignment from incompatible pointer type
% g++ -c a.c
a.c: In function `void f()':
a.c:5: warning: assignment to `int **' from `const int **' discards const
a.c: In function `void g()':
a.c:9: warning: assignment to `const int **' from `int **' adds cv-quals
without intervening `const'
というように、 g++ では単に const の有無の違いとして
処理されているようです。
# cv-quals? なにそれ (^^;;
--
齊藤高明
SAITO Takaaki wrote: in <39EA7193...@sonicteam.com>
・
| > gccだと、確かに「not compatible type」の警告が出ます。
|
| g++ だと、 -Wall にしても何も言いませんね。
| 調べていないので推測に過ぎないのですが、
| C と C++ とで違うのではないでしょうか。
|
| 少し違う状況で実験をしてみました。
すみません。「g++ だと、 -Wall にしても何も言いませんね。」と、
「少し違う状況で実験をしてみました。」の関係が良く理解できません。
以下の"a.c"は、「少し違う状況」の方ですね。「何も言いません」の
方が、どのようなソースだったのか、教えて頂けますか。
| % gcc -c a.c
| a.c: In function `f':
| a.c:5: warning: assignment from incompatible pointer type
| a.c: In function `g':
| a.c:9: warning: assignment from incompatible pointer type
| % g++ -c a.c
| a.c: In function `void f()':
| a.c:5: warning: assignment to `int **' from `const int **' discards const
| a.c: In function `void g()':
| a.c:9: warning: assignment to `const int **' from `int **' adds cv-quals
| without intervening `const'
|
| というように、 g++ では単に const の有無の違いとして
| 処理されているようです。
C++の「const」は、「named constant」の作成に使ったり、Cよりも広い機能を
持っているようですから、Warningの出方が変わっても、それ程驚きません。
Cの場合の素っ気ないメッセージに比べて、C++の方が、具体的にどのような
不具合が発生しているのか、教えてくれているように読めます。
|
| # cv-quals? なにそれ (^^;;
私が今見ている参考書*)によると、「const」、「volatile」を指して一般に
使われているアクセス修飾子(access modifier)と言う呼び名の公式名は
「cv-qualifiers」だそうですので、これの省略形であろうと思われます。
*) 「標準講座C++」P.202、ハーバート・シルト著、柏原 正三 訳/監修
翔泳社、ISBN4-88135-705-0 ¥6,800-
>int * を、const int * に代入するのは可能でも、
>int ** を、const int ** に代入することは出来ません。
int * const * はどういう意味でしょう?
ほかにも
int const **
int **const
なんかがありますね
#前橋さんに対する反論(質問)ではありません。:-)
@@@ -----------------------------------------------
口口っ (株) シーエーシー 金融システム第2事業部
ん おおさき たかゆき ohs...@cac.co.jp
ohsaki takayuki wrote:
> int * const * はどういう意味でしょう?
[intへのポインタ]へのconstポインタ
> int const **
[intへのconstポインタ]へのポインタ
> int **const
こ、これは・・・?
修行が足りないようで。
<39EC34BA...@tsjpn.co.jp>の記事において
ue...@tsjpn.co.jpさんは書きました。
ueken> ohsaki takayuki wrote:
ueken> > int * const * はどういう意味でしょう?
ueken> [intへのポインタ]へのconstポインタ
[int への const なポインタ] へのポインタ
つまり, int * const *p; ってのがあると:
p = xxxx: OK
*p = xxxx: NG
**p = xxxx: OK
# p, *p, **p の型はそれぞれ int * const *, int * const, int で, *p
# は定数 (const で修飾されている) なので代入不可.
ueken> > int const **
ueken> [intへのconstポインタ]へのポインタ
[const な int へのポインタ] へのポインタ
同じく int const **p; ってのがあると:
p = xxxx: OK
*p = xxxx: OK
**p = xxxx: NG
# p, *p, **p の型はそれぞれ int const **, int const *, int const.
ueken> > int **const
ueken> こ、これは・・・?
ueken> 修行が足りないようで。
[int へのポインタ] への const なポインタ
同様に
p = xxxx: NG
*p = xxxx: OK
**p = xxxx: OK
# p, *p, **p の型はそれぞれ int **const, int *, int.
いかがでしょ.
--
名古屋大学 工学部 電子工学科 平田研究室
小野 孝男
> > int * const * はどういう意味でしょう?
>
> [intへのポインタ]へのconstポインタ
はずれ。
[int] <- [int * const] <- [int * const *]
^この変数がconst
> > int const **
>
> [intへのconstポインタ]へのポインタ
はずれ。
[int const] <- [int const *] <- [int const **]
^この変数がconst
> > int **const
>
> こ、これは・・・?
[int] <- [int *] <- [int **const]
^この変数がconst
(等幅フォント限定...)
前田敦司
Masao Seki wrote in news:39EB309C...@gb3.so-net.ne.jp :
> すみません。「g++ だと、 -Wall にしても何も言いませんね。」と、
> 「少し違う状況で実験をしてみました。」の関係が良く理解できません。
すみません。これは私が、
Masao Seki wrote in news:39E96E03...@gb3.so-net.ne.jp :
> れれ? VC++5.0だと警告レベルを4(最高)にしても、何も言いません。
この文脈を読み違えていたためです。
const double a[3][3] という仮引数に double[3][3] 型の値を
渡す場合のことと思ってしまいました。
失礼致しました。
> 以下の"a.c"は、「少し違う状況」の方ですね。「何も言いません」の
> 方が、どのようなソースだったのか、教えて頂けますか。
私の誤解だったというのは、上記の通りですが、
一応、ソースと実行結果を記しておきます。
---- ここから ----
% cat hoge.c
void
mult_m33xv3(const double a[3][3],
const double b[3],
double c[3])
{
int i;
for(i = 0; i < 3; i++){
c[i] = (a[i][0] * b[0] +
a[i][1] * b[1] +
a[i][2] * b[2]);
}
return;
}
void f( void ) {
double a[3][3];
double b[3];
double c[3];
mult_m33xv3(a, b, c);
}
% gcc --version
egcs-2.91.66
% gcc -Wall -c hoge.c
hoge.c: In function `f':
hoge.c:19: warning: passing arg 1 of `mult_m33xv3' from incompatible
pointer type
% g++ --version
egcs-2.91.66
% g++ -Wall -c hoge.c
%
---- ここまで ----
結局、言いたかった事は、 VC++5.0 と gcc とで警告が異なるのは、
言語が違うからではないか、ということです。
--
齊藤高明
constの修飾ルールが今いちわからないのですが、左にある
ものを修飾するということでしょうか?でもそれだと、
const char *
は説明できないし、constがついた時の「対象物」を確定す
る法則みたいなものを知りたいんですが。
const char *とchar const *はイコール?
奥が深い。
Kenichiro Ueda wrote:
>
> 植田@自宅です。
>
> constの修飾ルールが今いちわからないのですが、左にある
> ものを修飾するということでしょうか?
その通りです。 だからころ、私の記事 <39E96E03...@gb3.so-net.ne.jp>
での
私> 私は、
私> 「const TYPE x」に「TYPE a」を渡して良い
私> と云う規則をポインタに適用して言える事は、
私> 「TYPE *const x」に「TYPE *a」を渡して良い
^^^^^^
なども出てくる訳です。
> でもそれだと、
>
> const char *
>
> は説明できないし、constがついた時の「対象物」を確定す
> る法則みたいなものを知りたいんですが。
>
> const char *とchar const *はイコール?
その通りです。
「const char *」の方が、視覚的な便宜を考慮した例外的記述法と理解
した方が、良いと思います。
constが後置演算子(後置修飾子?)である事を、明示した資料が
今手元にないのですが、K & R (第2版)にも「*const」と云う記述は
出ていたような記憶があります。
In article <39ECC4A9...@gb3.so-net.ne.jp>,
Masao Seki <ma-...@gb3.so-net.ne.jp> writes:
>constが後置演算子(後置修飾子?)である事を、明示した資料が
後置ではなく、前置ですね。
>今手元にないのですが、K & R (第2版)にも「*const」と云う記述は
>出ていたような記憶があります。
int * const p; とかは、よく使いますよ。
--
片山@PFU
p に何かを代入したいときはキャストを使うんですか?
KATAYAMA Yoshio wrote: in <KATE.00Oc...@sims211.trad.pfu.co.jp>
>
> 片山@PFUです。
>
> In article <39ECC4A9...@gb3.so-net.ne.jp>,
> Masao Seki <ma-...@gb3.so-net.ne.jp> writes:
>
> >constが後置演算子(後置修飾子?)である事を、明示した資料が
>
> 後置ではなく、前置ですね。
ん? 「char const」や「int * const」は、「後置」と表現するのでは。
「n++」の「++」は「後置の++」ですよね。
それとも、「const char foo」が標準形だからconstは「前置修飾子」で
あると云う御主張でしょうか。それはそれで、一つの見解だと思います。
私は、「char const」や「int * const」の方を標準形とみなして、
前置で使われる「const char」の方を例外形と思った方が理解しやすく
従って覚えやすいのではと、主張しています。
まあ、3種類しかない使用法で、どれが標準で、どれが例外だと言っても
仕方ない話ですが。
# C++の話になりますが、constメンバ関数の宣言なんて云うのも
# ありますね。これも、後置です。
> >今手元にないのですが、K & R (第2版)にも「*const」と云う記述は
> >出ていたような記憶があります。
>
> int * const p; とかは、よく使いますよ。
これは、わたしの言い方があまりにも省略し過ぎで、意図が伝わらな
かったものと思われます。
言いたかった事は、
K & R (第2版)にも「*const」と云う記述は
出ていたような記憶があります。
だから、その付近に「const」が「後置修飾子」であると明言した
説明がもしかして見つかるかも知れない。
です。
それで私の記憶に残っていた場所は、P.269のポインタの解説の所だった
ようですが、私が期待した「constは後置で使われる」と云った類いの
表現はありませんでした。
何かで、見た記憶があるのですが、見つかりません。どなたか、そんな
記述がある資料、ご存知ないでしょうか。
ohsaki takayuki wrote: in <8sjbt4$l...@infonet.sedept.cac.co.jp>
>
> In article <KATE.00Oc...@sims211.trad.pfu.co.jp>, ka...@pfu.co.jp says...
> >
> >int * const p; とかは、よく使いますよ。
>
> p に何かを代入したいときはキャストを使うんですか?
そうしたくなった時は、プログラムの基本設計を見直しましょう。:-)
初期化や引数として、一度だけ値を与えて、それ以降、値を変更するつもり
がない(変更されては困る)と宣言するのが「const」の目的ですから。
デバッグ目的で一時的に値を変えたいだけなら、const宣言をはずして
しまうのが、手っ取り早い方法でしょうか。
# 作業が一段落したら、すかさず元に戻しておかないと、後で痛い目を
# みるかも。
Masao Seki wrote:
> 私は、「char const」や「int * const」の方を標準形とみなして、
> 前置で使われる「const char」の方を例外形と思った方が理解しやすく
> 従って覚えやすいのではと、主張しています。
えーと、そうではなくて
char const *p;
とした場合、「const は *p を修飾している」という解釈だと思います。
* より前に書いたら *p が const で、後に書いたら p が const だと。
だから char との前後関係は、どうでも良いわけです。
--
竹本 聡 ソニー(株)PNC,PVC,DI部門1部
> > >int * const p; とかは、よく使いますよ。
> >
> > p に何かを代入したいときはキャストを使うんですか?
>
> そうしたくなった時は、プログラムの基本設計を見直しましょう。:-)
いやいや, それならば何故 `;' が付いているのかというツッコミでは.
--
Kazuo Fox Dohzono / doh...@hf.rim.or.jp
[12],(6,9),0,0,2
>> >constが後置演算子(後置修飾子?)である事を、明示した資料が
>>
>> 後置ではなく、前置ですね。
>
>ん? 「char const」や「int * const」は、「後置」と表現するのでは。
>「n++」の「++」は「後置の++」ですよね。
>
>それとも、「const char foo」が標準形だからconstは「前置修飾子」で
>あると云う御主張でしょうか。それはそれで、一つの見解だと思います。
>
>私は、「char const」や「int * const」の方を標準形とみなして、
>前置で使われる「const char」の方を例外形と思った方が理解しやすく
>従って覚えやすいのではと、主張しています。
declarator:
pointer-opt direct-declarator
pointer:
* type-qualifier-list-opt
* type-qualifier-list-opt pointer
で direct-declarator から見れば前置
また
declaration-specifiers:
storage-class-specifier declaration-specifiers-opt
type-specifier declaration-specifiers-opt
type-qualifier declaration-specifiers-opt
function-specifier declaration-specifiers-opt
で前に来ている。 :-)
int * const p;
という場合 const がかかるのは p だと思うのですが?
* const p は int
const p は int へのポインタ
p は const な int へのポインタ
int を修飾すると見れば、後置ということ?
# int * const p; というのは使えませんが :-)
#(値がゴミで書き込みができない)
はい。単なる茶々でした m(_._)m
ちなみに、私は const はうざったいので、 ^^;;
ポインターの形の仮引数にしか使いません。
strcpy(const char *to, ....
なんて場合だけ。
Kenichiro Ueda wrote:
>
> 植田@自宅です。
>
> constの修飾ルールが今いちわからないのですが、左にある
> ものを修飾するということでしょうか?でもそれだと、
>
> const char *
>
> は説明できないし、constがついた時の「対象物」を確定す
> る法則みたいなものを知りたいんですが。
この方のページが参考になるのではないでしょうか。
[1] http://member.nifty.ne.jp/maebashi/programmer/pointer.html
ちなみに
> const char *とchar const *はイコール?
[1]によると
const char *src と char const *src は、全く同じ意味である。
とあります
--
Tomotaka SAKUMA
Systems engineering, Shizuoka univ.
http://spade.sys.eng.shizuoka.ac.jp/
ohsaki takayuki wrote: in <8slnib$l...@infonet.sedept.cac.co.jp>
・
・
> int * const p;
>
> という場合 const がかかるのは p だと思うのですが?
const/volatileは、日本語で書くと「型修飾子」となっている通り、
「型指定子」を修飾するtokenなので、識別子を直接修飾するとする
解釈は、相当無理があると思われます。
かなり信用の置ける文献と思われる「エキスパートCプログラミング」
の p.96にも、
「C constやvolatileキーワードが型指定子(intやlongなど)
の直後に書かれている場合、それは型指定子を修飾する。それ
以外の場合、これらのキーワードはすぐ前の(ポインタを表す)
アスタリスクを修飾する。」
なる解説があります。
> # int * const p; というのは使えませんが :-)
> #(値がゴミで書き込みができない)
これについては別記事に書きましたように、100万年掛かって、やっと
理解できました。(^_^;;
ohsaki takayuki wrote: in <8slo9n$l...@infonet.sedept.cac.co.jp>
>
> In article <8slne8$8b0$1...@netnews.rim.or.jp>, doh...@hf.rim.or.jp says...
> >> > >int * const p; とかは、よく使いますよ。
> >> > p に何かを代入したいときはキャストを使うんですか?
> >>
> >> そうしたくなった時は、プログラムの基本設計を見直しましょう。:-)
> >
> >いやいや, それならば何故 `;' が付いているのかというツッコミでは.
>
> はい。単なる茶々でした m(_._)m
あっはっは。
堂園さんのフォローを読んでも、「何の話?」状態で、「茶々」を目にして、
もう一度片山さんの記事を読み返して、やっと気が付きました。
理解が遅くて、済みません。
const変数の値は、初期化以外にもハードウエアでの設定でも変えられますが、
と云うのは、この際苦し過ぎる弁明ですね。
"Takemoto,Satoshi" wrote: in <39EE5A7F...@cv.sony.co.jp>
・
・
> えーと、そうではなくて
>
> char const *p;
>
> とした場合、「const は *p を修飾している」という解釈だと思います。
> * より前に書いたら *p が const で、後に書いたら p が const だと。
別記事にも書きましたように、constがpを修飾する事があると云う部分に
無理があるので、このような解釈は成立しないと思います。
直接宣言子の「型を指定する」、「型を修飾する」と理解してました。
(型指定子を修飾するで無い点に注意)
#ちなみに私の英語力は最低、模試の最後の3回は 9点7点3点(120点満点)
#です ^^;;;
Masao Seki wrote:
> const/volatileは、日本語で書くと「型修飾子」となっている通り、
> 「型指定子」を修飾するtokenなので、識別子を直接修飾するとする
> 解釈は、相当無理があると思われます。
原語でも型指定子=type-specifier, 型修飾子=type-qualifierですから
「訳語が悪い」という主張も弱いですね。
ただ、役割(意味)を見ると、const char としても
char の性質に何か付加した訳では無く、
「書換できない」というアクセス属性が加わるだけです。
もう一つの型修飾子 volatile も「揮発性である」という記憶の属性を
示しています。
で、これが指定された変数に対して続けて2回リードアクセスすると
異なる値が出てくる可能性が有るよ、とコンパイラ(オプティマイザ)に
注意する目的で設けられていると。
つまり言語上はアクセス属性の意味合いが強い。
だから、「型修飾子」よりは static, extern とかの
記憶クラス指定子(storage-class-specifier)に倣って
「アクセスクラス指定子(access-class-specifier)」とか呼んだ方が
適切な用語だとは思います。
つまり「用語が悪い」と言いたい。
で、やっぱり char const *p ならconstは*pにかかると考える方が、
キーワードの用いられかたと整合するのだと思うわけです。
--
竹本 聡 ソニー(株)PNC,PVC,DI部門1部
>> >いやいや, それならば何故 `;' が付いているのかというツッコミでは.
>> はい。単なる茶々でした m(_._)m
>const変数の値は、初期化以外にもハードウエアでの設定でも変えられますが、
>と云うのは、この際苦し過ぎる弁明ですね。
それならば、volatile も必要でしょう。
「int * const p;」の使い道は、
int * const p;
・・・
int x;
・・・
int * const p = &x;
のようなときですね。もっとも、「int * const p;」には extern を付
けることが多いですけど。
--
片山@PFU
In article <KATE.00Oc...@sims211.trad.pfu.co.jp>
ka...@pfu.co.jp (KATAYAMA Yoshio) writes:
> >const変数の値は、初期化以外にもハードウエアでの設定でも変えられますが、
> >と云うのは、この際苦し過ぎる弁明ですね。
>
> それならば、volatile も必要でしょう。
そういえばそういう例が規格にありました.
extern volatile const int real_time_clock;
> int * const p;
> ・・・
> int x;
> ・・・
> int * const p = &x;
>
> のようなときですね。もっとも、「int * const p;」には extern を付
> けることが多いですけど。
二度目の p の宣言で最初の p が隠れてしまいませんか?
>> int * const p;
>> ・・・
>> int x;
>> ・・・
>> int * const p = &x;
>>
>> のようなときですね。もっとも、「int * const p;」には extern を付
>> けることが多いですけど。
>二度目の p の宣言で最初の p が隠れてしまいませんか?
初めのは tentative definition で、二番目のは definition になりま
す。いずれも同じオブジェクトを示します。(cf. 6.7.2 External
object definitions)
「int * const p;」に extern を付けると declaration になります。
#関数の外での話です
--
片山@PFU
> const/volatileは、日本語で書くと「型修飾子」となっている通り、
> 「型指定子」を修飾するtokenなので、識別子を直接修飾するとする
> 解釈は、相当無理があると思われます。
const/volatileを、*やintと同じオブジェクトの前置修飾と考えるか、型の後
置修飾と考えるかは、文脈自由文法の右からの解釈と左からの解釈に置き換え可
能で、これは同等であることが証明されているので、どちらの解釈も正しいと思
うのですが。
あえていえば、"const char A"がとおるなら前置修飾、とおらないなら後置修
飾ではないでしょうか。とはいえ、オブジェクトの方を変換しない""という型を
考えれば、自分には同じものを示しているようにしか見えません。
KATAYAMA Yoshio wrote: in <KATE.00Oc...@sims211.trad.pfu.co.jp>
>
> In article <39EF00E2...@gb3.so-net.ne.jp>,
> Masao Seki <ma-...@gb3.so-net.ne.jp> writes:
・
・
> >const変数の値は、初期化以外にもハードウエアでの設定でも変えられますが、
> >と云うのは、この際苦し過ぎる弁明ですね。
>
> それならば、volatile も必要でしょう。
はい。 やはり、見逃して貰えませんでしたか。(^_^;;
いや、素早い訂正を、有り難うございました。
>
> 「int * const p;」の使い道は、
>
> int * const p;
> ・・・
> int x;
> ・・・
> int * const p = &x;
>
> のようなときですね。もっとも、「int * const p;」には extern を付
> けることが多いですけど。
こう云う使い方もあるのですね。初めて見ました。
奥が深いです。
この週末は、X3010-1993を眺めたり、簡単なコンパイル実験を
しながら、この件について、下手の考え何とやらをしていました。
"Takemoto,Satoshi" wrote: in <39EFBC10...@cv.sony.co.jp>
・
・
> だから、「型修飾子」よりは static, extern とかの
> 記憶クラス指定子(storage-class-specifier)に倣って
> 「アクセスクラス指定子(access-class-specifier)」とか呼んだ方が
> 適切な用語だとは思います。
>
> つまり「用語が悪い」と言いたい。
用語が適切かどうかは、取敢えず置いて置いておきます。
> で、やっぱり char const *p ならconstは*pにかかると考える方が、
> キーワードの用いられかたと整合するのだと思うわけです。
このような見方も、分からない訳ではありません。宣言の色々な
ヴァリエーションに、前置修飾の解釈ですっきり対応できるなら、
非常に魅力的な解釈法だと思います。
本来、この手の議論は、個人がどう思うかではなく、規格でどう
なっているかが全てで、規格書では多少の分かり難さはあっても、
明確な結論が出せる形で記述されているべきだと思います。
しかし、私の読解力では、X3010から、今回の議論を明確に結論付け
るような記述は見出せませんでした。
仕方がないので、前置修飾で曖昧性なく解釈する為の、解釈規則を
考えてみました。
i) constが、型指定子の直前に置かれたら、型指定子を修飾する
ii-a) constが、型指定子より後方に置かれた場合は、constより
前方の「*」はポインタ宣言子、後方の「*」は間接参照
演算子と看做す
ii-b) constは直後の特定のtokenを修飾するのではなく、const
より後方全体の記述が指し示すオブジェクトを修飾する
ii-c) (補足)これは又、constより前方の記述全体が指し示す
オブジェクトに等しい
iii) 上記に関らず、constを除いた型を考える時は、全ての「*」
は、ポインタ宣言子と看做す
これ位の規則を置かないと、複雑な宣言中に現れたconstを解釈する
時、曖昧になってしまうと思います。
但し、このような見方は、或る程度基本が身についていて、「宣言子」
と「演算子」の意味/機能の違いが理解できている人には、特段の
抵抗もなく受け入れられるでしょうが、ポインタと配列の区別もあ
やしい、入門・初級レベルの人には、どうでしょう。
このような見方よりは、私は「エキスパートCプログラミング」の
解説に従い、著者が冗長として省略したと思われる部分を補足した
次の規則をお勧めしたいと思います。
i) constは、「型指定子」(char, int, etc)、又は「*」(ポイ
ンタ宣言子)を修飾する
ii) constが、「型指定子」の直前、又は直後に置かれた場合は
「型指定子」を修飾する
iii) constが、「*」の直後に置かれた場合は「*」を修飾する
多分、前置修飾の時の解釈規則は、必要以上に複雑で解り難くなって
いるでしょうから、どなたかもっと簡明な規則に修正して頂けないで
しょうか。
その上で、X3010の6.5.4, 6.5.5にある色々な宣言の例 (ex.
int (*fpfi(int (*)(long), int)) (int, ...);
int (*const [])(unsigned int, ...)
等々)の、constを置く事の出来る任意の場所にconstを置いてみて、
どちらの解釈がすんなりと適用できるか比べてみて下さい。
山手さんが述べておられる通り、両者は等価でしょうから、各人が
自分にあった解釈をすれば良いのだと思います。
# 二つの流儀が、同等の勢力を保ち続けるのは、スムーズなコミュ
# ニケーションを実現する上では好ましい事ではないしょう。
# 今回の議論で、constを後置修飾子と看做す見方が、むしろ
# 少数派であるらしい事が分かりましたので、人に説明する時は
# どちらにするか、良く考えて決めたいと思います。
In article <39F3A066...@gb3.so-net.ne.jp>
Masao Seki <ma-...@gb3.so-net.ne.jp> writes:
> >
> > 「int * const p;」の使い道は、
> >
> > int * const p;
> > ・・・
> > int x;
> > ・・・
> > int * const p = &x;
> >
> > のようなときですね。もっとも、「int * const p;」には extern を付
> > けることが多いですけど。
>
> こう云う使い方もあるのですね。初めて見ました。
私も勘違いしてたんですけど,
--- foo.h
typedef int *intp_t;
extern const intp_t p;
--- foo.c
int x;
...
const intp_t p = &x;
と考えると自然に感じませんか (const int と変わりませんよね).
Masao Seki wrote:
> 多分、前置修飾の時の解釈規則は、必要以上に複雑で解り難くなって
> いるでしょうから、どなたかもっと簡明な規則に修正して頂けないで
> しょうか。
充分簡潔な規則が有ります。1文で済みます:
・const より右側のポインタ宣言子、配列宣言、関数宣言について、
それぞれ参照はがし演算子、配列参照演算子、関数呼出演算子と見なし、
式として評価したもの が変更不可である事を指定する。
です。これ以上の事は何も無い筈です。
この規則は型指定子,記憶クラス指定子とも整合します。
const を static に、「変更不可」を「静的」に変えれば
記憶クラス指定子の説明になるし、
const を int に、「変更不可」を「整数型」に変えれば
型指定子の説明にもなる。
型変換も同じ解釈の応用で乗り切れる、と。
という訳で、断然「前置」解釈です。単純で応用が利いて漏れが無い。
> 山手さんが述べておられる通り、両者は等価でしょうから、各人が
> 自分にあった解釈をすれば良いのだと思います。
しかし、わざわざ回り道をする必要は無いと思います。
--
竹本 聡 ソニー(株)PNC,PVC,DI部門1部
> const/volatileを、*やintと同じオブジェクトの前置修飾と考えるか、型の後
>置修飾と考えるかは、文脈自由文法の右からの解釈と左からの解釈に置き換え可
>能で、これは同等であることが証明されているので、どちらの解釈も正しいと思
>うのですが。
規格では型修飾子が前置/後置であるという言い方はしていませんから、
文法を覚えたりする場合の便方でしかありません。どちらが分かり易い
かですね。
後置修飾と考えた場合、… * const … において、ポインターが const
なのか、ポインターが指している先が const なのかを混乱し易いので
はないかと思います。
その代わり、int const a, b; において、b が const であることが分
かり易いですね。
前置と考えた場合は逆になります。型については、
… * const …
↑↑ ↑
││ └ const pointer to TYPE
│└ pointer to TYPE
└ TYPE
#等幅フォントでお読み下さい(_ _)
と単純明解ですが、int const a, b; において b が const であること
を見落とし易いです。
# * と違うところです
> あえていえば、"const char A"がとおるなら前置修飾、とおらないなら後置修
もちろん通りますが、関さんは、
In article <39ECC4A9...@gb3.so-net.ne.jp>,
Masao Seki <ma-...@gb3.so-net.ne.jp> writes:
|「const char *」の方が、視覚的な便宜を考慮した例外的記述法と理解
|した方が、良いと思います。
と例外と考えた方がよいという立場を取られているようです。
--
片山@PFU
<39F4DAA1...@cv.sony.co.jp>の記事において
take...@cv.sony.co.jpさんは書きました。
>> 充分簡潔な規則が有ります。1文で済みます:
>>
>> ・const より右側のポインタ宣言子、配列宣言、関数宣言について、
>> それぞれ参照はがし演算子、配列参照演算子、関数呼出演算子と見なし、
>> 式として評価したもの が変更不可である事を指定する。
>>
>> です。これ以上の事は何も無い筈です。
C の const はわかるような気がしているんですが、C++ のメンバ関数に
つく const:
class Hoge {
// 省略
int hogehoge(int arg) const;
};
はどう解釈したらいいでしょうか?
BView *FindView(const char *name) const;
みたいなのを BeOS の API でよく見かけるんですが、気になっています。
自分で作る部分には、(わかってないので)const をつけないんですが(^^;)。
# 変数には const つけます。
--
川端『メイっぱい』一之
E-mail:k-k...@tdc.minolta.co.jp
"Takemoto,Satoshi" wrote: in <39F4DAA1...@cv.sony.co.jp>
・
> Masao Seki wrote:
>
> > 多分、前置修飾の時の解釈規則は、必要以上に複雑で解り難くなって
> > いるでしょうから、どなたかもっと簡明な規則に修正して頂けないで
> > しょうか。
>
> 充分簡潔な規則が有ります。1文で済みます:
>
> ・const より右側のポインタ宣言子、配列宣言、関数宣言について、
> それぞれ参照はがし演算子、配列参照演算子、関数呼出演算子と見なし、
> 式として評価したもの が変更不可である事を指定する。
>
> です。これ以上の事は何も無い筈です。
私の記事 <39F3A03E...@gb3.so-net.ne.jp>でのi)は、省略しようか
どうか迷ったのですが、代表的でシンプルなケースには、同様にシンプルな
説明をと思って、別項目にしました。
これを別にすると、私の規則から補足的な説明部分を除き機能説明を付け
足したものの様に見えますので、基本的に解釈に関する食い違いはない様
ですね。
> > 山手さんが述べておられる通り、両者は等価でしょうから、各人が
> > 自分にあった解釈をすれば良いのだと思います。
>
> しかし、わざわざ回り道をする必要は無いと思います。
何を「回り道」と感じるかが、個人によって違うと思うので、「各人が
自分にあった解釈をすれば良い」と申し上げております。
私から見れば、直前のtoken(前がない時だけは後ろ)だけをみれば
解釈が確定するのに、何故宣言全体を見なければならないのか。
しかも、宣言の解釈の過程で、式としての評価まで持ち込んで。
constの右側を修飾すると言っても、最終的に解釈が確定するまでには、
constの左側を見ているのではないか。好き好んで回り道する必要ない
のでは、と思われます。
# 前置修飾と解釈した方が、幾つかの意味で自然である事は否定しません。
# その為にペナルティが生じていると思うか、又そのペナルティをどれ位
# 煩わしく感じるかだと思います。
慣れてしまえば、どちらの流儀でも、瞬時に解釈出来てしまうと思います
ので、コミュニケーション上の不具合を考えなければ、どちらかに固執
する意味はないと思います。
KATAYAMA Yoshio wrote: in <KATE.00Oc...@sims211.trad.pfu.co.jp>
>
> In article <39F0BE97...@bg.mbn.or.jp>,
> yam...@bg.mbn.or.jp writes:
>
> > const/volatileを、*やintと同じオブジェクトの前置修飾と考えるか、型の後
> >置修飾と考えるかは、文脈自由文法の右からの解釈と左からの解釈に置き換え可
> >能で、これは同等であることが証明されているので、どちらの解釈も正しいと思
> >うのですが。
>
> 規格では型修飾子が前置/後置であるという言い方はしていませんから、
> 文法を覚えたりする場合の便方でしかありません。どちらが分かり易い
> かですね。
>
> 後置修飾と考えた場合、… * const … において、ポインターが const
> なのか、ポインターが指している先が const なのかを混乱し易いので
> はないかと思います。
これは、単なる慣れの問題だと思います。
私は、constが「*」を修飾していたなら、ポインタそのものがconstと判断し、
それ以外の可能性は、考えもしません。
> > あえていえば、"const char A"がとおるなら前置修飾、とおらないなら後置修
>
> もちろん通りますが、関さんは、
>
> In article <39ECC4A9...@gb3.so-net.ne.jp>,
> Masao Seki <ma-...@gb3.so-net.ne.jp> writes:
>
> |「const char *」の方が、視覚的な便宜を考慮した例外的記述法と理解
> |した方が、良いと思います。
>
> と例外と考えた方がよいという立場を取られているようです。
今回の、一連の議論の間に宗旨変えしました。
# いささか、無節操?
現在は、<39F3A03E...@gb3.so-net.ne.jp>で述べました通り、
i) constは、「型指定子」(char, int, etc)、又は「*」(ポイ
ンタ宣言子)を修飾する
ii) constが、「型指定子」の直前、又は直後に置かれた場合は
「型指定子」を修飾する
iii) constが、「*」の直後に置かれた場合は「*」を修飾する
と云う解釈規則を推奨しています。
これを、C++のconstメンバ関数もカバーする形に直し、さらに機能の説明も
追加すると、こうなります。
# 箇条書の規則に反しますが、見易くなるように「。」(句点)を付けます。
i) constは、「型指定子」(char, int, etc)、又は「*」(ポインタ宣言子)、
又は「( )」(関数宣言子)を修飾する。
但し、「( )」を修飾するのは、関数が或るクラスのメンバ関数の場合に
限る。
ii) constが、「型指定子」の直前、又は直後に置かれた場合は
「型指定子」を修飾する。
この場合、識別子がポインタでない場合は、識別子自体(配列なら配列
要素)の値が不変になる。
識別子がポインタの場合、ポインタが指し示すオブジェクトが不変になる。
iii) constが、「*」の直後に置かれた場合は「*」を修飾する。
この場合、ポインタそのものの値が不変になる。
iv) constが、「( )」の直後に置かれた場合は「( )」を修飾する。
この場合、このメンバ関数の内部では、全てのクラスメンバが不変になる。
「型指定子」を修飾した時の機能の説明が若干かったるい気がしますが、constが
現れたら、直前(又は直後)を見るだけで解釈が確定し、宣言全体を見回し、
式として評価したりする必要がないのが、私には何より魅力です。
Masao Seki wrote:
> これは、単なる慣れの問題だと思います。
> 私は、constが「*」を修飾していたなら、ポインタそのものがconstと判断し、
> それ以外の可能性は、考えもしません。
いろいろ書こうと思ったのですがやめて、一つだけ質問したいと思います。
char a[10][10][10];
char (*const p)[10][10] = a;
のような場合にも const の右側を見ずに解釈は確定するのでしょうか?
--
竹本 聡 ソニー(株)PNC,PVC,DI部門1部
"Takemoto,Satoshi" wrote:
> char a[10][10][10];
>
> char (*const p)[10][10] = a;
>
> のような場合にも const の右側を見ずに解釈は確定するのでしょうか?
論点がぼけますね。
「左側だけで解釈した方が、右側を見るより簡単だと思われるのでしょうか?」
に訂正します。
--
竹本 聡 ソニー(株)PNC,PVC,DI部門1部
別記事と同様の、(余計なお世話かも、の)代理投稿記事です。
--
関@神奈川
Masao Seki <ma-...@gb3.so-net.ne.jp>
- - - - - - - - - - - - - - ここから - - - - - - - - - - - - - -
In article <39F4DAA1...@cv.sony.co.jp>,
"Takemoto,Satoshi" <take...@cv.sony.co.jp> writes:
> 充分簡潔な規則が有ります。1文で済みます:
>
> ・const より右側のポインタ宣言子、配列宣言、関数宣言について、
> それぞれ参照はがし演算子、配列参照演算子、関数呼出演算子と見なし、
> 式として評価したもの が変更不可である事を指定する。
>
> です。これ以上の事は何も無い筈です。
>
> この規則は型指定子,記憶クラス指定子とも整合します。
> const を static に、「変更不可」を「静的」に変えれば
> 記憶クラス指定子の説明になるし、
記憶クラス指定子の説明には当てはまりません。
void f()
{
int i;
static int *p;
}
p は静的ですが、*p すなわち i は静的ではありません。
static は、p が静的であることを指定します。
--
坂本智彦 saka...@sm.sony.co.jp
- - - - - - - - - - - - - - ここまで - - - - - - - - - - - - - -
このNewsGroupにも、しばしば冴えた記事を投稿されている坂本さんから
メールが届きました。どうも、環境が不調で、記事が出て行かないらしい
です。
私が、フォロー記事をグダグダと考えているうちに、いつものように朝に
なってしまうといけないので、取敢えず代理の形で投稿します。
公開について坂本さんの了解は得ていませんが、私の記憶では、公開されて
困るようなメールは出さない主義をお持ちだった筈ですので、問題ない筈
です。
宗旨変えしてませんよね > 坂本さん
--
関@神奈川
Masao Seki <ma-...@gb3.so-net.ne.jp>
- - - - - - - - - - - - - - ここから - - - - - - - - - - - - - -
In article <39F3A03E...@gb3.so-net.ne.jp>,
Masao Seki <ma-...@gb3.so-net.ne.jp> writes:
> i) constは、「型指定子」(char, int, etc)、又は「*」(ポイ
> ンタ宣言子)を修飾する
> ii) constが、「型指定子」の直前、又は直後に置かれた場合は
> 「型指定子」を修飾する
> iii) constが、「*」の直後に置かれた場合は「*」を修飾する
const unsigned int v = 3;
const は、直後に置かれた型指定子 unsigned だけを修飾していますか?
const static char nl = '\n';
const の直前にも直後にも型指定子はありません。
const は何を修飾していますか?
私なら、次のように表現します。
0) const は、型を修飾し、その型のオブジェクトが変更禁止であることを
指定する。
1) const が宣言指定子列の一部である場合、その宣言指定子列の指定する型
を修飾する。
2) const が * の直後に置かれた場合、そのポインタ型を修飾する。
宣言指定子列について、ちょっと補足。
extern const unsigned long int *const *p;
とあったら、extern const unsigned long int が宣言指定子列です。
宣言指定子列の指定する型は、const unsigned long int です。
const は、unsigned long int を修飾しています。
記憶クラス指定子の extern は、型を指定しません。
宣言されるオブジェクトの記憶クラスやリンケージを指定するだけです。
"Takemoto,Satoshi" wrote: in <39F64E19...@cv.sony.co.jp>
・
> "Takemoto,Satoshi" wrote:
>
> > char a[10][10][10];
> >
> > char (*const p)[10][10] = a;
> >
> > のような場合にも const の右側を見ずに解釈は確定するのでしょうか?
>
> 論点がぼけますね。
> 「左側だけで解釈した方が、右側を見るより簡単だと思われるのでしょうか?」
>
> に訂正します。
訂正して頂いたのですが、まだご質問の意図と言うか、主旨が分かりません。
多分、的を外したフォローとなるかと思いますが、ご容赦下さい。
私が、解釈が確定すると言っているのは、当然ながら(?)constが関わる部分
に関してです。
つまり、(*const p)だけで、「pは何らかのオブジェクトへの不変ポインタ」で
ある事が確定します。あとは、constがない時と全く同様に、優先順位に従って
解釈が進むだけの事です。
constが、どこで何回登場しても、そこだけでケリが付きます。
# constに修飾されて不変になるのが何かが、部分の解釈で決まると云う事です。
識別子の素性を、完全に決めるには、勿論宣言全体を見なければなりません。
# 今回のような単純な例では、右も左もなくて、全部中央と云う感じですが。
Tomohiko Sakamoto wrote: in <39F6EBFF...@gb3.so-net.ne.jp>
・
> In article <39F3A03E...@gb3.so-net.ne.jp>,
> Masao Seki <ma-...@gb3.so-net.ne.jp> writes:
> > i) constは、「型指定子」(char, int, etc)、又は「*」(ポイ
> > ンタ宣言子)を修飾する
> > ii) constが、「型指定子」の直前、又は直後に置かれた場合は
> > 「型指定子」を修飾する
> > iii) constが、「*」の直後に置かれた場合は「*」を修飾する
>
> const unsigned int v = 3;
> const は、直後に置かれた型指定子 unsigned だけを修飾していますか?
この規則は、コンピュータにそのまま教え込むのに使えるレベルを意図した
ものでないので、『「型指定子」(char, int, etc)』と云う表現でお茶を
濁したつもりでした。
> const static char nl = '\n';
> const の直前にも直後にも型指定子はありません。
> const は何を修飾していますか?
X3010によると、externやstaticが先頭以外の所に置かれるのは、互換性
の為に残してあるだけで、いずれ消えるべき物の様でしたので、煩雑を
避ける為、敢えて言及しませんでした。
> 私なら、次のように表現します。
>
> 0) const は、型を修飾し、その型のオブジェクトが変更禁止であることを
> 指定する。
> 1) const が宣言指定子列の一部である場合、その宣言指定子列の指定する型
> を修飾する。
> 2) const が * の直後に置かれた場合、そのポインタ型を修飾する。
私の表現にあった曖昧性が消えて、同等にスッキリしていますね。総合的に
みて、坂本さんの案の方が優秀である事を認めます。
只、少し気になる事として
・「型」を修飾するとすると、関数も修飾するようで、C限定版では少し
具合が悪い
・extern unsigned long int const *const *p;では、
extern unsigned long int const が、宣言指定子列である事を既定
の合意事項として良いのか
が、挙げられます。
> extern const unsigned long int *const *p;
>
> とあったら、extern const unsigned long int が宣言指定子列です。
> 宣言指定子列の指定する型は、const unsigned long int です。
> const は、unsigned long int を修飾しています。
上で述べた事の繰り返しになりますが、extern unsigned long int const
の時も、constがunsigned long intを修飾する事を、ルールとして明示した
かった訳です。
坂本さんの案を核にして、曖昧性を排除する為の、補足項目を付け足した
形にするのが、私にとってはベストになりそうです。
Masao Seki wrote:
> > 記憶クラス指定子の説明になるし、
>
> 記憶クラス指定子の説明には当てはまりません。
> void f()
> {
> int i;
> static int *p;
> }
>
> p は静的ですが、*p すなわち i は静的ではありません。
> static は、p が静的であることを指定します。
調子に乗って暴走してしまいましたね。(^^;
おっしゃる通り、記憶クラス指定子の説明には当てはまりません。
私の記事が間違っておりました。_o_
--
竹本 聡 ソニー(株)PNC,PVC,DI部門1部
Masao Seki wrote:
> 訂正して頂いたのですが、まだご質問の意図と言うか、主旨が分かりません。
<39F59468...@gb3.so-net.ne.jp>より
> 私から見れば、直前のtoken(前がない時だけは後ろ)だけをみれば
> 解釈が確定するのに、何故宣言全体を見なければならないのか。
> しかも、宣言の解釈の過程で、式としての評価まで持ち込んで。
> constの右側を修飾すると言っても、最終的に解釈が確定するまでには、
> constの左側を見ているのではないか。好き好んで回り道する必要ない
> のでは、と思われます。
というお話だったので、
「右側を評価する方が、左側を見て行くより解釈が楽だ」という例を
出したつもりでした。
> つまり、(*const p)だけで、「pは何らかのオブジェクトへの不変ポインタ」で
> ある事が確定します。あとは、constがない時と全く同様に、優先順位に従って
> 解釈が進むだけの事です。
この場合、「const p」だけで「シンボル p が不変である」事は確定します。
不変性の解釈に「*」は不要です。
> 識別子の素性を、完全に決めるには、勿論宣言全体を見なければなりません。
> # 今回のような単純な例では、右も左もなくて、全部中央と云う感じですが。
どのみち、型解釈の為に全体を式として評価せざるを得ない訳です。
定数性の判断は、そのおまけであると。
ALGOL 系言語の様に、配列やポインタ(参照でしたっけ?)、関数(手続き)まで
全て型指定で面倒を見ている言語なら、定数性の指定は型指定を修飾している、
と言われても素直に受け入れられます。
しかしCの場合、良く言えばプラグマティック、悪く言えばルーズな文法で、
「このシンボルに、このような演算を適用したコレ、コレが int なんだ。」
みたいな指定の仕方になります。
だから const も型指定より「コレ」を修飾すると考えた方が
自然(統一的)だと思うわけです。
--
竹本 聡 ソニー(株)PNC,PVC,DI部門1部
竹本さんと私の感覚の相違点が、かなりはっきりして来たように思えます。
"Takemoto,Satoshi" wrote: in <39F7A78A...@cv.sony.co.jp>
・
・
> この場合、「const p」だけで「シンボル p が不変である」事は確定します。
> 不変性の解釈に「*」は不要です。
私にとっては、「シンボル p が不変である」事より「シンボル pはポインタで
ある」事の方が先に確定して欲しいのですが。素性も分からないうちに、不変
であると言われても、何かが確定した気持ちにはなれません。
> > 識別子の素性を、完全に決めるには、勿論宣言全体を見なければなりません。
> > # 今回のような単純な例では、右も左もなくて、全部中央と云う感じですが。
>
> どのみち、型解釈の為に全体を式として評価せざるを得ない訳です。
> 定数性の判断は、そのおまけであると。
私は、宣言の解釈の過程では、式としての評価は一切しません。この辺りの表現
に竹本さんが求める物と、私が求める物の違いが、如実に現れていると思います。
私が、宣言を解釈した結果として期待する物は、宣言が
char a[2][3][4];
const char (*const p)[3][4] = a;
なら、
「pは、charの不変な要素を4個持った配列を、3個要素として持った配列
への不変ポインタであり、ポインタ型に解釈し直された3次元配列 aで
初期化されている。」
と云うメッセージです。
でも、竹本さんが望まれるているのは
「シンボルpは、不変である。pに間接参照演算子を作用させると、要素数が3
の配列が得られる。さらに配列(添字)演算子を作用させると、要素数が4
の配列が得られる。さらに配列演算子を作用させた結果は、不変なchar型の
オブジェクトになる。pは、ポインタ型に解釈し直された3次元配列 aで
初期化されている。」
と云う、演算操作に必要な情報(だけ?)なのでないでしょうか。
最終的に目指している目標が、上記のように異なっていたのでは、何を回り
道と感じるかが食い違うのは当たり前と云う気がします。
それにしても、「型解釈の為に全体を式として評価せざるを得ない」と云う
言い方は、やや問題発言でないでしょうか。これを、まともに受け取ると、
int n = 123;
int *p = &n;
の妥当性の、文法的な説明が出来なくなりそうです。あくまで、文法を覚える
過程の人が対象の話ですが。
> しかしCの場合、良く言えばプラグマティック、悪く言えばルーズな文法で、
> 「このシンボルに、このような演算を適用したコレ、コレが int なんだ。」
> みたいな指定の仕方になります。
私も、左辺値や右辺値の形で、オブジェクトの素性を(意識的に)はっきり
させなければならなくなった時は、宣言文に立ち返って、該当部分を式として
評価したりします。でもそれは、必要に応じて立ち返るのであって、普段は
あまり、そのような事は意識していない気がします。
> だから const も型指定より「コレ」を修飾すると考えた方が
> 自然(統一的)だと思うわけです。
識別子に適用される演算子は、宣言文に現れた(宣言子を読替えた)演算子
だけでなく、例えば配列名はsizeofのオペランドになったりもするので、
私としは、初めの方で挙げたメッセージの形で識別子の素性を、はっきり
させておきたい訳です。
そのメッセージにストレートに到達するには、「型を修飾する」と解釈する
のが、様々な宣言に統一的に、無駄なく対応できる流儀になるのです。
この辺りは、やはり個人の感覚次第で、好みの方を選ぶしかないと思うの
ですが、竹本さんとしては、「前置修飾」に皆が賛成しないと気が済まない
(そう云う人の気が知れない)と云うお気持ちでしょうか。
>この辺りは、やはり個人の感覚次第で、好みの方を選ぶしかないと思うの
>ですが、
そうであれば、話はすでに終わっているんですよね :-)
#関さんの話はたいへん勉強になりました。 m(_._)m
Masao Seki wrote:
> 私にとっては、「シンボル p が不変である」事より「シンボル pはポインタで
> ある」事の方が先に確定して欲しいのですが。素性も分からないうちに、不変
> であると言われても、何かが確定した気持ちにはなれません。
シンボル p が不変である、という情報だけで、例えば「p = NULL;」など
p が左辺値になる式は誤りである事が解ります。
ポインタがどうの、という事まで踏み込んで来ると型の全体が解らないと
どうにも不安な気分です。
> でも、竹本さんが望まれるているのは
> 「シンボルpは、不変である。pに間接参照演算子を作用させると、要素数が3
> の配列が得られる。さらに配列(添字)演算子を作用させると、要素数が4
> の配列が得られる。さらに配列演算子を作用させた結果は、不変なchar型の
> オブジェクトになる。pは、ポインタ型に解釈し直された3次元配列 aで
> 初期化されている。」
> と云う、演算操作に必要な情報(だけ?)なのでないでしょうか。
「だけ」です。
「文法上、間違いないプログラム」を書く為には必要にして充分です。
また、充分厳密な解釈だとも思います。確かに広がりは無いけれど。
「実際に起こる事」をきちんと押えてから
「なんでそのようにするのか」を教えないと、教わる方は混乱しませんか?
# 別に最上段に振りかぶって「公理的プログラミング」がどうの、とか
# 言うつもりも、論じるだけの知識もございませんが。
> 最終的に目指している目標が、上記のように異なっていたのでは、何を回り
> 道と感じるかが食い違うのは当たり前と云う気がします。
ですね。
> それにしても、「型解釈の為に全体を式として評価せざるを得ない」と云う
> 言い方は、やや問題発言でないでしょうか。これを、まともに受け取ると、
> int n = 123;
> int *p = &n;
> の妥当性の、文法的な説明が出来なくなりそうです。あくまで、文法を覚える
> 過程の人が対象の話ですが。
あ、すみません。意味が取れません。
宣言と初期化は別です。念のため。
以前の投稿で示したルールの中にも、代入演算は含まれてなかったです。
# と言う事では無くて? (^^;
私が拘ったのは、
ある程度煩雑な型指定および定数指定を混乱無く解釈する為の簡潔なルール、
という側面です。
# 「式として解釈すれば、どんな煩雑な型でも扱える」と解った人の中に
# 「ナントカを憶えた猿」状態に陥る人がいるのは否めません。
# かく言う私も、一時期そうだったと思います。(^^;
> 私も、左辺値や右辺値の形で、オブジェクトの素性を(意識的に)はっきり
> させなければならなくなった時は、宣言文に立ち返って、該当部分を式として
> 評価したりします。でもそれは、必要に応じて立ち返るのであって、普段は
> あまり、そのような事は意識していない気がします。
最後の拠り所をいきなり教えてしまうのは、教育として有るまじき行為で
しょうか? (^^;
> 識別子に適用される演算子は、宣言文に現れた(宣言子を読替えた)演算子
> だけでなく、例えば配列名はsizeofのオペランドになったりもするので、
> 私としは、初めの方で挙げたメッセージの形で識別子の素性を、はっきり
> させておきたい訳です。
> そのメッセージにストレートに到達するには、「型を修飾する」と解釈する
> のが、様々な宣言に統一的に、無駄なく対応できる流儀になるのです。
うーん、ここも意味が取れません。すみません。
sizeof のオペランドが何型か解釈する方法は、
オペランドを式として解釈する以外無い様に思うのですが。
> この辺りは、やはり個人の感覚次第で、好みの方を選ぶしかないと思うの
> ですが、竹本さんとしては、「前置修飾」に皆が賛成しないと気が済まない
> (そう云う人の気が知れない)と云うお気持ちでしょうか。
気が済まないという事は無いです。
そこまで高圧的な印象を与えたのならお詫びします。すみません。
ただ気が知れないというのは当たっています。
関さんのような感覚の人が、決して少数派ではないというのは感じています。
「人それぞれ」と割切る事もできます。
が、私には どうにも不合理な感覚 だとしか思えない。
「その人の気持ちになって、考えの筋道を理解する」という
コミュニケーションの第1段階で苦慮している次第です。
--
竹本 聡 ソニー(株)PNC,PVC,DI部門1部
"Takemoto,Satoshi" wrote: in <39FD3A73...@cv.sony.co.jp>
・
> Masao Seki wrote:
>
> > 私にとっては、「シンボル p が不変である」事より「シンボル pはポインタで
> > ある」事の方が先に確定して欲しいのですが。素性も分からないうちに、不変
> > であると言われても、何かが確定した気持ちにはなれません。
>
> シンボル p が不変である、という情報だけで、例えば「p = NULL;」など
> p が左辺値になる式は誤りである事が解ります。
「何かが確定した気持ちにはなれません。」と云う気持ちは、自分でも良く
理解出来ていないのですが、識別子が配列名や関数名だった場合、不変で
あると云う情報の対象になり得ない(不変かどうかの評価の対象外)ので、
単純変数なのか、ポインタなのか、それ以外なのかの区別を済ませてしまい
たいのかも知れません。
じゃないな。
constが付く付かないに関係なく、上記の区別を済ませてしまいたいのです。
> ポインタがどうの、という事まで踏み込んで来ると型の全体が解らないと
> どうにも不安な気分です。
これは、全く同感なのです。只、(繰り返しになると思いますが、)「型の
全体が解」る、解り方が決定的に異なっている様なのです。
> > でも、竹本さんが望まれるているのは
> > 「シンボルpは、不変である。pに間接参照演算子を作用させると、要素数が3
> > の配列が得られる。さらに配列(添字)演算子を作用させると、要素数が4
> > の配列が得られる。さらに配列演算子を作用させた結果は、不変なchar型の
> > オブジェクトになる。pは、ポインタ型に解釈し直された3次元配列 aで
> > 初期化されている。」
> > と云う、演算操作に必要な情報(だけ?)なのでないでしょうか。
>
> 「だけ」です。
>
> 「文法上、間違いないプログラム」を書く為には必要にして充分です。
> また、充分厳密な解釈だとも思います。確かに広がりは無いけれど。
「必要」な事は、間違いないでしょう。「充分」かどうかには、疑問があります。
私が、「宣言文からの直接の帰結として許される演算が何で結果は何」と云う
形式より、「識別子Pは、char型の不変な要素を・・・への不変なポインタで
ある。」形式のメッセージを確定させてしまいたいと思う背景には、後者の
メッセージを得る事で、様々な文脈で識別子が登場した時に、演算の対象よりも
一段抽象度が高い概念で識別子を理解した様な気持ちになれる事があるような気が
します。
仮に、「一段抽象度が高い概念で識別子を理解」しているが、Trueだとしても、
それが何らかの現実的な利益に繋がっているのかと問われると、自信がありません。
竹本さんが言われる通り「必要にして充分」なら、私の流儀は確かに、回り道
なのでしょう。
> > それにしても、「型解釈の為に全体を式として評価せざるを得ない」と云う
> > 言い方は、やや問題発言でないでしょうか。これを、まともに受け取ると、
> > int n = 123;
> > int *p = &n;
> > の妥当性の、文法的な説明が出来なくなりそうです。あくまで、文法を覚える
> > 過程の人が対象の話ですが。
>
> あ、すみません。意味が取れません。
> 宣言と初期化は別です。念のため。
> 以前の投稿で示したルールの中にも、代入演算は含まれてなかったです。
> # と言う事では無くて? (^^;
これは、殆どチャチャですので、真剣に受け取らないで下さい。
int *p = &n;
は、pが、intへのポインタ変数である事を宣言すると同時に、pの為に用意する
アドレス・データ格納領域に、nのアドレスを書込む事を、コンパイラ/リンカに
要求している訳で、別の言い方をすると、
int *p;
と、
p = &n;
を、同時に済ませている訳です。(代入と初期化の違いを、意図的に無視して
います。)
でも、「全体を式として評価」すると言ってしまうと、
int *p;
と
*p = &n;
を、同時に済ませていると云う説明しか出来なくなるのではないでしょうか、
と言いたかった訳です。
「(*p)」が、宣言文の中にあれば、「pはポインタ変数である」になりますが、
式として評価すると「pが指し示す実体」を意味しますよね。
> > 識別子に適用される演算子は、宣言文に現れた(宣言子を読替えた)演算子
> > だけでなく、例えば配列名はsizeofのオペランドになったりもするので、
> > 私としは、初めの方で挙げたメッセージの形で識別子の素性を、はっきり
> > させておきたい訳です。
> > そのメッセージにストレートに到達するには、「型を修飾する」と解釈する
> > のが、様々な宣言に統一的に、無駄なく対応できる流儀になるのです。
>
> うーん、ここも意味が取れません。すみません。
>
> sizeof のオペランドが何型か解釈する方法は、
> オペランドを式として解釈する以外無い様に思うのですが。
sizeof のオペランドが何型か解釈する方法の話では、ありません。識別子の
型により、演算の結果が異なりますし、そもそも許される演算が異なってくる
訳ですから、「pはポインタである」とか「aは配列である」とかの認識を明示
的に持っていたいと云う話です。我々が、「sizeof(a)」と書く時、その都度
「aの型は何か」を考えたりはせず、先に、「aは配列」であるとかの認識が
あって、目的に応じて「sizeof(a)」とか書くと思うのですが。
# 多分、竹本さんの疑問を解く説明にはなってないでしょうね。
# 困ったなあ (^_^;;
> > この辺りは、やはり個人の感覚次第で、好みの方を選ぶしかないと思うの
> > ですが、竹本さんとしては、「前置修飾」に皆が賛成しないと気が済まない
> > (そう云う人の気が知れない)と云うお気持ちでしょうか。
>
> 気が済まないという事は無いです。
> そこまで高圧的な印象を与えたのならお詫びします。すみません。
あれま、高圧的な印象を受けているような表現になってなってました? そんな
印象は全く受けていません。私の方こそ、誤解を与える表現で申し訳ありません
でした。
> ただ気が知れないというのは当たっています。
> 関さんのような感覚の人が、決して少数派ではないというのは感じています。
> 「人それぞれ」と割切る事もできます。
>
> が、私には どうにも不合理な感覚 だとしか思えない。
私がお訊きしたかったのは、「人それぞれ」と割切るに当たって、私の説明の
結果、「そのような感覚なら後置修飾で解釈したくなるのも理解できる」程度
に、心情的な理解が高まったか、「現実としてそのような解釈をする人間が
居る事は否定できないので、人それぞれと割り切るしかない」と云う心境か
です。
前者なら、今後今回と同様の議論が持ち上がった時、対等な二つの流儀として
安心して紹介できますが、後者なら
1. 後置修飾解釈の考え方(思考プロセス)が、まだ充分に伝わっていない
2. 後置修飾解釈について、私が気が付いていない欠点(不合理性)が在る
3. 前置修飾解釈の優位点について、理解が不足している
について、もう一度深く考え直さなければいけないかと思っていました。
今回の記事で、後者である事が分かりましたので、少し時間を掛けて、考えて
みたいと思います。
長々と、私の分かり難い文章にお付き合い頂きまして、有り難うございました。