Blog
2004/04/28のBlog
[ 14:30 ]
[ └■アーキテクチャな話 ]
【ポインタってどういうもの?】
お待たせしました。
いよいよポインタというものがどういうものかを説明します。
ポインタというのは、「指し棒」のことです。
プログラム上で「指し棒」を使いたいときには、このポインタという機能を利用します。
…ん、よくわからない?(^^;)
例えば…
世界地図を広げて見てみましょう。地球儀でもかまいません。
次にあなたの指で、世界地図の東経135度 北緯37度を指差してみてください…。
さて、この状況をプログラム上であると仮定すると、
地図(地球儀)… メモリ
東経&北緯… アドレス
あなたの指… ポインタ
という関係に当てはめることができます。
ポインタはプログラムで使っている「今見たい情報」に注目するための機能なんです。
ポインタを実際にプログラムで使うには、ポインタ変数として宣言してやる必要があります。
例えば、このように宣言します。
char *pcData;
使う時には、
今さしているのがどこかを見たい・代入したいpcData
今さしているところの値を見たい・代入したい*pcData
という形にします。
実際にどんな値が入っているのかは、以下のようなプログラムを書いて試してみて下さい。
char *pcData;
char strData=10;
pcData = &strData;
printf("strData[Data:%x,%d]\r\n", strData, strData);
printf(" pcData[Pointer:%x, Data:%x,%d]\r\n", pcData, *pcData, *pcData);
ここで、&strDataと言う表現が出てきました。
この表現で「strDataという器の番号(アドレス)」を知る事ができます。
pcData = &strData;
というふうにポインタ変数にアドレス値を渡してやると、ポインタ変数を使ってそのアドレス値が示す器を見に行く事ができます。
言葉ばかりでわかりにくいので、地図(地球儀)の例にもう一度当てはめてみましょう。
&strData: 東経135度 北緯37度
strData: 東経135度 北緯37度が示している土地の名前
pcData : あなたの指
*pcData : あなたの指が示している土地の名前
と当てはめる事ができます。ここで、
pcData = &strData;
とすると、あなたの指をこの位置に持ってきている事になりますので、
pcData : あなたの指がさしているのは東経135度 北緯37度
*pcData : あなたの指がさしている東経135度 北緯37度にある土地の名前
となります。
ここで、pcData += 北緯1度分 とするとどうなるでしょう?
pcData : あなたの指がさしているのは東経135度 北緯38度
*pcData : あなたの指がさしている東経135度 北緯38度の土地の名前
となります。
指の位置は、あなたの思うどおりに変える事ができるのですね。
さて、
char *pcData;
で、宣言した変数でどんな大きさの器が用意されるのかというと、
┃│││┃
┗┷┷┷┛これぐらいの大きさです。
ちなみに
short *psData; でも
long *plData; でも
┃│││┃
┗┷┷┷┛この大きさなんです。
あれ?とおもいました?
longは同じ大きさなんですが、charと宣言しているのなら
┃┃
┗┛これぐらいの大きさだしshortなら
┃│┃
┗┷┛ですね。
このポインタ変数の器にはどんな値が入るのかというと、普通の値ではなく、
「アドレス」の値なのです。
アドレスの値を入れるには
┃│││┃
┗┷┷┷┛これぐらいの大きさの器が必要なわけなのです。
ここで、全部がこの大きさになるのなら、わざわざ char とか short とかって指定してやらなくってもいいんじゃないの?という疑問が出てくるかもしれません。
用意する器の大きさは同じなのですが、ちょっとだけ動作が違ったりするために、char や short などのデータ型で宣言してやらなければならないのです。
どのように違うのかを、例を出してみましょう。
char *pcData;
short *psData;
long *plData;
char hsData[10]
という3つのポインタ変数と1つのchar型配列を用意して、それぞれに同じアドレス値が入るように
pcData = &hsData[10];
psData = (short*)(&hsData[0]);
plData = (long*)(&hsData[0]);
とセットしてみましょう。
このとき、それぞれのポインタ変数が見ているアドレス値は下向き矢印(↓)の
ある地点になります。
pcData
↓
┃│││││││││┃
┗┷┷┷┷┷┷┷┷┷┛
psData
↓
┃│││││││││┃
┗┷┷┷┷┷┷┷┷┷┛
plData
↓
┃│││││││││┃
┗┷┷┷┷┷┷┷┷┷┛
さて、ここで、
pcData++;
psData++;
plData++;
とそれぞれのポインタ変数をインクリメントしてみたらどうなるでしょうか?
pcData
↓
┃│││││││││┃
┗┷┷┷┷┷┷┷┷┷┛
psData
↓
┃│││││││││┃
┗┷┷┷┷┷┷┷┷┷┛
plData
↓
┃│││││││││┃
┗┷┷┷┷┷┷┷┷┷┛
見ている位置がこれだけ変わりました。
それぞれデータ型で宣言したとおりの大きさ分だけポインタの位置が進んでいることが分かります。
ポインタ変数は、扱っているデータ型のサイズを覚えてくれているのです。
注意:ポインタ変数はあくまで変数という器を指差すものです。
それ以外のところを指差さないようにしましょう。
プログラムが落ちる原因になります。
例)
plData = (long*)(&hsData[0]);
plData+=3;
plData
↓
┃│││││││││┃ ここは
┗┷┷┷┷┷┷┷┷┷┛ 変数じゃないよぉ!
ちなみに…
配列の先頭要素のアドレスは
&hsData[0]
と表現しましたが、
hsData
としても同じ事になります。
char hsData[10];
printf("Address: &hsData[0] = %x, hsData = %x\r\n", &hsData[0], hsData);
と試してみましょう。
お待たせしました。
いよいよポインタというものがどういうものかを説明します。
ポインタというのは、「指し棒」のことです。
プログラム上で「指し棒」を使いたいときには、このポインタという機能を利用します。
…ん、よくわからない?(^^;)
例えば…
世界地図を広げて見てみましょう。地球儀でもかまいません。
次にあなたの指で、世界地図の東経135度 北緯37度を指差してみてください…。
さて、この状況をプログラム上であると仮定すると、
地図(地球儀)… メモリ
東経&北緯… アドレス
あなたの指… ポインタ
という関係に当てはめることができます。
ポインタはプログラムで使っている「今見たい情報」に注目するための機能なんです。
ポインタを実際にプログラムで使うには、ポインタ変数として宣言してやる必要があります。
例えば、このように宣言します。
char *pcData;
使う時には、
今さしているのがどこかを見たい・代入したいpcData
今さしているところの値を見たい・代入したい*pcData
という形にします。
実際にどんな値が入っているのかは、以下のようなプログラムを書いて試してみて下さい。
char *pcData;
char strData=10;
pcData = &strData;
printf("strData[Data:%x,%d]\r\n", strData, strData);
printf(" pcData[Pointer:%x, Data:%x,%d]\r\n", pcData, *pcData, *pcData);
ここで、&strDataと言う表現が出てきました。
この表現で「strDataという器の番号(アドレス)」を知る事ができます。
pcData = &strData;
というふうにポインタ変数にアドレス値を渡してやると、ポインタ変数を使ってそのアドレス値が示す器を見に行く事ができます。
言葉ばかりでわかりにくいので、地図(地球儀)の例にもう一度当てはめてみましょう。
&strData: 東経135度 北緯37度
strData: 東経135度 北緯37度が示している土地の名前
pcData : あなたの指
*pcData : あなたの指が示している土地の名前
と当てはめる事ができます。ここで、
pcData = &strData;
とすると、あなたの指をこの位置に持ってきている事になりますので、
pcData : あなたの指がさしているのは東経135度 北緯37度
*pcData : あなたの指がさしている東経135度 北緯37度にある土地の名前
となります。
ここで、pcData += 北緯1度分 とするとどうなるでしょう?
pcData : あなたの指がさしているのは東経135度 北緯38度
*pcData : あなたの指がさしている東経135度 北緯38度の土地の名前
となります。
指の位置は、あなたの思うどおりに変える事ができるのですね。
さて、
char *pcData;
で、宣言した変数でどんな大きさの器が用意されるのかというと、
┃│││┃
┗┷┷┷┛これぐらいの大きさです。
ちなみに
short *psData; でも
long *plData; でも
┃│││┃
┗┷┷┷┛この大きさなんです。
あれ?とおもいました?
longは同じ大きさなんですが、charと宣言しているのなら
┃┃
┗┛これぐらいの大きさだしshortなら
┃│┃
┗┷┛ですね。
このポインタ変数の器にはどんな値が入るのかというと、普通の値ではなく、
「アドレス」の値なのです。
アドレスの値を入れるには
┃│││┃
┗┷┷┷┛これぐらいの大きさの器が必要なわけなのです。
ここで、全部がこの大きさになるのなら、わざわざ char とか short とかって指定してやらなくってもいいんじゃないの?という疑問が出てくるかもしれません。
用意する器の大きさは同じなのですが、ちょっとだけ動作が違ったりするために、char や short などのデータ型で宣言してやらなければならないのです。
どのように違うのかを、例を出してみましょう。
char *pcData;
short *psData;
long *plData;
char hsData[10]
という3つのポインタ変数と1つのchar型配列を用意して、それぞれに同じアドレス値が入るように
pcData = &hsData[10];
psData = (short*)(&hsData[0]);
plData = (long*)(&hsData[0]);
とセットしてみましょう。
このとき、それぞれのポインタ変数が見ているアドレス値は下向き矢印(↓)の
ある地点になります。
pcData
↓
┃│││││││││┃
┗┷┷┷┷┷┷┷┷┷┛
psData
↓
┃│││││││││┃
┗┷┷┷┷┷┷┷┷┷┛
plData
↓
┃│││││││││┃
┗┷┷┷┷┷┷┷┷┷┛
さて、ここで、
pcData++;
psData++;
plData++;
とそれぞれのポインタ変数をインクリメントしてみたらどうなるでしょうか?
pcData
↓
┃│││││││││┃
┗┷┷┷┷┷┷┷┷┷┛
psData
↓
┃│││││││││┃
┗┷┷┷┷┷┷┷┷┷┛
plData
↓
┃│││││││││┃
┗┷┷┷┷┷┷┷┷┷┛
見ている位置がこれだけ変わりました。
それぞれデータ型で宣言したとおりの大きさ分だけポインタの位置が進んでいることが分かります。
ポインタ変数は、扱っているデータ型のサイズを覚えてくれているのです。
注意:ポインタ変数はあくまで変数という器を指差すものです。
それ以外のところを指差さないようにしましょう。
プログラムが落ちる原因になります。
例)
plData = (long*)(&hsData[0]);
plData+=3;
plData
↓
┃│││││││││┃ ここは
┗┷┷┷┷┷┷┷┷┷┛ 変数じゃないよぉ!
ちなみに…
配列の先頭要素のアドレスは
&hsData[0]
と表現しましたが、
hsData
としても同じ事になります。
char hsData[10];
printf("Address: &hsData[0] = %x, hsData = %x\r\n", &hsData[0], hsData);
と試してみましょう。
[ 14:20 ]
[ └■アーキテクチャな話 ]
【ポインタってどういう時に役に立つの?】
ポインタがあるということは、それが役に立つからあるのです。(^^;)
どういう時に役に立つのかというと、
1) 呼び出した関数と値の受け渡しをするとき
2) どれぐらいの要素数かわからない配列の変数を扱うとき
というのが代表例です。
理解の手助けのためには、まず【スコープって何?】の項目をちょっと先に読んでおいて下さい。
スコープが何かが分かる方は別に読まなくってもいいですよー。
【呼び出した関数と値の受け渡しをする】
呼び出した関数と値の受け渡しするときには、
1) 関数の引数で値を渡し、計算結果を関数の戻り値として受け取る
2) 関数の引数でアドレス値を渡し、関数側ではポインタ変数としてアドレス値を受け取る計算結果を関数側でポインタ変数に代入する
の2つのパターンが考えられます。
グローバル変数に値をほうり込むというのはこの場合無視しますね。
1)の場合は、計算結果として返したい値が一つだけのときには使えますが、複数ある場合には使えない方法です。
こういう場合には、2)の方法を取り入れます。
例えば、ある値(inData)を入れると、その計算結果を返してくれる関数を作ったとします。
普通なら、
outData = funkKeisan(inData);
でよさそうなんですが、呼び出している関数の途中で何らかのトラブルが発生してエラーを返してしまう場合がありえるのでしたら、
error_check = funkKeisan(inData, &outData);
のような使い方をするのが賢いといえるでしょう。
-------------------------------------------
2)のパターンの例
# 図で使っている器の大きさは気にしないでね…。
void funkA(){
int iData;
:
funkB(&iData); /* step 1. 3. */
:
}
int funkB(int *iData){ /* step 1. */
:
/* 何らかの計算をし、その値をiDataに代入 step 2. */
}
[step 1.] funkAのiDataのアドレス値を引数としてfunkBを呼び出す。
この時、funkAのiDataのアドレス値はfunkBのiDataにコピーされる。
funkB では iDataには funkAでのiDataのアドレス値が入っている。
*iDataで funkAでのiDataの値を参照できる。
funkAで使っているiDataを器ごとfunkBに貸し出している形になる。
funkA funkB
iData ││ ─→ *iData ┃┃
└┘ 貸出┗┛
[step 2.] funkB内部で計算を行う。その結果を*iDataに代入する。
funkA funkB
iData ││ *iData ┃┃←→(計算)
└┘┗┛
[step 3.] funkAにiDataの器を返却する。(語弊はあるけど…) funkAのiDataの内容は、funkBで計算された結果になっている。 この時点で、funkBのiDataは破棄される。
funkA funkB
iData ┃┃ ←─ *iData ││
┗┛ 返却└┘
-------------------------------------------
【どれぐらいの要素数かわからない配列の変数を扱うとき】
ポインタ型または配列のデータを扱いたいのだけれども、そのままでは使いにくいという場合、本当は配列のデータとして扱いたいのだけれども、どうしてもそういう訳にはいかない場合にはこんな方法はいかがでしょうか。
例1:ポインタ型または配列のデータを扱いたいのだけれども
char pcData[65536];
char *pcBuffer;
/* : */
/* 値をもらう処理が入る */
funk_GetData(pcData);
/* : */
/* 有効な要素の数値を4分の1している */
for(pcBuffer=pcData; *pcBuffer!=0; pcBuffer++)
*pcBuffer >>= 2;
となります。また、
char pcData[65536];
int cnt;
/* : */
/* 値をもらう処理が入る */
funk_GetData(pcData);
/* : */
/* 有効な要素の数値を4分の1している */
for(cnt=0; pcData[cnt]!=0; cnt++)
pcData[cnt] >> 2;
としても同じ事ですので、好みと用途に合わせてお使いになればいいと思います。
例2:本当は配列のデータとして扱いたいんだけれども
# 皆さん、構造体はわかるかな…?
typedef struct _AddressData {
char FirstName[50];
char FamilyName[50];
struct _BirthDay {
int year;
int month;
int day;
} BIRTHDAY BirthDay;
char Address[256];
char JipCode[10];
char PhoneNo[15];
char Email[50];
} ADDRESS_DATA;
fnk_GetAddressData(){
ADDRESS_DATA *pAddresData; /* アドレス帳データ */
int iMembers; /* アドレス帳に登録されている人数 */
/* アドレス帳に何人の人が登録されているか */
iMembers = GetMembers();
/* 人数分のデータの領域を確保 */
pAddressData = (ADDRESS_DATA*)malloc(sizeof(ADDRES_DATA)*iMembers);
:
:
/* データとして確保した領域を開放 */
free(pAddressData);
}
ポインタがあるということは、それが役に立つからあるのです。(^^;)
どういう時に役に立つのかというと、
1) 呼び出した関数と値の受け渡しをするとき
2) どれぐらいの要素数かわからない配列の変数を扱うとき
というのが代表例です。
理解の手助けのためには、まず【スコープって何?】の項目をちょっと先に読んでおいて下さい。
スコープが何かが分かる方は別に読まなくってもいいですよー。
【呼び出した関数と値の受け渡しをする】
呼び出した関数と値の受け渡しするときには、
1) 関数の引数で値を渡し、計算結果を関数の戻り値として受け取る
2) 関数の引数でアドレス値を渡し、関数側ではポインタ変数としてアドレス値を受け取る計算結果を関数側でポインタ変数に代入する
の2つのパターンが考えられます。
グローバル変数に値をほうり込むというのはこの場合無視しますね。
1)の場合は、計算結果として返したい値が一つだけのときには使えますが、複数ある場合には使えない方法です。
こういう場合には、2)の方法を取り入れます。
例えば、ある値(inData)を入れると、その計算結果を返してくれる関数を作ったとします。
普通なら、
outData = funkKeisan(inData);
でよさそうなんですが、呼び出している関数の途中で何らかのトラブルが発生してエラーを返してしまう場合がありえるのでしたら、
error_check = funkKeisan(inData, &outData);
のような使い方をするのが賢いといえるでしょう。
-------------------------------------------
2)のパターンの例
# 図で使っている器の大きさは気にしないでね…。
void funkA(){
int iData;
:
funkB(&iData); /* step 1. 3. */
:
}
int funkB(int *iData){ /* step 1. */
:
/* 何らかの計算をし、その値をiDataに代入 step 2. */
}
[step 1.] funkAのiDataのアドレス値を引数としてfunkBを呼び出す。
この時、funkAのiDataのアドレス値はfunkBのiDataにコピーされる。
funkB では iDataには funkAでのiDataのアドレス値が入っている。
*iDataで funkAでのiDataの値を参照できる。
funkAで使っているiDataを器ごとfunkBに貸し出している形になる。
funkA funkB
iData ││ ─→ *iData ┃┃
└┘ 貸出┗┛
[step 2.] funkB内部で計算を行う。その結果を*iDataに代入する。
funkA funkB
iData ││ *iData ┃┃←→(計算)
└┘┗┛
[step 3.] funkAにiDataの器を返却する。(語弊はあるけど…) funkAのiDataの内容は、funkBで計算された結果になっている。 この時点で、funkBのiDataは破棄される。
funkA funkB
iData ┃┃ ←─ *iData ││
┗┛ 返却└┘
-------------------------------------------
【どれぐらいの要素数かわからない配列の変数を扱うとき】
ポインタ型または配列のデータを扱いたいのだけれども、そのままでは使いにくいという場合、本当は配列のデータとして扱いたいのだけれども、どうしてもそういう訳にはいかない場合にはこんな方法はいかがでしょうか。
例1:ポインタ型または配列のデータを扱いたいのだけれども
char pcData[65536];
char *pcBuffer;
/* : */
/* 値をもらう処理が入る */
funk_GetData(pcData);
/* : */
/* 有効な要素の数値を4分の1している */
for(pcBuffer=pcData; *pcBuffer!=0; pcBuffer++)
*pcBuffer >>= 2;
となります。また、
char pcData[65536];
int cnt;
/* : */
/* 値をもらう処理が入る */
funk_GetData(pcData);
/* : */
/* 有効な要素の数値を4分の1している */
for(cnt=0; pcData[cnt]!=0; cnt++)
pcData[cnt] >> 2;
としても同じ事ですので、好みと用途に合わせてお使いになればいいと思います。
例2:本当は配列のデータとして扱いたいんだけれども
# 皆さん、構造体はわかるかな…?
typedef struct _AddressData {
char FirstName[50];
char FamilyName[50];
struct _BirthDay {
int year;
int month;
int day;
} BIRTHDAY BirthDay;
char Address[256];
char JipCode[10];
char PhoneNo[15];
char Email[50];
} ADDRESS_DATA;
fnk_GetAddressData(){
ADDRESS_DATA *pAddresData; /* アドレス帳データ */
int iMembers; /* アドレス帳に登録されている人数 */
/* アドレス帳に何人の人が登録されているか */
iMembers = GetMembers();
/* 人数分のデータの領域を確保 */
pAddressData = (ADDRESS_DATA*)malloc(sizeof(ADDRES_DATA)*iMembers);
:
:
/* データとして確保した領域を開放 */
free(pAddressData);
}
[ 14:10 ]
[ └■アーキテクチャな話 ]
【どこもかしこもポインタなのかも】
変数ばかりがポインタで扱えるわけではありません。
値を入れる器はすべてポインタで扱うことができます。
変数以外にそんなものあるの?
あるんですね。今までの話の中で、ヒントがあります。
ヒント1:
作業場の話
ヒント2:
関数の戻り値の話
正解は「関数」です。
関数もポインタで扱うことができるのです。
関数という名前の大きな器に値が入っているのです。
# 値がない場合もありますが…。
どんな時に使うのでしょう?
あまり使う機会はないと思いますが、Windowsプログラムではコールバック関数なんてものがありますから、このあたりで使っているといえますね。
【ポインタ値が関数の戻り値であるときの注意点】
時によってはポインタ値が関数の戻り値となる場合があります。
char *strchr(char*, int) 関数などがその例です。
このような関数を作る場合には、少し注意が必要です。
理解の手助けのためには、まず【スコープって何?】の項目をちょっと先に読んでおいて下さい。
例:やっちゃいかんこと
void fnk_Main(void){
char *pcData;
pcData = fnk_Sub();
:
}
char *fnk_Sub(void){
char cData[10];
:
:
return cData;
}
さて、この例ですが、なぜやってはいけないことなのでしょう?
step 1.
fnk_Main関数が呼び出されました。
fnk_Mainfnk_Sub
pcDataの用意まだ姿はなし
┃┃
┗┛
step 2.
fnk_Main内部でfnk_Sub関数が呼び出されました。
fnk_Mainfnk_Sub
fnk_Subの呼出 → cDataの用意
┃┃ ┃┃
┗┛ ┗┛
step 3.
fnk_Subの処理が終わり、fnk_Subで得られた結果をfnk_Mainに返します。
fnk_Mainfnk_Sub
fnk_SubからcDataのアドレス値を
値をもらう戻り値として返す
┃┃←─ cData──┃┃
┗┛┗┛
step 4.
fnk_Sub関数を終了します。
fnk_Mainfnk_Sub
関数ごと破棄される
┃┃
┗┛
step 4.の状態のとき、fnk_Sub関数にあったcDataという変数はどうなったのでしょう?
もう要らなくなったとして処分されてしまうんですね。
ここで、この変数の器の番号を示すアドレス値は自分自身を器であると認識しなくなってしまいます。(ちょっと語弊はあるかも…。)
この時、処分されてしまった変数のアドレス値を持っているfnk_Main関数のpcDataはなくなってしまったはずの器を示していた部分を勝手に器として勘違いしてしまう事になるのです。
これはちょっと困った事…。
器ではないところを器だと勝手に思ってしまうようなプログラムは落ちるプログラムを作ってしまう事になってしまいます。
この事にちょっと気をつけてプログラムを組んでいきましょうね。
変数ばかりがポインタで扱えるわけではありません。
値を入れる器はすべてポインタで扱うことができます。
変数以外にそんなものあるの?
あるんですね。今までの話の中で、ヒントがあります。
ヒント1:
作業場の話
ヒント2:
関数の戻り値の話
正解は「関数」です。
関数もポインタで扱うことができるのです。
関数という名前の大きな器に値が入っているのです。
# 値がない場合もありますが…。
どんな時に使うのでしょう?
あまり使う機会はないと思いますが、Windowsプログラムではコールバック関数なんてものがありますから、このあたりで使っているといえますね。
【ポインタ値が関数の戻り値であるときの注意点】
時によってはポインタ値が関数の戻り値となる場合があります。
char *strchr(char*, int) 関数などがその例です。
このような関数を作る場合には、少し注意が必要です。
理解の手助けのためには、まず【スコープって何?】の項目をちょっと先に読んでおいて下さい。
例:やっちゃいかんこと
void fnk_Main(void){
char *pcData;
pcData = fnk_Sub();
:
}
char *fnk_Sub(void){
char cData[10];
:
:
return cData;
}
さて、この例ですが、なぜやってはいけないことなのでしょう?
step 1.
fnk_Main関数が呼び出されました。
fnk_Mainfnk_Sub
pcDataの用意まだ姿はなし
┃┃
┗┛
step 2.
fnk_Main内部でfnk_Sub関数が呼び出されました。
fnk_Mainfnk_Sub
fnk_Subの呼出 → cDataの用意
┃┃ ┃┃
┗┛ ┗┛
step 3.
fnk_Subの処理が終わり、fnk_Subで得られた結果をfnk_Mainに返します。
fnk_Mainfnk_Sub
fnk_SubからcDataのアドレス値を
値をもらう戻り値として返す
┃┃←─ cData──┃┃
┗┛┗┛
step 4.
fnk_Sub関数を終了します。
fnk_Mainfnk_Sub
関数ごと破棄される
┃┃
┗┛
step 4.の状態のとき、fnk_Sub関数にあったcDataという変数はどうなったのでしょう?
もう要らなくなったとして処分されてしまうんですね。
ここで、この変数の器の番号を示すアドレス値は自分自身を器であると認識しなくなってしまいます。(ちょっと語弊はあるかも…。)
この時、処分されてしまった変数のアドレス値を持っているfnk_Main関数のpcDataはなくなってしまったはずの器を示していた部分を勝手に器として勘違いしてしまう事になるのです。
これはちょっと困った事…。
器ではないところを器だと勝手に思ってしまうようなプログラムは落ちるプログラムを作ってしまう事になってしまいます。
この事にちょっと気をつけてプログラムを組んでいきましょうね。
[ 14:00 ]
[ └■アーキテクチャな話 ]
【スコープって何?】
スコープのことを説明しないといけない…と思いつつ、どこにこの話を入れるか悩んだ挙げ句、最後になってしまいました…。(^^;)
さて、「スコープ」って何でしょう?
平たく言えば、変数の有効範囲のことです。
変数は宣言した場所によって、それ自身を使うことができるエリアが決まってしまいます。
使うことができるエリアを外れてしまうと変数は破棄されてしまいますので、このことに注意してプログラムを組む必要があります。
具体的にどうなるのかは、例を見てもらいましょう。
例)スコープの例
[BOF][EOF]はそれぞれ「ファイルの始まり」と「ファイルの終わり」のことです。
[BOF]
#include "hoge"
int iData1; ↑iData1の有効範囲
│
int main(void){ │
int iData2; │ ↑iData2の有効範囲
: │ │
if(hogehoge){ │ │
int iData3; │ │ ↑iData3の有効範囲
: │ │ ↓
} │ │
: │ │
return 1; │ ↓
} │
│
int fnkCheck(int iData4){ │ ↑iData4の有効範囲
int iData5; │ │ ↑iData5の有効範囲
: │ ↓ ↓
} │
↓
[EOF]
各変数の説明:
iData1 グローバル変数
宣言した位置から、ファイルの後わりまで有効
iData2 main関数のローカル変数
main関数内の宣言した位置から、main関数の終わりまで有効
iData3 main関数のif条件内でのローカル変数
if文内の宣言した位置から、ifが終わるまで有効
iData4 fnkCheck関数のローカル変数
fnkCheck関数内の宣言した位置から、fnkCheck関数の終わりまで有効
iData5 fnkCheck関数のローカル変数
fnkCheck関数内の宣言した位置から、fnkCheck関数の終わりまで有効
上記で示した有効範囲を抜けてしまうと、変数は破棄されてしまいます。変数が破棄されるということは、値を入れておく器がなくなってしまうということです。変数が破棄されると、中に入っていた値は消えてなくなってしまいます。
もし、ある変数の値をほかの関数に渡したい場合には、
1) ポインタ変数を使って値を入れる器をお借りする
2) 値だけを関数の戻り値として返してやる
の2つのパターンをつかわないとせっかく必要な値を得られたのになくしてしまうことになります。
# ここでもグローバル変数は無視します…。(^^;)
プログラムを組む際には、変数の有効範囲・スコープを考えましょうね。
スコープのことを説明しないといけない…と思いつつ、どこにこの話を入れるか悩んだ挙げ句、最後になってしまいました…。(^^;)
さて、「スコープ」って何でしょう?
平たく言えば、変数の有効範囲のことです。
変数は宣言した場所によって、それ自身を使うことができるエリアが決まってしまいます。
使うことができるエリアを外れてしまうと変数は破棄されてしまいますので、このことに注意してプログラムを組む必要があります。
具体的にどうなるのかは、例を見てもらいましょう。
例)スコープの例
[BOF][EOF]はそれぞれ「ファイルの始まり」と「ファイルの終わり」のことです。
[BOF]
#include "hoge"
int iData1; ↑iData1の有効範囲
│
int main(void){ │
int iData2; │ ↑iData2の有効範囲
: │ │
if(hogehoge){ │ │
int iData3; │ │ ↑iData3の有効範囲
: │ │ ↓
} │ │
: │ │
return 1; │ ↓
} │
│
int fnkCheck(int iData4){ │ ↑iData4の有効範囲
int iData5; │ │ ↑iData5の有効範囲
: │ ↓ ↓
} │
↓
[EOF]
各変数の説明:
iData1 グローバル変数
宣言した位置から、ファイルの後わりまで有効
iData2 main関数のローカル変数
main関数内の宣言した位置から、main関数の終わりまで有効
iData3 main関数のif条件内でのローカル変数
if文内の宣言した位置から、ifが終わるまで有効
iData4 fnkCheck関数のローカル変数
fnkCheck関数内の宣言した位置から、fnkCheck関数の終わりまで有効
iData5 fnkCheck関数のローカル変数
fnkCheck関数内の宣言した位置から、fnkCheck関数の終わりまで有効
上記で示した有効範囲を抜けてしまうと、変数は破棄されてしまいます。変数が破棄されるということは、値を入れておく器がなくなってしまうということです。変数が破棄されると、中に入っていた値は消えてなくなってしまいます。
もし、ある変数の値をほかの関数に渡したい場合には、
1) ポインタ変数を使って値を入れる器をお借りする
2) 値だけを関数の戻り値として返してやる
の2つのパターンをつかわないとせっかく必要な値を得られたのになくしてしまうことになります。
# ここでもグローバル変数は無視します…。(^^;)
プログラムを組む際には、変数の有効範囲・スコープを考えましょうね。
[ 13:22 ]
[ ├■占い・診断シリーズ ]
パンダとペンギンと白い海さんちにお住まいのpanda_penguinさまがお書きになったファンタジー職業適性診断のトラックバックです。
はじめはのんちぃさまのお宅で見つけてしまったのですが、元ネタの方にトラックバックしました。
のんちぃさまの仰せのよう、確かに、ファンタジー好きたったらこの診断はやらなきゃダメですよね。(^^;)
ファンタジー職業適性診断のCAMUSの結果はこんな感じになりました。
戦士レベル -6 あきらめましょう
盗賊レベル 4 天性の才能あり
僧侶レベル 7 天性の才能あり
魔法使いレベル 5 天性の才能あり
占い師「そなたに最も似合う職業は、万能だが強引さに欠ける『魔物使い』じゃ。
盗賊と僧侶と魔法使いの特徴を併せ持ち、戦士的な『パワフルさ』に欠けるタイプじゃな。
僧侶的性質が強めなので、『情にもろく』て『過程を重視』するところが特徴じゃ。
弱点をある程度は克服しているものの、強引な『戦士』タイプが苦手なようじゃの。
苦手なタイプの人間とは、対立するのではなく方向転換させるようにしてみることをお勧めするぞ。
(職業メモ)敵であるはずの魔物を味方にできる特殊な術者。魔法は使えないが魔物の能力を借りることができる。
動物と付き合っていかなければならない遊牧民族気質のB型には結構入り用な職業のようですネ。(^^;)
ちなみに、昨日まで猫の手も借りたいほどでしたが、ネコを操ることなんてできやしませんでしたぞ。
あ、ネコは魔物じゃないのか…。
はじめはのんちぃさまのお宅で見つけてしまったのですが、元ネタの方にトラックバックしました。
のんちぃさまの仰せのよう、確かに、ファンタジー好きたったらこの診断はやらなきゃダメですよね。(^^;)
ファンタジー職業適性診断のCAMUSの結果はこんな感じになりました。
戦士レベル -6 あきらめましょう
盗賊レベル 4 天性の才能あり
僧侶レベル 7 天性の才能あり
魔法使いレベル 5 天性の才能あり
占い師「そなたに最も似合う職業は、万能だが強引さに欠ける『魔物使い』じゃ。
盗賊と僧侶と魔法使いの特徴を併せ持ち、戦士的な『パワフルさ』に欠けるタイプじゃな。
僧侶的性質が強めなので、『情にもろく』て『過程を重視』するところが特徴じゃ。
弱点をある程度は克服しているものの、強引な『戦士』タイプが苦手なようじゃの。
苦手なタイプの人間とは、対立するのではなく方向転換させるようにしてみることをお勧めするぞ。
(職業メモ)敵であるはずの魔物を味方にできる特殊な術者。魔法は使えないが魔物の能力を借りることができる。
動物と付き合っていかなければならない遊牧民族気質のB型には結構入り用な職業のようですネ。(^^;)
ちなみに、昨日まで猫の手も借りたいほどでしたが、ネコを操ることなんてできやしませんでしたぞ。
あ、ネコは魔物じゃないのか…。
[ 11:32 ]
[ ├■占い・診断シリーズ ]
パンダとペンギンと白い海さんちにおすまいのpanda_penguinさまがお記になった辛口性格判断のトラックバックです。
辛口性格判断のCAMUSの結果はこんな感じです。
座右の銘は、お気楽極楽。そんなあなたはきのみきのままタイプです。
【出現率】
2003年1月1日以降、このタイプの回答者は845438人のうち 33384人 (3.9%) でした。
【あなたはこんな人】
あなたは「きのみきのまま」タイプの人です。このタイプの人は自分にも他人にも厳しさがなく、責任感を持って物事を処理し、理想に向かって邁進するような努力家でもありません。ただ、もめごとにまきこまれたり、重い責任を負わされなければそれでOK。極めて気楽、気ままに生きているので、ノイローゼや心身症にかかることなどもまずありません。人との摩擦も少なく、いわゆる「毒にも薬にもならないタイプ」・・・なーんて思っているのは大きな勘違い!平穏な間は「気さくないい人」という評価に安住していてもいいですが、いったんトラブルや面倒が生じると、仲間を捨てて1人ででも遁走。あなたのその無責任さゆえに周囲の責任感のある人間がどれだけ迷惑していることか!!自分では「要領よく立ち回る」世渡り上手だなんて自己評価をしていても周囲の人間から見ればただの「ルーズな八方美人」。本当の意味であてにされたり、頼りにされたりすることはありません。適当に利用された後、「さようなら」というパターンが多いようです。男性で言えば「弱い父親」の典型タイプです。また、最近は企業の若い年代で多く見られ、概してやさしい男性が多くなってきたことが端的に示されています。
【仕事】
あなたは仕事に対する意識は高いほうで、日々の仕事でスキルアップを目指しつつも現状に満足することなく、機会があれば転職をする思い切りのよさを持っています。エネルギッシュですが、ときには周りに気づかれないように手を抜いて、要領よく立ち回ります。質やプロセスを犠牲にしてもあまり罪悪感を感じないようです。新分野の開拓に積極的で、魅力的なアイディアを持っている一方で、細かな監督業務などは苦手で飽きっぽいところがあります。仕事人間ではないのでプライベートもそれなりに楽しむことができます。
好きなブランドBEST3
1.無印良品 17%
2.ルイ・ヴィトン/GAP 12%
【芸能人・有名人ならこのタイプ】
木村拓哉(SMAP・タレント)
女性誌の「好きな男No.1」で10連覇を達成するなど、一向にその人気に衰えが見えない彼の強みは、歌、ドラマ、バラエティ...何をするにしても気負いが感じられない自然体にあるのでしょう。外見だけの「いい男」だったらこんなに長続きしないでしょう、いくらSMAPといったってね。
山口智子(女優)
ロンバケ以来、ドラマ出てないなー。やっと出るかと思ったらスペシャルもの。でも「ちょこちょこCM出てるし、旦那も頑張って仕事してくれてるから私はマイペースでやればいいの~」ってとこでしょう。ユニクロのCMでも歩いていただけ、そうあなたは自然体。
・・・
キムタクも山口智子もきのみきのままタイプなのか?!
私と同じでいいのか、それで?!
なんだか、あなたはこんな人と、仕事での態度が違いすぎるのが多少気になりまふ。
無印良品大好きだしGAPも結構好き…でもヴィトンは好きではないぞぅ。
でも、まぁまぁえっか。(笑)
辛口性格判断のCAMUSの結果はこんな感じです。
座右の銘は、お気楽極楽。そんなあなたはきのみきのままタイプです。
【出現率】
2003年1月1日以降、このタイプの回答者は845438人のうち 33384人 (3.9%) でした。
【あなたはこんな人】
あなたは「きのみきのまま」タイプの人です。このタイプの人は自分にも他人にも厳しさがなく、責任感を持って物事を処理し、理想に向かって邁進するような努力家でもありません。ただ、もめごとにまきこまれたり、重い責任を負わされなければそれでOK。極めて気楽、気ままに生きているので、ノイローゼや心身症にかかることなどもまずありません。人との摩擦も少なく、いわゆる「毒にも薬にもならないタイプ」・・・なーんて思っているのは大きな勘違い!平穏な間は「気さくないい人」という評価に安住していてもいいですが、いったんトラブルや面倒が生じると、仲間を捨てて1人ででも遁走。あなたのその無責任さゆえに周囲の責任感のある人間がどれだけ迷惑していることか!!自分では「要領よく立ち回る」世渡り上手だなんて自己評価をしていても周囲の人間から見ればただの「ルーズな八方美人」。本当の意味であてにされたり、頼りにされたりすることはありません。適当に利用された後、「さようなら」というパターンが多いようです。男性で言えば「弱い父親」の典型タイプです。また、最近は企業の若い年代で多く見られ、概してやさしい男性が多くなってきたことが端的に示されています。
【仕事】
あなたは仕事に対する意識は高いほうで、日々の仕事でスキルアップを目指しつつも現状に満足することなく、機会があれば転職をする思い切りのよさを持っています。エネルギッシュですが、ときには周りに気づかれないように手を抜いて、要領よく立ち回ります。質やプロセスを犠牲にしてもあまり罪悪感を感じないようです。新分野の開拓に積極的で、魅力的なアイディアを持っている一方で、細かな監督業務などは苦手で飽きっぽいところがあります。仕事人間ではないのでプライベートもそれなりに楽しむことができます。
好きなブランドBEST3
1.無印良品 17%
2.ルイ・ヴィトン/GAP 12%
【芸能人・有名人ならこのタイプ】
木村拓哉(SMAP・タレント)
女性誌の「好きな男No.1」で10連覇を達成するなど、一向にその人気に衰えが見えない彼の強みは、歌、ドラマ、バラエティ...何をするにしても気負いが感じられない自然体にあるのでしょう。外見だけの「いい男」だったらこんなに長続きしないでしょう、いくらSMAPといったってね。
山口智子(女優)
ロンバケ以来、ドラマ出てないなー。やっと出るかと思ったらスペシャルもの。でも「ちょこちょこCM出てるし、旦那も頑張って仕事してくれてるから私はマイペースでやればいいの~」ってとこでしょう。ユニクロのCMでも歩いていただけ、そうあなたは自然体。
・・・
キムタクも山口智子もきのみきのままタイプなのか?!
私と同じでいいのか、それで?!
なんだか、あなたはこんな人と、仕事での態度が違いすぎるのが多少気になりまふ。
無印良品大好きだしGAPも結構好き…でもヴィトンは好きではないぞぅ。
でも、まぁまぁえっか。(笑)
2004/04/27のBlog
[ 10:12 ]
[ ├■占い・診断シリーズ ]
ザイーガさんちにお住まいのパルモさまがお書きになった、【占い・鑑定】あなたの認定資格、あなたの評判診断のトラックバックです。
パルモ様のところのコメントに書くにはあまりにあんまりな結果なんですもの~。(T_T)
まずは、あなたの認定資格からデス。
ノーベル殺人賞ってなんどすか~~~?
と、ひとしきり大きな声で叫んでみたいと思います。(T^T)
さて、私は誰を殺したんでしょう?
とりあえず、ノーベルさんなんでしょか?
パルモ様のところのコメントに書くにはあまりにあんまりな結果なんですもの~。(T_T)
まずは、あなたの認定資格からデス。
ノーベル殺人賞ってなんどすか~~~?
と、ひとしきり大きな声で叫んでみたいと思います。(T^T)
さて、私は誰を殺したんでしょう?
とりあえず、ノーベルさんなんでしょか?
そして、あなたの評判診断です。
56%とは、後ろ指さされすぎのような気がします。
このトコロ、背中や後頭部やお尻が痛いのは、このせいなのでしょうか。
ともあれ、人のことを気にしすぎているようですので、唯我独尊体制でやっていくべきなのでしょう。
できるのか?>CAMUS
56%とは、後ろ指さされすぎのような気がします。
このトコロ、背中や後頭部やお尻が痛いのは、このせいなのでしょうか。
ともあれ、人のことを気にしすぎているようですので、唯我独尊体制でやっていくべきなのでしょう。
できるのか?>CAMUS
2004/04/26のBlog
[ 17:07 ]
[ ├■Doblogger企画! ]
Letter from CeylonさんちにおすまいのFRANK LLOYDさま率いるB型倶楽部に強制加入になってしまいました。
会員Noは44番だそうです。
ありがたく拝領いたしまする~…。
…って、TB先の諸先輩の方々、絵を張ってらっしゃいますが、張んなきゃならないものでしょうか。(^^;)
# ココで協調性を欠いてどーする>CAMUS
B型は自由奔放な遊牧民が起源だという説があるそうですね。
馬にのって草原を自由に駆け回りたいよーという私の欲求は、ご先祖様の血から来るものなのでしょうか。
牛乳や乳製品が好きなのも、ご先祖様の血なのでしょうか。
ココをみると、B型は
バランスを崩すと自己免疫疾患や珍しい感染症にかかることもあるそうですが、
生活習慣病などには極めて強く、心臓病や癌にもすばらしい抵抗力を持っているそうです
とのことですが、喘息&白血病にかかっちゃっている私はバランス崩れまくり状態なんでしょうね。
とほほ~。(T_T)
がんばってヤクルト400飲みましょう。
会員Noは44番だそうです。
ありがたく拝領いたしまする~…。
…って、TB先の諸先輩の方々、絵を張ってらっしゃいますが、張んなきゃならないものでしょうか。(^^;)
# ココで協調性を欠いてどーする>CAMUS
B型は自由奔放な遊牧民が起源だという説があるそうですね。
馬にのって草原を自由に駆け回りたいよーという私の欲求は、ご先祖様の血から来るものなのでしょうか。
牛乳や乳製品が好きなのも、ご先祖様の血なのでしょうか。
ココをみると、B型は
バランスを崩すと自己免疫疾患や珍しい感染症にかかることもあるそうですが、
生活習慣病などには極めて強く、心臓病や癌にもすばらしい抵抗力を持っているそうです
とのことですが、喘息&白血病にかかっちゃっている私はバランス崩れまくり状態なんでしょうね。
とほほ~。(T_T)
がんばってヤクルト400飲みましょう。
2004/04/24のBlog
[ 22:54 ]
[ ├■DB関連(SQL以外) ]
SQL Server固有の話を書きますね。
SQL Server7.0あたりから、照合順序という概念が導入されました。
要は、検索のときにどういう辞書を使うのか?というのを決めておけるというもので、辞書の種類が豊富になったということが結構なメダマであるとCAMUSは感じています。
通常、デフォルトインストールでSQL Serverを導入すると、Japanese_CI_ASという照合順序になります。
Japaneseというのは見てのとおり「日本語」ということでして、CIというのは大文字小文字を区別しませんということ、ASというのはアクセントを区別しますということです。
もっと平たく言うと、検索時には大文字小文字は区別しません、シングルバイト・ダブルバイトも区別しません、ひらがなカタカナも区別しませんということなんです。
他にもいろいろあるのですが、Japanese_CI_ASとおなじ日本語でありながら、対極にあるといえるのがJapanese_BINでしょう。
要は、大文字小文字も区別すりゃ、シングルバイト・ダブルバイトは当然、ひらがなカタカナだってちゃんと区別するぞという形です。
コレが検索だけの世界だ…と思っていたら、実はそこにワナが潜んでいたのです。
検索のときに利用する言語は、SQLですよね。
そのSQLも、
大文字小文字を区別してくれる
ようになってしまうのです。
例えば、Japanese_CI_ASでは、
SELECT * FROM SYSOBJECTS
というSQLは通用しますが、Japanese_BINでは、通用しなくなります。
怒られてしまう箇所はSYSOBJECTSがありませんというような内容です。
というわけですので、Japanese_BINでも同じ動作をするSQLを書くには、
SELECT * FROM sysobjects
とsysobjectsを小文字で書くことが必須になってしまうのです。
うう、6.5ではこんなことなかったのに!
そういうわけですので、照合順序をデフォルトインストール時と変更しなければならない際には、テーブルレイアウトの大文字小文字からきちんと意識してやらないと、開発がえらい面倒になりますので、ご注意くださいませね。
→ [DB関連INDEX]
SQL Server7.0あたりから、照合順序という概念が導入されました。
要は、検索のときにどういう辞書を使うのか?というのを決めておけるというもので、辞書の種類が豊富になったということが結構なメダマであるとCAMUSは感じています。
通常、デフォルトインストールでSQL Serverを導入すると、Japanese_CI_ASという照合順序になります。
Japaneseというのは見てのとおり「日本語」ということでして、CIというのは大文字小文字を区別しませんということ、ASというのはアクセントを区別しますということです。
もっと平たく言うと、検索時には大文字小文字は区別しません、シングルバイト・ダブルバイトも区別しません、ひらがなカタカナも区別しませんということなんです。
他にもいろいろあるのですが、Japanese_CI_ASとおなじ日本語でありながら、対極にあるといえるのがJapanese_BINでしょう。
要は、大文字小文字も区別すりゃ、シングルバイト・ダブルバイトは当然、ひらがなカタカナだってちゃんと区別するぞという形です。
コレが検索だけの世界だ…と思っていたら、実はそこにワナが潜んでいたのです。
検索のときに利用する言語は、SQLですよね。
そのSQLも、
大文字小文字を区別してくれる
ようになってしまうのです。
例えば、Japanese_CI_ASでは、
SELECT * FROM SYSOBJECTS
というSQLは通用しますが、Japanese_BINでは、通用しなくなります。
怒られてしまう箇所はSYSOBJECTSがありませんというような内容です。
というわけですので、Japanese_BINでも同じ動作をするSQLを書くには、
SELECT * FROM sysobjects
とsysobjectsを小文字で書くことが必須になってしまうのです。
うう、6.5ではこんなことなかったのに!
そういうわけですので、照合順序をデフォルトインストール時と変更しなければならない際には、テーブルレイアウトの大文字小文字からきちんと意識してやらないと、開発がえらい面倒になりますので、ご注意くださいませね。
→ [DB関連INDEX]
[ 10:13 ]
[ ├■会社にて… ]
水曜日あたりから、別プロジェクトの消火活動のため、別オフィスに拉致られています。
自分の私物のNotePCも投入して支援しているのですが、会社のNotePCもお借りしての作業。
自分の私物のNotePCはVAIOで会社のNotePCがThinkPadでして、外付けマウスを利用していないので、ポインタデバイスの違いに指が戸惑っています。(^^;)
消火活動対象は200を超える帳票のレイアウトとデータチェックなもんだから、いつまで経っても終わりませぬ。
帳票用のデータを作るだけでも大変。(T_T)
しかもこのプロジェクトのピンを取っている部署がダメだし以外あまり協力してくれず、そればかりか仕様追加をしてくれるモンで、余計にいつまで経っても終わらない。(怒)
うう、いつまでこの状況が続くのでしょうか。
持病(血液疾患です)もち、小さい子供付きの身では体力が尽きるのは時間の問題と思われるので、ちょっとチーム長に直訴しようかと思案中です…。
自分の私物のNotePCも投入して支援しているのですが、会社のNotePCもお借りしての作業。
自分の私物のNotePCはVAIOで会社のNotePCがThinkPadでして、外付けマウスを利用していないので、ポインタデバイスの違いに指が戸惑っています。(^^;)
消火活動対象は200を超える帳票のレイアウトとデータチェックなもんだから、いつまで経っても終わりませぬ。
帳票用のデータを作るだけでも大変。(T_T)
しかもこのプロジェクトのピンを取っている部署がダメだし以外あまり協力してくれず、そればかりか仕様追加をしてくれるモンで、余計にいつまで経っても終わらない。(怒)
うう、いつまでこの状況が続くのでしょうか。
持病(血液疾患です)もち、小さい子供付きの身では体力が尽きるのは時間の問題と思われるので、ちょっとチーム長に直訴しようかと思案中です…。