2次元配列とポインタ

今日は息抜きだよ!

#include <stdio.h>

int main(int argc, char *argv[])
{
	char strings[3][4] = {
		{ 'a', 'b', 'c', '\0' },
		{ 'd', 'e', 'f', '\0' },
		{ 'g', 'h', 'i', '\0' }
	};

	printf( "strings[0] = %s\n", strings[0] );
	printf( "strings[1] = %s\n", strings[1] );
	printf( "strings[2] = %s\n", strings[2] );
	return 0;
}

実行結果
strings[0] = abc
strings[1] = def
strings[2] = ghi

こうすれば、文字列の配列を表現できるよ。
今日は簡単だったね。

2次元配列

2次元配列の正体は?

#include <stdio.h>

int main(int argc, char *argv[])
{
	char matrix[3][2] = { {0, 1}, {2, 3}, {4, 5} };

	printf( "--- value ---\n" );
	printf( "matrix[0][0] = %d\n", matrix[0][0] );
	printf( "matrix[0][1] = %d\n", matrix[0][1] );
	printf( "matrix[1][0] = %d\n", matrix[1][0] );
	printf( "matrix[1][1] = %d\n", matrix[1][1] );
	printf( "matrix[2][0] = %d\n", matrix[2][0] );
	printf( "matrix[2][1] = %d\n", matrix[2][1] );
	printf( "--- address ---\n" );
	printf( "matrix = %016lX\n", (unsigned long)matrix );
	printf( "matrix[0] = %016lX\n", (unsigned long)(matrix[0]) );
	printf( "&(matrix[0][0]) = %016lX\n", (unsigned long)&(matrix[0][0]) );
	printf( "&(matrix[0][1]) = %016lX\n", (unsigned long)&(matrix[0][1]) );
	printf( "matrix[1] = %016lX\n", (unsigned long)(matrix[1]) );
	printf( "&(matrix[1][0]) = %016lX\n", (unsigned long)&(matrix[1][0]) );
	printf( "&(matrix[1][1]) = %016lX\n", (unsigned long)&(matrix[1][1]) );
	printf( "matrix[2] = %016lX\n", (unsigned long)(matrix[2]) );
	printf( "&(matrix[2][0]) = %016lX\n", (unsigned long)&(matrix[2][0]) );
	printf( "&(matrix[2][1]) = %016lX\n", (unsigned long)&(matrix[2][1]) );
	return 0;
}

実行結果
--- value ---
matrix[0][0] = 0
matrix[0][1] = 1
matrix[1][0] = 2
matrix[1][1] = 3
matrix[2][0] = 4
matrix[2][1] = 5
--- address ---
matrix = 00007FFF5FBFF900
matrix[0] = 00007FFF5FBFF900
&(matrix[0][0]) = 00007FFF5FBFF900
&(matrix[0][1]) = 00007FFF5FBFF901
matrix[1] = 00007FFF5FBFF902
&(matrix[1][0]) = 00007FFF5FBFF902
&(matrix[1][1]) = 00007FFF5FBFF903
matrix[2] = 00007FFF5FBFF904
&(matrix[2][0]) = 00007FFF5FBFF904
&(matrix[2][1]) = 00007FFF5FBFF905

値の取得は簡単だね。値を格納するのも同じだよ。
アドレスをよーく見てね、すべて連続してるよ。
実は、1次元の配列なんだけど、
ヒトが指定した2つ添字からどの変数が該当するかを、
Cコンパイラさんが計算してくれてるよ。
今回も添字を指定しないで取得できるアドレスと、
最初の変数のアドレスは一緒だね。
添字を1つだけ指定したときのアドレスは、
2つ目に[0]を指定したときの変数と同じアドレスだね。

2次元配列もアドレスを格納した変数は存在しないよ。
行列や表を表現するのに、2次元配列は使えそうだね。

const

constを使いこなそう!

#include <stdio.h>

int main(int argc, char *argv[])
{
	char abc[] = "abc";

	const char *p1 = abc;
	char * const p2 = abc;

	printf( "p1 = %s\n", p1 );
	p1++;
	printf( "p1 = %s\n", p1 );
	p1++;
	printf( "p1 = %s\n", p1 );

	printf( "p2 = %s\n", p2 );
	// p2++; error: increment of read-only variable ‘p2’         

	p1 = abc;
	// p1[0] = 'd'; error: assignment of read-only location
	printf( "p1 = %s\n", p1 );

	// p2 = abc; error: assignment of read-only variable ‘p2’
	p2[0] = 'd';
	p2[1] = 'e';
	p2[2] = 'f';
	printf( "p2 = %s\n", p2 );

	printf( "abc[] = %s\n", abc );
	return 0;
}

実行結果
p1 = abc
p1 = bc
p1 = c
p2 = abc
p1 = abc
p2 = def
abc[] = def

constの位置によって意味が変わるよ!
ポインタに*を付けると、値を書き換えられるの覚えているかな?
constがchar *に掛かっていると、
値の書き換えをCコンパイラさんが監視してくれるよ。
p2は、char *ではなくて、p2にconstが掛かっているね。
これだと、p2そのものを変更出来ないようにCコンパイラさんが監視してくれるよ。
でも、p2を使って値の書き換えは出来ちゃうから、
こういう使い方はあまりしないよ。
p1は、途中で配列の先頭アドレスを代入出来るけど、
p2は、constで監視されているのも確認してね。

関数の引数でポインタを受け取るときに、
このポインタを使って書き換えは行わないよって宣言するのに、
constはよく使われるよ。

前回のソースコードをもう一度、確認してみてね!

配列のアドレス渡し

今日は、配列を関数に渡すよ!

#include <stdio.h>

void printElement(const char *pElements, int index)
{
	printf( "pElements[%d] = %d\n", index, pElements[index] );
}

int main(int argc, char *argv[])
{
	char elements[] = { 12, 34, 56, 78, 90 };
	printElement( elements, 0 );
	printElement( elements, 1 );
	printElement( elements, 2 );
	printElement( elements, 3 );
	printElement( elements, 4 );
	return 0;
}

実行結果
pElements[0] = 12
pElements[1] = 34
pElements[2] = 56
pElements[3] = 78
pElements[4] = 90

printElementの引数には、先頭アドレスと何個目を表示するかの番号を渡してるよ。
先頭アドレスといっても、配列の最初の変数のアドレスだね。
そして、関数の中では、ポインタを配列のように扱っているよ。

そして、constってキーワードが今回、新たに出てきたけど、
その意味はポインタを通じて値を変更したらエラーが出るように、
Cコンパイラさんにお願いするキーワードだよ。
こうやって、読み取り専用の変数を実現するよ。

ブロードバンドルーターを買っちゃった

うーん、まだ、よくわかんないですが、
11n/aと11n/g/bっていうのがあって、
2系統を使うことが出来るんですね。
で、PCは11n/a、iPhoneは11n/g/bに設定しました。
どっちがどっちである必要はないそうなんですが、
iPhone4じゃないと11n/aはダメらしいです。
でも、PCが11n/aで繋がったので良しとします。
ちなみに、PCはMacBookです。

あと、11n/aは20MHzにしました。
もしかして、ダメな設定???

おしまい。

ポインタ演算 – その2

ついて来てるかな?

#include <stdio.h>

int main(int argc, char *argv[])
{
	char str[] = "0123456789";
	char *p = str;
	printf( "(p + 0): %s\n", (p + 0) );
	printf( "(p + 1): %s\n", (p + 1) );
	printf( "(p + 2): %s\n", (p + 2) );
	printf( "(p + 3): %s\n", (p + 3) );
	printf( "(p + 4): %s\n", (p + 4) );
	printf( "(p + 5): %s\n", (p + 5) );
	printf( "(p + 6): %s\n", (p + 6) );
	printf( "(p + 7): %s\n", (p + 7) );
	printf( "(p + 8): %s\n", (p + 8) );
	printf( "(p + 9): %s\n", (p + 9) );
	return 0;
}

実行結果
(p + 0): 0123456789
(p + 1): 123456789
(p + 2): 23456789
(p + 3): 3456789
(p + 4): 456789
(p + 5): 56789
(p + 6): 6789
(p + 7): 789
(p + 8): 89
(p + 9): 9

配列の最後に’\0’が入ってるのは、もう聞き飽きたかな?
今回は、先頭アドレスを1つずつずらして出力したよ。
予想通りの結果になったかな?

2次元配列で参った続き

#include <stdio.h>

//void print3x3Matrix(const long m[][3])
void print3x3Matrix(const long (*m)[3])
{
	printf( "--- matrix ---\n" );
	printf( "%3ld, %3ld, %3ld\n", m[0][0], m[0][1], m[0][2] );
	printf( "%3ld, %3ld, %3ld\n", m[1][0], m[1][1], m[1][2] );
	printf( "%3ld, %3ld, %3ld\n", m[2][0], m[2][1], m[2][2] );
}

int main(int argc, char *argv[])
{
	long matrix[][3] = {
		{  0,  1,  2 },
		{ 10, 11, 12 },
		{ 20, 21, 22 }
	};
	print3x3Matrix( (const long (*)[])matrix );
	return 0;
}

結局、分かったことはこんな感じ。
引数の書き方として、3行目と4行目はどちらも同じということ。
関数を呼ぶときに、const付きでキャストしてあげれば、
警告はでないということ。
vcだとでないけど、gccだと出るということ。
(確かに、普段はVC9だけど見た事がない。)

char **をconat char **を引数に取る関数に渡しても、
同様の警告がでるらしい。
詳しくは「C言語 FAQ 日本語訳」を読むと良いと思う。
(リンクしても良いのか判断しかねたので、検索してください。)

という訳で、Cコンパイラさんは、ルールにのっとって警告しているだけで、
この点に関しては、ユーザーの判断でキャストするのが良さそうです。

ちなみに、long (*m)[3]とlong *m[3]は意味が違うから気を付けてね。
おしまい。

2次元配列で参った

#include <stdio.h>

void print3x3Matrix(const long m[][3])
{
	printf( "--- matrix ---\n" );
	printf( "%3ld, %3ld, %3ld\n", m[0][0], m[0][1], m[0][2] );
	printf( "%3ld, %3ld, %3ld\n", m[1][0], m[1][1], m[1][2] );
	printf( "%3ld, %3ld, %3ld\n", m[2][0], m[2][1], m[2][2] );
}

int main(int argc, char *argv[])
{
	long matrix[][3] = {
		{  0,  1,  2 },
		{ 10, 11, 12 },
		{ 20, 21, 22 }
	};
	print3x3Matrix( matrix );
	return 0;
}

で、これをコンパイルすると、
warning: passing argument 1 of ‘print3x3Matrix’ from incompatible
pointer type

13行目にconst付けて宣言するか、
関数の引数からconstを取ると、このwarning消えるみたい。
こういうコードを多々書いてきて、warning出た事ないから、
地味に焦ってたり。
ちなみに環境は、

Mac OS X 10.6.5, Intel Core 2 Duo
gcc -v
Using built-in specs.
Target: i686-apple-darwin10
Configured with: /var/tmp/gcc/gcc-5664~89/src/configure --disable-checking --enable-werror --prefix=/usr --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build=i686-apple-darwin10 --program-prefix=i686-apple-darwin10- --host=x86_64-apple-darwin10 --target=i686-apple-darwin10 --with-gxx-include-dir=/include/c++/4.2.1
Thread model: posix
gcc version 4.2.1 (Apple Inc. build 5664)

このままだと、一人Advent Calendarが頓挫しちゃう、
ってのはどーでも良い話だけど、お願い助けてエロい人!

気が向いたら、助けてくれると幸いです。
おやすみなさい。

ポインタ演算 – その1

今日は、ポインタに数字を足すよ!

#include <stdio.h>

int main(int argc, char *argv[])
{
	char *pInt08 = (char *)0;
	short *pInt16 = (short *)0;
	int *pInt32 = (int *)0;
	long *pInt64 = (long *)0;

	printf( "sizeof(char) = %lu\n", sizeof(char) );
	printf( "pint08 = %2lu\n", (unsigned long)(pInt08 + 0) );
	printf( "pint08 = %2lu\n", (unsigned long)(pInt08 + 1) );
	printf( "pint08 = %2lu\n", (unsigned long)(pInt08 + 2) );

	printf( "sizeof(short) = %lu\n", sizeof(short) );
	printf( "pint16 = %2lu\n", (unsigned long)(pInt16 + 0) );
	printf( "pint16 = %2lu\n", (unsigned long)(pInt16 + 1) );
	printf( "pint16 = %2lu\n", (unsigned long)(pInt16 + 2) );

	printf( "sizeof(int) = %lu\n", sizeof(int) );
	printf( "pint32 = %2lu\n", (unsigned long)(pInt32 + 0) );
	printf( "pint32 = %2lu\n", (unsigned long)(pInt32 + 1) );
	printf( "pint32 = %2lu\n", (unsigned long)(pInt32 + 2) );

	printf( "sizeof(long) = %lu\n", sizeof(long) );
	printf( "pint64 = %2lu\n", (unsigned long)(pInt64 + 0) );
	printf( "pint64 = %2lu\n", (unsigned long)(pInt64 + 1) );
	printf( "pint64 = %2lu\n", (unsigned long)(pInt64 + 2) );
	return 0;
}

実行結果
sizeof(char) = 1
pint08 = 0
pint08 = 1
pint08 = 2
sizeof(short) = 2
pint16 = 0
pint16 = 2
pint16 = 4
sizeof(int) = 4
pint32 = 0
pint32 = 4
pint32 = 8
sizeof(long) = 8
pint64 = 0
pint64 = 8
pint64 = 16

今日から入れ物を変数って呼ぶよ。
変数にはいくつかの大きさがって、
大きさごとに名前が付いてるけど、それを型と呼んでいるよ。
もちろん、ポインタにも大きさがあるよ。
でも、型と、その大きさはプラットフォーム依存だから、
今回と違う結果が得られる環境が存在するよ。

ポインタが配列のように扱えるのは、こないだやったばかりだね。
配列は、変数を連続して並べたものだけど、
型が違うと次の変数があるアドレスまでの大きさも違ってくるよね。
でも、そこはCコンパイラさんが、ポインタの型に応じて、
さりげなく対処してくれてるよ。
つまり、どの大きさの変数のポインタかによって、
+1や+2の意味は変わってくるよ。

今日も、Cコンパイラさんのさりげないやさしさを垣間見れたね。

シンタックスシュガー

ポインタを配列っぽく扱うよ!

#include <stdio.h>

int main(int argc, char *argv[])
{
	short array[3] = { 123, 456, 789 };
	short *p = array;
	printf( "&(array[0]) = 0x%016lX\n", (unsigned long)&(array[0]) );
	printf( "&(array[1]) = 0x%016lX\n", (unsigned long)&(array[1]) );
	printf( "&(array[2]) = 0x%016lX\n", (unsigned long)&(array[2]) );
	printf( "(p + 0) = 0x%016lX\n", (unsigned long)(p + 0) );
	printf( "(p + 1) = 0x%016lX\n", (unsigned long)(p + 1) );
	printf( "(p + 2) = 0x%016lX\n", (unsigned long)(p + 2) );
	printf( "*(p + 0) = %d, p[0] = %d\n", *(p + 0), p[0] );
	printf( "*(p + 1) = %d, p[1] = %d\n", *(p + 1), p[1] );
	printf( "*(p + 2) = %d, p[2] = %d\n", *(p + 2), p[2] );
	return 0;
}

実行結果
&(array[0]) = 0x00007FFF5FBFF900
&(array[1]) = 0x00007FFF5FBFF902
&(array[2]) = 0x00007FFF5FBFF904
(p + 0) = 0x00007FFF5FBFF900
(p + 1) = 0x00007FFF5FBFF902
(p + 2) = 0x00007FFF5FBFF904
*(p + 0) = 123, p[0] = 123
*(p + 1) = 456, p[1] = 456
*(p + 2) = 789, p[2] = 789

今日から、住所専用の入れ物をポインタって言うね。
関数の引数に配列を渡す前に、
ポインタに配列の先頭アドレスが格納して何に使うのか説明するよ。
ポインタには、配列の最初の入れ物の住所が入っているよ。
ポインタに+1や+2をすると、
次の入れ物や、その次の入れ物の住所が取得できるよ。

ポインタに*を付けると、ポインタに格納された住所の入れ物から、
値を取得したり、書き換えたりできるよ。
配列と同じように添字を付けても、値を取得したり書き換えたりできるよ。
今回みたいに、ヒトにとって分かりやすくする特別ルールを、
シンタックスシュガーっていうよ。

ポインタを配列のように使うことができるなんて、
Cコンパイラさんは、ヒトにもやさしいね!