mumumuの日記: 閑話休題: Cの配列とポインタ
日記 by
mumumu
翻訳ばかりここに書いているのもなんなので。
最近Cを久々にいじってみたいと言う気になり、手持ちのPerlで
書かれたCGIをCで置き換えようとしています。
・・・と言えばカッコいいんですが、Cを触るのは入社の研修以
来なので、柴田先生の本やC FAQとかを見ながら
いぢっていますが、、ポインタと配列わかりにくすぎ(´ー`;)
素直にJavaに戻れってか。いや、そうはいかんのですよ(謎
なにげにCの仕事も回ってきてたりしますし。
----
・Cの文字配列に対する注意
*初期化の際は特に注意を要する。一次元配列の際は以下の規
則が適用される
http://www.bohyoh.com/CandCPP/FAQ/FAQ00111.html
int a[] = {1, 2, 5}; /* これはOK */
だが、2重配列では以下のようにやるとコンパイルエラーが出る
( Red Hat Linux 9 : gcc 3.2.2 )
int a[][] = { { 12,23,33 },{12,11,23} }; /* NG */
int a[2][5] = { { 12,23,33 },{12,11,23} }; /* OK */
test.c:5: 配列 `a' の要素に不完全型があります
test.c:5: 警告: 配列 `a' は一要素を持っているものと見なされます
test.c:5: `a' の領域サイズがわかりません
配列の要素数を明示的に指定してやるとOKになる。
----
あと、
int a[][] = { { 12,23,33 },{12,11,23} }; /* NG */
がだめで
int *a[] = { { 12,23,33 },{12,11,23} }; /* OK */
となるのは何故?????? 現在調査中。
[ Update September 11th 13:39 JST by m ]
前者がNGとなる理由は、コンパイラが配列の要素数を把握でき
ないからである。
配列はデータ構造上連続してメモリに確保され、コンパイラが
それを順番に見ていく都合上、配列の要素数がわからないと配
列の境の区別がつかないのである。
*pascalさめありがとうございました。
----
*C、C++の配列一般にいえることだが、配列から配列へ、代入演算
子(=)を使用した直接コピーは仕様上できない。これはウルトラ
F・A・Qであり、配列が左辺値として代入できない要素としてCの仕
様となっていることによる。
http://www.bohyoh.com/CandCPP/FAQ/FAQ00023.html
http://www.catnet.ne.jp/kouno/c_faq/c6.html#7
----
・下のふたつの違いについては、以下のFAQを参照
char a[] = "string literal";
char *a = "string literal";
http://www.catnet.ne.jp/kouno/c_faq/c1.html#32
http://www.catnet.ne.jp/kouno/c_faq/c6.html
式中に現われる型「Tの配列」という左辺値は、3つの 例外を除い
て、配列の最初の要素を指すポインター に意味が格下げになる。
結果としてできるポインター の型は「Tへのポインター」となる。
(3つの例外とは、1)配列がsizeofの引数となるとき(このときの配
列は配列全体と解釈される 2)アドレス演算子&の引数となるとき、
3)char型の配列を文字列リテラルで初期値するとき)。
あと、添え字演算子([])を伴わない配列名も、原則として配列
への先頭ポインタとして解釈される
*pは"string literal"の先頭部分をさすポインタである。決し
て上の二つは等価ではない。これは、「charの配列が文字列リテ
ラルで初期化される場合、上のaはポインタとして扱われること
は無い。」
という配列=ポインタ の例外から来ていると考えられる。
----
・あと、配列へのポインタがそれの先頭を(上記の例外を除
いて)指しているとしても、以下のNGな操作も存在する。
void f()
{
char *a = "ABC"; /* "ABC"に対するポインタ */
char b[] = "DEF"; /* 配列そのもの。ポインタではない */
*a = '1'; /* NG → Aを書き換えることじゃないの? */
b[0] = '1'; /* OK */
}
実際*a = '1'とやると落ちるときもあるし、そうでないときもあ
るのは確認した。理由は↓にある。
文字列リテラルには2種類の少し違った使いみちがある。配列の初
期化指定子(char a[]の宣言で使うような)に使うときは、その配
列の各文字の初期値を指定する。その他の場所で使うときは、
文字の名無し のstaticの配列となる。このときは書き込み
禁止のメモリーに保存されるかもしれない。だから無事に値
を変更できない可能性がある。
http://www.cmagazine.jp/src/kinjite/c/variable.html#index15
最近Cを久々にいじってみたいと言う気になり、手持ちのPerlで
書かれたCGIをCで置き換えようとしています。
・・・と言えばカッコいいんですが、Cを触るのは入社の研修以
来なので、柴田先生の本やC FAQとかを見ながら
いぢっていますが、、ポインタと配列わかりにくすぎ(´ー`;)
素直にJavaに戻れってか。いや、そうはいかんのですよ(謎
なにげにCの仕事も回ってきてたりしますし。
----
・Cの文字配列に対する注意
*初期化の際は特に注意を要する。一次元配列の際は以下の規
則が適用される
http://www.bohyoh.com/CandCPP/FAQ/FAQ00111.html
int a[] = {1, 2, 5}; /* これはOK */
だが、2重配列では以下のようにやるとコンパイルエラーが出る
( Red Hat Linux 9 : gcc 3.2.2 )
int a[][] = { { 12,23,33 },{12,11,23} }; /* NG */
int a[2][5] = { { 12,23,33 },{12,11,23} }; /* OK */
test.c:5: 配列 `a' の要素に不完全型があります
test.c:5: 警告: 配列 `a' は一要素を持っているものと見なされます
test.c:5: `a' の領域サイズがわかりません
配列の要素数を明示的に指定してやるとOKになる。
----
あと、
int a[][] = { { 12,23,33 },{12,11,23} }; /* NG */
がだめで
int *a[] = { { 12,23,33 },{12,11,23} }; /* OK */
となるのは何故?????? 現在調査中。
[ Update September 11th 13:39 JST by m ]
前者がNGとなる理由は、コンパイラが配列の要素数を把握でき
ないからである。
配列はデータ構造上連続してメモリに確保され、コンパイラが
それを順番に見ていく都合上、配列の要素数がわからないと配
列の境の区別がつかないのである。
*pascalさめありがとうございました。
----
*C、C++の配列一般にいえることだが、配列から配列へ、代入演算
子(=)を使用した直接コピーは仕様上できない。これはウルトラ
F・A・Qであり、配列が左辺値として代入できない要素としてCの仕
様となっていることによる。
http://www.bohyoh.com/CandCPP/FAQ/FAQ00023.html
http://www.catnet.ne.jp/kouno/c_faq/c6.html#7
----
・下のふたつの違いについては、以下のFAQを参照
char a[] = "string literal";
char *a = "string literal";
http://www.catnet.ne.jp/kouno/c_faq/c1.html#32
http://www.catnet.ne.jp/kouno/c_faq/c6.html
式中に現われる型「Tの配列」という左辺値は、3つの 例外を除い
て、配列の最初の要素を指すポインター に意味が格下げになる。
結果としてできるポインター の型は「Tへのポインター」となる。
(3つの例外とは、1)配列がsizeofの引数となるとき(このときの配
列は配列全体と解釈される 2)アドレス演算子&の引数となるとき、
3)char型の配列を文字列リテラルで初期値するとき)。
あと、添え字演算子([])を伴わない配列名も、原則として配列
への先頭ポインタとして解釈される
*pは"string literal"の先頭部分をさすポインタである。決し
て上の二つは等価ではない。これは、「charの配列が文字列リテ
ラルで初期化される場合、上のaはポインタとして扱われること
は無い。」
という配列=ポインタ の例外から来ていると考えられる。
----
・あと、配列へのポインタがそれの先頭を(上記の例外を除
いて)指しているとしても、以下のNGな操作も存在する。
void f()
{
char *a = "ABC"; /* "ABC"に対するポインタ */
char b[] = "DEF"; /* 配列そのもの。ポインタではない */
*a = '1'; /* NG → Aを書き換えることじゃないの? */
b[0] = '1'; /* OK */
}
実際*a = '1'とやると落ちるときもあるし、そうでないときもあ
るのは確認した。理由は↓にある。
文字列リテラルには2種類の少し違った使いみちがある。配列の初
期化指定子(char a[]の宣言で使うような)に使うときは、その配
列の各文字の初期値を指定する。その他の場所で使うときは、
文字の名無し のstaticの配列となる。このときは書き込み
禁止のメモリーに保存されるかもしれない。だから無事に値
を変更できない可能性がある。
http://www.cmagazine.jp/src/kinjite/c/variable.html#index15
閑話休題: Cの配列とポインタ More ログイン