☆ アドレスについて。 ・オブジェクト(変数)が、メモリ上の何処に存在するか?を表すもの。基本的に数字 で表記され、「メモリ上の番地」となっている。この番地は、コンパイルする時に自 動的に決定される。 (例)
int a = 123;
「メモリ上のある番地に、変数a
としての領域を確保し、その領域に123
という値 を代入する。」という意味。メモリ上のある領域
アドレスを決定 番地:100
a
整数値123 を代入。 ☆ ポインタについて ・ポインタとは、アドレス変数。つまり、「変数のアドレスを記憶する変数」である。 (例) int *ptr = & a; 「メモリ上のある領域に a と ptr の領域を確保し、ptr に a のアドレスの 値を代入する。メモリ上のある領域
番地:
100
a
123 とする番地;
200
*ptr
整数値
200
を代入
(
a
のアドレスの値)1.ポインタについての考察
☆ポインタについての補足 ・ポインタ演算子 ポインタ演算子 使用例 意 味
&
& a
変数
a
のアドレスの値を表す。
*
* ptr
ポインタ p が指すオブジェクトに格納されている値を取り出す。 ・プログラムにおける、ポインタの使い方 1.ポインタの使用に関する、大まかな手順。 ⅰ.ポインタの宣言 ⅱ.ポインタに対して、値(アドレス)の設定 ⅲ.ポインタを使用する --- 2.「int a ;
」 と 「int *a ;
」 の違い ⅰ.int a ;
について。 これは、int
型の変数(オブジェクト)の宣言である。変数a
の値として整数 を格納する箱を用意するイメージ。 ⅱ.int *a ;
について。 これは、ポインタa
の宣言である。ここで宣言されているポインタa
の型は、 「int
型オブジェクトへのポインタ型」という。 --- 3.ポインタを変数のエイリアスとして使用する ⅰ.ポインタに変数のアドレスを代入する。20
int a ;
int *pc ; a = 20;
pc = &a ;
アドレス:345a
変数aに整数値20を格納する。ポインタに、変数aのアドレスを格納する。
*pc
ⅱ
.int
型のポインタpc
の値がint
型の変数a
のアドレスである時、 「ポインタpc
は変数a
を指す」 という。int a ;
int *pc ;
pc a
pc = &a ;
pc
はa
を指す ⅲ.int
型のポインタpc
がint
型の変数a
を指す時、pc
に間接演算子*
を付 けた*pc
は変数a
のエイリアスとなる。 「*pc
が変数a
のエイリアスである」とは、「*pc
が変数a
そのものをす」 という意味である。<参考プログラム>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include<stdio.h> int main() { int a,b; int *pc; a = 25; printf(" a のアドレスの値:%x(16 進数)\n\n",&a); printf(" b のアドレスの値:%x(16 進数)\n\n",&b); printf(" a の値 :%d(10 進数)\n\n",a); puts("---\n"); pc = &a; b = *pc + 100; *pc = 6; puts("pc = &a\n" "b = *pc +100\n" "*pc =6\n" "と初期化した。 \n"); printf(" pc の値 :%x(16 進数)\n\n",pc); printf(" b の値 :%d(10 進数)\n\n",b); printf("再び、a の値 :%d(10 進数)\n\n",a); return 0; }<参考プログラム>の実行結果
a のアドレスの値:5fbffacc(16 進数) b のアドレスの値:5fbffac8(16 進数) a の値 :25(10 進数) ---pc = &a b = *pc +100 *pc =6 と初期化した。 pc の値 :5fbffacc(16 進数) b の値 :125(10 進数) 再び、a の値 :6(10 進数)<解 説>
・ポインタを変数のエイリアスとして用いるプログラムである。 ・10
、11
行目はアドレス演算子&
を用いて、変数のために指定されたアドレスの値を16
進数で表示している。 ・12
行目で表示した変数a
の値は、8
行目で初期化した値の25
である。 ・15
行目では、ポインタpc
に変数a
のアドレス値を代入した。そのため、これ以降の 「*pc
」はa
のエイリアスとなる。 ・21
行目は、pc
の値の表示である。pc
自体には、a
のアドレス値が入っているので、10
行目と同じ事を行ってる。 ・22
行目は、b
の値の表示である。b
の値は「*pc +100
」なので、「a+100
」と同 じ事である。よって、125
と表示されている。 ・23
行目では、再び変数a
の値を表示している。ここで気を付ける事は、「*pc =a
」 と初期化した事で、変数a
の値が8
行目の設定から変化している事である。 ・以上でプログラム終了。ポインタが、指定した変数のエイリアスとして使用出来た。
☆コマンドラインから受け取った文字列の大文字と小文字を変換するプロ
グラムを作成せよ。
<ソースプログラム> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 /* 大文字・小文字変更プログラム*/ #include<stdio.h> #include<ctype.h>void replace(char *dest,char *str); int main(int argc,char **argv){ char dest[256]={0};
int i;
printf("コマンドラインの文字列の個数:%d\n\n",argc); for(i=1; i<argc; i++){
replace(dest, argv[i]); // argv[i] == *(argv + i) printf("変換前: %s\n 変換後: %s\n\n",argv[i],dest);
}
return 0; }
void replace(char *dest,char *str){
int i;
for(i=0; str[i] != NULL; i++){ if(isalpha(str[i])){ if(isupper(str[i])) dest[i] = tolower(str[i]); else dest[i] = toupper(str[i]); } else dest[i] = str[i]; } dest[i] = NULL; }
2.課題ⅰ
<課題ⅰ の実行結果>
Yukihiro-Ikehara-no-MacBook-Pro:prog1 e105746$ ./a.out Abc DeTrmInaNt .
コマンドラインの文字列の個数:4 変換前: Abc 変換後: aBC 変換前: DeTrmInaNt 変換後: dEtRMiNAnT 変換前: . 変換後: .
<課題
i
の解説>
・ 4 行目 :ヘッダー<ctype.h>を読み込んでいる。 これは、isalpha,isupper,tolwer,toupper を使用するからであ る。 ・ 6 行目 :返却値 void 型の関数 replace を宣言している。この関数の仮引数と して、char型のポインタ *dest , *str を設定している。・ 8∼19行目: int 型の main 関数である。コマンドライン引数として、int argc と char argv を宣言している。main 関数が受け取る引数は、この 2 つだけである。 ・ 9行目 :char 型の変数 destを 256 個の配列として宣言。また、全要素の値 を 0 として初期化している。 ・12行目 :変数 argc に格納された値を表示する。argc が受け取る値は、コマ ンドラインに置かれた文字列の数である。 ・14∼17行目 :main 関数における for文である。変数 iの初期値を 1 と設定し、 i が argc の値と同値になれば、ループ終了、ループを繰り返す度に i の値を+1 ずつ増やしていく。
16 行目で、関数 replace を使用。dest と argv[i]の値を決定する。 17 行目で、dest と argv[i]の値を表示。 ・21∼40行目 :replace 関数の詳細設定。void 型なので、値を返す形ではない。 この関数が行う事は、str に格納されたコマンドラインの全ての文字 に対して、英数文字であるかどうかの判定をした後、それが大文字で あれば小文字に、小文字であれば大文字に変換する作業である。 for 文でループしているために、全ての文字を判定出来る。
<課題ⅰの解説(続き)>
今回の実行結果においては、「./a.out Abc DeTrmInaNt .」とコマンドラインに入力 した。 まず、コマンドラインの文字列の個数を格納する変数 argc に入っている値は、4であ る。スペースをで区切られた場所が1つの文字列である。 argv に関して、コマンドラインから受け取った文字列は次のように argv[]に格納さ れる。argv argv[0]
.
/
a
.
o
u
t
NULL
argv[1]A
b
c
NULL
argv[2]D
e
T
r
m
I
n
a
N
t
NULL
argv[3].
NULL
1 回目のループでは、関数 replaceに argv[0]のアドレスを、2 回目は、argv[1]の アドレスを、...、n 回目は、argv[n]のアドレスをという具合で、argv []の値が NULL 値になるまで繰り返す。今回の場合は、3 回 replace 関数にアドレスを渡した ことになる。 関数replaceでは、main関数から受け取ったアドレスをそれぞれ *dest,*strに格納 している。 *str をインクリメントすることで、1文字ずつ取り出し変換処理を行っている。 小文 字は大文字に、大文字は小文字に変換していて、その他の記号はそのまま格納。
配列 dest の最後に NULL を格納して、main 関数に戻る。
main関数のfor文で、iの初期値を0に設定しているのは、「./a.out」を変換処理し たくないからである。
☆文字列を反転して表示するプログラムを作成せよ。
<ソースプログラム>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 /* 文字列入れ替えプログラム*/ #include<stdio.h>void reverse(char *dest,char *str); int main(int argc,char **argv){ char dest[256]={0};
int i;
printf("コマンドラインの文字列の個数:%d\n\n",argc); for(i=1; i<argc; i++){
reverse(dest, argv[i]);
printf("変換前: %s\n 変換後: %s\n\n",argv[i],dest); }
return 0; }
void reverse(char *dest,char *str){
int i,kiri;
for(kiri = 0; str[kiri] != NULL; kiri++); for(i=0; i<kiri; i++)
dest[i]=str[kiri-i-1]; dest[i]=NULL;
}
3.課題ⅱ
<課題ⅱの実行結果>
Yukihiro-Ikehara-no-MacBook-Pro:prog1 e105746$ ./a.out God of eater .
コマンドラインの文字列の個数:5 変換前: God 変換後: doG 変換前: of 変換後: fo 変換前: eater 変換後: retae 変換前: . 変換後: .
<課題ⅱの解説>
・main
関数までの過程は、<課題ⅰ>と同じなので省略する。自作関数reverse
に ついて述べる。 ・20
∼28
行目:14
行目で用いた関数reverse
の定義である。 ・20
行目 :char
型へのポインタ*dest
と*str
を宣言している。このポインタ 変数には、14
行目の時点でのdest
、argv[i]
の値がfor
文でルー プする度に送られる。・
22
行目 :reverse
関数でのint
型変数、i,kiri
を宣言んしている。 ・24
行目 :1文だけのfor
文。argv[i]
から送られた文字列を考慮しながら、 これ以降で使用するkiri
の値を決定するために行う。Kiri
の値はargv[i]
の文字列の中にある文字の個数と同じになる。例えば、
argv[i]
から「God
」と送られてきたとすると、kiri
の 値は3になる。 ・25
、26
行目:argv[i]
から送られた文字列を、本格的に入れ替える過程。kiri
の値が3だとすると、この文によって出される結果が、dest[0] = str[2]
dest[1] = str[1]
dest[2] = str[0]
となるので、dest
に入る値は、argv[i]
から送られてきた文字が 反転する事になる。<課題ⅱの解説の続き>
・
28
行目 :最後にdest[i]
の値を0に初期化する。これを行わなければ、前 の配列の大きさが次の過程に反映されてしまい、argv[i]
から送ら れてきた文字列によっては、実行結果が変になる時がある。・後は、
reverse
関数で決定されたdest
とstr
の値を、main
関数内で出力して終 了という流れである。 ・今回の実行結果における、過程の図を書いてみる。reverse
関数内 ・argv[1]
str[0] str[1] str[2]
G
o
d
↓
dest[0] dest[1] dest[2]
d
o
G
--- ・argv[2]
str[0] str[1]
o
f
↓
dest[0] dest[1]
f
o
・argv[3]
str[0] str[1] str[2] str[3] str[4]e
a
t
e
r
・
argv[3]
の結果dest[0] dest[1] dest[2] dest[3] dest[4]
r
e
t
a
e
---<実行結果がエラーになる場合>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include<stdio.h>void reverse(char *dest,char *str); int main(int argc,char **argv){ char dest[256]={0};
int i;
printf("コマンドラインの文字列の個数:%d\n\n",argc); for(i=1; i<argc; i++){
reverse(dest, argv[i]);
printf("変換前: %s\n 変換後: %s\n\n",argv[i],dest); }
return 0; }
void reverse(char *dest,char *str){
int i,kiri;
for(kiri = 0; str[kiri] != NULL; kiri++); for(i=0; i<kiri; i++)
dest[i]=str[kiri-i-1]; }
<実行結果>
Yukihiro-Ikehara-no-MacBook-Pro:prog1 e105746$ ./a.out God of eater
コマンドラインの文字列の個数:4 変換前: God 変換後: doG 変換前: of 変換後: foG 変換前: eater 変換後: retae ・
reverse
関数の最後のdest[i] = NULL;
を抜かしてみた。2番目のof
の変換で、何故か最後にG
が残っている。<実行結果エラーの解説>
reverse
関数内 ・argv[1]
str[0] str[1] str[2]
G
o
d
↓