アドレス渡し

関数にアドレスを渡すよ!

#include <stdio.h>

void plusOne(char *p)
{
	printf( "in func, *p = %d, p = 0x%016lX\n", *p, (unsigned long)p );
	*p += 1;
	printf( "in func, *p = %d, p = 0x%016lX\n", *p, (unsigned long)p );
}

int main(int argc, char *argv[])
{
	char val = 0;
	printf( "in main, &val = 0x%016lX\n", (unsigned long)&val );
	printf( "in main, val = %d\n", val );
	plusOne( &val );
	printf( "in main, val = %d\n", val );
	plusOne( &val );
	printf( "in main, val = %d\n", val );
	return 0;
}

実行結果
in main, &val = 0x00007FFF5FBFF90F
in main, val = 0
in func, *p = 0, p = 0x00007FFF5FBFF90F
in func, *p = 1, p = 0x00007FFF5FBFF90F
in main, val = 1
in func, *p = 1, p = 0x00007FFF5FBFF90F
in func, *p = 2, p = 0x00007FFF5FBFF90F
in main, val = 2

関数の引数には、入れ物の住所も渡せちゃうよ。
関数の引数に住所を渡す方法を、「アドレス渡し」って呼ぶよ。
他にも呼び方があるみたいだよ。

mainの中からplusOneを呼ぶときに渡した住所が、
char *pに入っているのが分かるかな?
mainの中で表示した住所と、plusOneの中で表示した住所が一緒だね。
その住所にある入れ物の中身を書き換えているのが、この行だよ。

*p += 1;

pに入っている入れ物の住所は変わらないけど、
入れ物の中身は変わってるね。

関数の引数は大きく分けて、
「値渡し」と「アドレス渡し」しかないから覚えてね!

引数と戻り値

今日は関数を作るよ!

#include <stdio.h>

char getNextValue(char val)
{
	char ret = val + 1;
	printf( "in func, val = %d, ret = %d\n", val, ret );
	val += 2;
	printf( "in func, val = %d, ret = %d\n", val, ret );
	val += 2;
	printf( "in func, val = %d, ret = %d\n", val, ret );
	return ret;
}

int main(int argc, char *argv[])
{
	char val = 0;
	printf( "in main, val = %d\n", val );
	val = getNextValue( val );
	printf( "in main, val = %d\n", val );
	val = getNextValue( val );
	printf( "in main, val = %d\n", val );
	return 0;
}

実行結果
in main, val = 0
in func, val = 0, ret = 1
in func, val = 2, ret = 1
in func, val = 4, ret = 1
in main, val = 1
in func, val = 1, ret = 2
in func, val = 3, ret = 2
in func, val = 5, ret = 2
in main, val = 2

今回作った関数は、charというサイズに収まる値を渡すと、
次の数字をcharというサイズに収めて返すものだよ。
getNextValueの中のvalと、mainのvalが違うものだって分かるかな?
入れ物から入れ物に値だけを渡す方法を、「値渡し」って呼んでいるよ。
returnを通して値を返しているけど、
これも入れ物の中の値を返しているよ。
どういう値が返ってきてるかは、実行結果をよく見てね。
1個1個分からなくても、なんとなく分かることが大切だよ。

配列とポインタ

今日は配列とポインタの関係だよ!

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
	char str[4] = { 'a', 'b', 'c', '\0' };
	char *p = str;
	printf( "%s, length = %lu\n", str, strlen(str) );
	printf( "str = 0x%016lX\n", (unsigned long)str );
	printf( "&(str[0]) = 0x%016lX\n", (unsigned long)&(str[0]) );
	printf( "p = 0x%016lX\n", (unsigned long)p );
	return 0;
}

実行結果
abc, length = 3
str = 0x00007FFF5FBFF900
&(str[0]) = 0x00007FFF5FBFF900
p = 0x00007FFF5FBFF900

配列の初期化にはいくつか方法があって、
今日は入れ物の数を指定して、文字列で初期化するよ。
お約束通り、入れ物の数は文字数+1で、最後尾には’\0’を入れるよ。
文字数を取得する関数strlenは、’\0’が見つかるまで数えてくれるんだけど、
‘\0’が見つからないと大変なことになるから注意してね。

今回覚える新しいことは、
配列というのは連続した入れ物なんだけど、
添字([0]とか[1]とか)を付けないで使うと、
最初の入れ物の住所が取得できるよ。
住所が取得できるということは、住所専用の入れ物に入れることができるよ。
そして、もう一つだいじなことは、
添字を付けないことで取得できる住所と、
最初の入れ物の住所は同じということだよ。
これは、最初の入れ物の住所を取得するには、
変数名に[0]と&を付けて取得する方法と、
添字も&も付けないで取得する方法があるけど、
その住所を格納した入れ物自体は存在しないことを意味してるよ。

ちょっと、難しかったかな。
でも、これ以上難しいことは出てこないから安心してね。

sshのport変更

sshのportを22から変更したんです。
そしたら、ログインできなくなりまして。

経緯としては、sshのportを変えなくちゃ通知が来たので、
良い機会だと思って、手順に沿って変更したんです。
そして、restartして、いざログイン!と思ったら、
受け付けない!ってメッセージが。
どうして?どうして?
何パターンか試したけどうまくいかず。

あ、きっと同じく困ってるヒトがいるはずだ!
ってな訳で、検索した訳ですね。
すると、出てきましたよ!!
portを変更したら、iptablesも変更しましょうねって・・・。
あ、コレだわ。

そんなこんなで、こんなこともあろうかと、
VPSのサイトの管理画面からiptablesの初期化が出来たので、
再度、設定し直して、事なきを得ました。

私のためにあるような機能ですね、iptablesの初期化機能。

変数とポインタ

今日は入れ物の関係だよ!

#include <stdio.h>

int main(int argc, char *argv[])
{
	char ch = 127;
	char *p = &ch;
	printf( "ch = %d\n", ch );
	printf( "*p = %d\n", *p );
	printf( "&ch = 0x%016lX\n", (unsigned long)&ch );
	printf( "p = 0x%016lX\n", (unsigned long)p );
	printf( "&p = 0x%016lX\n", (unsigned long)&p );
	return 0;
}

実行結果
ch = 127
*p = 127
&ch = 0x00007FFF5FBFF90F
p = 0x00007FFF5FBFF90F
&p = 0x00007FFF5FBFF900

入れ物に値を入れるのは大丈夫だね。
入れ物の住所を、住所専用の入れ物に入れるのも大丈夫かな?
住所専用の入れ物に入ってる住所から、その入れ物の値を取り出すには、
*pのように、*を付ければ値を取り出せるよ!
そして最後は、住所専用の入れ物にも住所があって、
それは他の入れ物と同じように、&を付ければ住所が分かるよ。
もう、ポインタなんてこわくないね!

win32 – JPerl Advent Calendar 2010

Advent Calendarって知ってますか!?
12月1日から25日まで、みんなで1個ずつTipsを書こうっていう企画です。
私も参加してみました!
オススメしないperlの実行方法

でも、6人しか参加してないの。
このままだと、25個も出てこないよー!
我こそは!って方じゃなくても参加したらいいって言ってたので、
ぜひ、こっちのAdvent Calendarにも参加してね!

では、おやすみ〜!!

配列?

これが連続した入れ物の正体だよ!

#include <stdio.h>

int main(int argc, char *argv[])
{
	char array[3];
	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]) );
	return 0;
}

実行結果
&(array[0]) = 0x00007FFF5FBFF900
&(array[1]) = 0x00007FFF5FBFF901
&(array[2]) = 0x00007FFF5FBFF902

ほらね、住所が連続してるでしょ。
入れ物の数を指定すると、連続した入れ物を用意できるんだよ。
配列って聞いたら、ああコレね、って思い出してね!

文字列を編集

こうすれば文字列の最後尾を指定できるよ。

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
	char str[] = "hello world";
	str[5] = '\0';
	printf( "%s, length = %lu\n", str, strlen(str) );
	return 0;
}

実行結果
hello, length = 5

連続した入れ物に文字が入ってて、
最後尾に’\0’が入ったものが文字列だよ。
だとしたら、’\0’を格納すれば、そこが最後尾になるよ。
文字列の編集も簡単だね。

文字列の初期化

こういう風に初期化してもいいんだよ。

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
	char str[] = "hello world";
	printf( "%s, length = %lu\n", str, strlen(str) );
	return 0;
}

実行結果
hello world, length = 11

簡単でしょ?
コンパイラさんは、連続した入れ物に自動的に格納してくれるの。
文字数も、こうやって取得できちゃうよ。
でもね、文字数と入れ物の数は別なんだよ。
連続した入れ物の意味は、あとで説明するけど、
入れ物の数は今、覚えてね。
連続した入れ物に文字を1個ずつ入れると、
どこが最後尾なのか分からないの。
それだと、コンパイラさんが困っちゃうのね。
そこで、ここで文字が終わりですよ、ってところで、
入れ物に’\0’を格納するの。
今回は、自動的にそれをやってくれてるから、
入れ物自体は12個用意されてるって訳。
だから、今後は文字列を格納するときは、
入れ物を文字数+1用意してね。

変数のアドレスは?

変数の前に&を付ければ見れるよ。

#include <stdio.h>

int main(int argc, char *argv[])
{
	char val;
	unsigned long address = (unsigned long)&val;
	printf( "&val = %lu(0x%016lX)\n", address, address );
	return 0;
}

実行結果
&val = 140734799804687(0x00007FFF5FBFF90F)

ところで、入れ物と言えば、
住所専用じゃない入れ物も存在するよ。
そういう入れ物の住所も、こんな感じで取得できるよ。
なんに使うかは、あとのお楽しみ。