• 検索結果がありません。

& & a a * * ptr p int a ; int *a ; int a ; int a int *a

N/A
N/A
Protected

Academic year: 2021

シェア "& & a a * * ptr p int a ; int *a ; int a ; int a int *a"

Copied!
13
0
0

読み込み中.... (全文を見る)

全文

(1)

☆ アドレスについて。  ・オブジェクト(変数)が、メモリ上の何処に存在するか?を表すもの。基本的に数字   で表記され、「メモリ上の番地」となっている。この番地は、コンパイルする時に自   動的に決定される。 (例)

int a = 123;

   「メモリ上のある番地に、変数

a

としての領域を確保し、その領域に

123

という値     を代入する。」という意味。            

メモリ上のある領域

    アドレスを決定       番地

:100

     

a

整数値123 を代入。 ☆ ポインタについて  ・ポインタとは、アドレス変数。つまり、「変数のアドレスを記憶する変数」である。   (例) int *ptr = & a; 「メモリ上のある領域に a ptr の領域を確保し、ptr に a のアドレスの  値を代入する。          

メモリ上のある領域

番地:

100

a       

123 とする

     

番地;

200

   

*ptr

      

整数値

200

を代入

       

       (

a

のアドレスの値)

1.ポインタについての考察

(2)

☆ポインタについての補足  ・ポインタ演算子 ポインタ演算子 使用例 意  味

&

& 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 ;

                    アドレス:345

  

a   

変数aに整数値20を格納する。

    

      

  

ポインタに、変数aのアドレスを格納する。

     

*pc

(3)

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; }

(4)

<参考プログラム>の実行結果

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

行目の設定から変化している事である。  ・以上でプログラム終了。ポインタが、指定した変数のエイリアスとして使用出来た。

(5)

☆コマンドラインから受け取った文字列の大文字と小文字を変換するプロ

  グラムを作成せよ。

<ソースプログラム> 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.課題ⅰ

(6)

<課題ⅰ の実行結果>

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 文でループしているために、全ての文字を判定出来る。

(7)

<課題ⅰの解説(続き)>

 今回の実行結果においては、「./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」を変換処理し たくないからである。

(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 /* 文字列入れ替えプログラム*/ #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.課題ⅱ

(9)

<課題ⅱの実行結果>

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]

から送られてきた文字が         反転する事になる。

(10)

<課題ⅱの解説の続き>

 ・

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

(11)

 ・

argv[3]

の結果

dest[0] dest[1] dest[2] dest[3] dest[4]

r

e

t

a

e

(12)

---<実行結果がエラーになる場合>

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

が残っている。

(13)

<実行結果エラーの解説>

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

      

arg1

G

が残っている。       

f

o

G

となる。つぎのarg[3]は、配列が5個になり、arg[2]の結果も全て押し出されて、   課題ⅱと同じ結果になった。    今回の課題は、ポインタを用いてプログラムを動作する課題でした。そのため、C言語 難関地点とされるポインタについて理解しなければならず、大変でした。ポインタは配列 と併用して使うので、複雑な考えになる点が難しいと感じました。今回の課題の範囲はあ る程度理解出来たと思います。  ポインタは奥が深い分野だと思うので、今回の課題の範囲でカバー出来なかった分は、 自分で勉強するつもりです。     G

4.感  想

参照

関連したドキュメント

Intervals graphs (denoted by INT ) are intersection graphs of intervals on a line, circular-arc graphs (CA ) are intersection graphs of intervals (arcs) on a circle, circle graphs (CI

What relates to Offline Turing Machines in the same way that functional programming languages relate to Turing Machines?.. Int Construction.. Understand the transition from

The conjecture of Erd¨os–Graham was proved by Dixmier [2], by combining Kneser’s addition theorem for finite abelian groups and some new arguments carried over the integers.. Let

Tsouli, Infinitely many solutions for nonlocal elliptic p-Kirchhoff type equation under Neumann boundary condition, Int. Journal

※ MSCI/S&amp;P GICSとは、スタン ダード&プアーズとMSCI Inc.が共 同で作成した世界産業分類基準 (Global Industry Classification

弊社専用ダイヤルもしくは、お買い上げの販 売会社にご連絡ください。( ☞裏表紙 ) 特定コンセント

TCLKP_AB TCLKN_AB DOUT0P_A_AB DOUT0N_A_AB DOUT1P_A_AB DOUT1N_A_AB DOUT0P_B_AB DOUT0N_B_AB DOUT1P_B_AB

       資料11  廃  棄  物  の  定  義  に  つ  い  て  の  現  行  の  解  釈.