メモリとポインタの話の続き
8月25日 3時間目
メモリとポインタの話(続き)
リテラル
ソースコードにおいて、数値や文字列を直接に記述した定数のこと
変数の対義語であり、変更されないことが確約された記述
C++の仕様では以下のようなものがある
- 文字リテラル
- 'A'とか'3'など半角英数文字1文字を表すもの=アスキーコード表に乗ってるもの
- 'シングルクォーテーション'で表す。1文字だけだよ。
- 文字列リテラル
- “Riteral” 任意の長さの、文字(アスキーコード表にあるもの)を並べたものを“ダブルクォーテーション”で囲ったもの
- “ダブルクォーテーション”で囲むと、1文字でも文字とは扱いが違うので注意が必要です
- 整数リテラル
- 315 などアラビア数字(インド数字)を並べたもの
- 何も指定(接頭辞(プレフィックス))がないとおそらく10進数
- →10進整数リテラル
- 315 のように数字だけを並べた一般的なもの
- ってことは、はじめの(一番左の)数字が0だと8進数と勘違いされるので注意!
- →8進整数リテラル
- 0315 のように8進数であることを明示するために、数字列の最左桁に0を付加して記述する
- →16進整数リテラル
- 0x315 又は 0X315
- 16進数であることを明示するために、数字列の最左桁に0xを付加して記述する
まとめ
- シングルクォーテーション=文字1文字分のデータを表す
- ダブルクォーテーション=文字の並んだもの=文字列を表す
- アラビア数字の列=10進数の数を表す
- 0 + アラビア数字列=この数字列は8進数表記
- 0x + 16進数=この数字列(+A~F)は16進表記を表す
という表記法である!(文法ってことよ)
ポインタとアドレスと戦う話
昨日と、前に何回か話した変数に別名を付ける話
こんなコードを書いてみます。
やっていることは。
int a = 5;
//aのメモリ領域(4バイト)を用意し、5で初期化
int *p = nullptr;
//ポインタ変数pを用意し、nullptrで初期化
p = &a;
//pにaのアドレスを代入
①int a;の宣言により、どこか空いているところのメモリアドレスがシステムから提供されそのアドレスに、int = 4byte分のメモリ領域を確保する メモリアドレスは1byteごとに割り振られているので、今0x00001235~0x00001238までの4byte=32bitが確保されている。
name| value | address name| value | address
|________|0x00001234 |________|0x0000123B
a |????????|0x00001235 |________|0x0000123C
|????????|0x00001236 |________|0x0000123D
|????????|0x00001237 |________|0x0000123E
|????????|0x00001238 |________|0x0000123F
|________|0x00001239 |________|0x00001240
|________|0x0000123A |________|0x00001241
②int *p=nullptr;により、たまたま空いていた0x0000123D~0x00001240がメモリアドレス保存用にシステムから提供され、nullptr(数値でいう0、どこのアドレスでもない場所を指すという意味)で初期化される。
nullptrは数値でいう0(=何もないという意味)と同じといったが概念的な話で(とは言えデバッガで値みると0x00000000って書いてるんだよねぇ)、コンパイラやシステムによって0ではなくどこも差していないポインタと解釈されている。
name| value | address name| value | address
|________|0x00001234 |________|0x0000123B
a |????????|0x00001235 |________|0x0000123C
|????????|0x00001236 p |00000000|0x0000123D
|????????|0x00001237 |00000000|0x0000123E
|????????|0x00001238 |00000000|0x0000123F
|________|0x00001239 |00000000|0x00001240
|________|0x0000123A |________|0x00001241
記述の仕方:
宣言が直感的じゃないので混乱するけれども、
型* 変数名 = nullptr;
で、どこかのアドレスを格納するための変数 変数名が用意される
その次にどこも差さないことを明示する呪文としてnullptrが代入される。
(昔は0とかNULLとかを使ってたけど、なんかおかしいよねって気づいた人がいてこうなった。。。)
*はアスタリスク、アスタ、コメとかホシとか読んだりします。
宣言によって自動的にメモリを取ってくれるタイプの変数=普通の変数に対して、
*&変数名*と&を付けることによって変数のある場所の開始アドレスを取得できる。
*&*はアンド、アンパサンドとかよむ
ポインタ変数p(宣言するときは*pのくせに変数として使うときはpである(仕様))
にアドレスを代入してみる
p = &a;
&a => 0x00001235
name| value | address name| value | address
|________|0x00001234 |________|0x0000123B
a |????????|0x00001235 |________|0x0000123C
|????????|0x00001236 p |00000000|0x0000123D
|????????|0x00001237 |00000000|0x0000123E
|????????|0x00001238 |00000000|0x0000123F
|________|0x00001239 |00001235|0x00001240
|________|0x0000123A |________|0x00001241
ところで、現在変数 a は初期化されていないため値が確定しいない。
このままでは値の参照はできない。
(std::cout « a « std::endl; とかすると値が入ってないのでエラーになる)
a = 105; //0x00000069 0...01101001
&a => 0x00001235
name| value | address name| value | address
|________|0x00001234 |________|0x0000123B
a |00000000|0x00001235 |________|0x0000123C
|00000000|0x00001236 p |00000000|0x0000123D
|00000000|0x00001237 |00000000|0x0000123E
|01101001|0x00001238 |00000000|0x0000123F
|________|0x00001239 |00001235|0x00001240
|________|0x0000123A |________|0x00001241
aに値が代入され、参照できるようになった。
ポインタ変数は変数などがある領域の開始アドレスが格納されているが、そのアドレスの中にある2進数の値が何なのかを参照するためには、
*変数名、この場合は *p と書く。
そうすると、p = 0x0…01235 = &a なので、
*p = ( 0x0…01235のアドレスに格納されている値)= aの値 = 0x0…..01101001
となり、指定されたアドレスにある値を参照することができる。
- p
- 現在格納されているどこかのアドレスを指す
- *p
- 現在格納されているどこかのアドレスの中身を指す