整数型と文字型
型によって表現できる値の"範囲"は変わります。
・符号無し整数型...0と正を表現する整数型
・符号付き整数型...負と0と正を表現する整数型
int ...符号付き整数型
signed int ...符号付き整数型
unsigned int ...符号無し整数型
int型は種類が沢山ありますが、短縮名があります。
signed short int は shortと表せる。
signed long int は longと表せる。
###各型の値の範囲を表示の仕方
を使えば各型で表せる値を表示することができます。(オブジェクト)
ただし値は処理系に依存するので私のPCの値なので異なる場合があります。
#include<stdio.h>
#include<limits.h>
int main(void){
puts("本環境での各文字型・整数型の値の範囲");
printf("char : %d ~ %d\n", CHAR_MIN , CHAR_MAX);
printf("signed char : %d ~ %d\n", SCHAR_MIN , SCHAR_MAX);
printf("unsigned char : %d ~ %d\n", 0 , UCHAR_MAX);
printf("short : %d ~ %d\n", SHRT_MIN , SHRT_MAX);
printf("int : %d ~ %d\n", INT_MIN , INT_MAX);
printf("long : %ld ~ %ld\n", LONG_MIN , LONG_MAX);
printf("unsigend short : %u ~ %u\n", 0 , USHRT_MAX);
printf("unsigned : %u ~ %u\n", 0 , UINT_MAX);
printf("unsigned long : %d ~ %lu\n", 0 , ULONG_MAX);
return 0;
}
実行結果
本環境での各文字型・整数型の値の範囲
char : -128 ~ 127
signed char : -128 ~ 127
unsigned char : 0 ~ 255
short : -32768 ~ 32767
int : -2147483648 ~ 2147483647
long : -9223372036854775808 ~ 9223372036854775807
unsigend short : 0 ~ 65535
unsigned : 0 ~ 4294967295
unsigned long : 0 ~ 18446744073709551615
このように表現できる値が映し出されました。
また文字型のchar型が符号付きなのか符号無し型なのかは処理系によって決まるのでどちらであるか、判定するプログラムを作ります。
#include<stdio.h>
#include<limits.h>
int main(void){
printf("この処理系のchar型は");
if (CHAR_MIN)
puts("符号付き型です。"); /*CHAR_MINは0でない。*/
else
puts("符号無し型です。"); /*CHAR_MINは0である。*/
return 0;
}
実行結果
この処理系のchar型は符号付き型です。
あらゆる大きさの型のを調べることができる。sizeof演算子
#include<stdio.h>
#include<limits.h>
int main(void){
printf("sizeof(char) = %u\n", (unsigned)sizeof(char));
printf("sizeof(short) = %u\n", (unsigned)sizeof(short));
printf("sizeof(int) = %u\n", (unsigned)sizeof(int));
printf("sizeof(long) = %u\n", (unsigned)sizeof(long));
return 0;
}
実行結果
sizeof(char) = 1
sizeof(short) = 2
sizeof(int) = 4
sizeof(long) = 8
これもまた処理系によって異なります。
####ビット単位の論理演算子
& 論理積
| 論理和
今まで && , | | を使ってきたのでC言語を学習している時このことが全くわからなかった。
今までRuby言語を学習していた時、trueとfalseの真理値で学習していたが、
ビット単位の論理演算子は1を真,0を偽とみなして論理演算を行うことがわかった。
#文字列
"ABC"⇦ダブルクォーテーションで囲んだ文字列を文字列リテラルという。その文字列リテラルの中にはナル文字が付与されている。
"ABC\0"
\0がナル文字となります。
これをsizeof演算子で大きさを表すと
#include<stdio.h>
#include<limits.h>
int main(void){
printf("sizeof(\"ABC\") = %u\n", (unsigned)sizeof("ABC"));
return 0;
}
実行結果
sizeof("ABC") = 4
となる。つまり\0は一文字としてカウントされる。
#ポインタ
記憶域上には多くのオブジェクトが雑居している。そのためオブジェクトの場所を何らかの方法で表す。その場所をアドレスという。アドレス演算子
#include<stdio.h>
int main(void){
int n;
double x;
int a[3];
printf("n のアドレス : %p\n", &n);
printf("x のアドレス : %p\n", &x);
printf("a[0] のアドレス : %p\n", &a[0]);
printf("a[1] のアドレス : %p\n", &a[1]);
printf("a[2] のアドレス : %p\n", &a[2]);
return 0;
}
※アドレスを表示するための変換指定は%pです。pointerに由来。
実行結果
n のアドレス : 0x7ffee789d9a4
x のアドレス : 0x7ffee789d998
a[0] のアドレス : 0x7ffee789d9ac
a[1] のアドレス : 0x7ffee789d9b0
a[2] のアドレス : 0x7ffee789d9b4
本プログラムで利用している単項&演算子は一般にアドレス演算子と呼ばれる。オブジェクトに&演算子を適用するとそのオブジェクトのアドレスが得られる。
オブジェクトのアドレスを表示したところであまり役に立たないのでもう少し現実的なプログラムを記述します。
#include<stdio.h>
int main(void){
int sato = 178;
int sanaka = 175;
int masaki = 179;
int *isako, *hiroko;
isako = &sato;
hiroko = &masaki;
printf("いさ子さんの好きな人の身長 : %d\n", *isako);
printf("ひろ子さんの好きな人の身長 : %d\n", *hiroko);
isako = &sanaka;
*hiroko = 180;
putchar('\n');
printf("佐藤くんの身長 : %d\n", sato);
printf("佐中くんの身長 : %d\n", sanaka);
printf("真崎くんの身長 : %d\n", masaki);
printf("いさ子さんの好きな人の身長 : %d\n", *isako);
printf("ひろ子さんの好きな人の身長 : %d\n", *hiroko);
return 0;
}
プログラムにあるint *isako, *hiroko;の*はポインタの意味。
・int型のオブジェクト
その値として<整数>を格納する箱。
・intへのポインタ型のオブジェクト
その値として<整数を格納するオブジェクトのアドレス>を格納する箱。
この部分を走査します。
isako = &sato;
hiroko = &masaki;```
1行目isakoとhirokoにポインタを付与します。
2行目 isakoはアドレスをもったsatoに代入されます。そのアドレスの中に178という値が入っています。なので*isakoはsatoであると言っても過言ではない。
3行目 *hirokoがmasakiと言っても過言ではない。
###ポインタと関数
関数の引数としてのポインタのプログラムです。
#include
void hiroko(int *height){
if (*height < 180)
*height = 180;
}
int main(void){
int sato = 178;
int sanaka = 175;
int masaki = 179;
hiroko(&masaki);
printf("佐藤くんの身長 : %d\n", sato);
printf("佐中くんの身長 : %d\n", sanaka);
printf("真崎くんの身長 : %d\n", masaki);
return 0;
}
hiroko(&masaki)で関数hirokoを呼び出し,実引数&masakiは仮引数*heightに代入される。なのでmasakiはheightである。
このように関数に対して、変数の値の変更を頼みたい時にアドレスを渡し依頼をすればよい。
アドレスを渡しますから、そのアドレスが指すオブジェクトに対して、色々な処理を行って、値を書き換えてください!!
###ポインタと配列
#include
int main(void){
int i;
int a[5] = {1,2,3,4,5};
int *p = a;
for (i = 0; i < 5; i++)
printf("&a[%d] = %p p + %d = %p\n", i, &a[i], i, p + i);
return 0;
}
実行結果
&a[0] = 0x7ffeeafb39a0 p + 0 = 0x7ffeeafb39a0
&a[1] = 0x7ffeeafb39a4 p + 1 = 0x7ffeeafb39a4
&a[2] = 0x7ffeeafb39a8 p + 2 = 0x7ffeeafb39a8
&a[3] = 0x7ffeeafb39ac p + 3 = 0x7ffeeafb39ac
&a[4] = 0x7ffeeafb39b0 p + 4 = 0x7ffeeafb39b0
走査していきます。
```int *p = a;```
*p = &a[0]になります。aは配列ではなく配列の先頭要素がpに代入されます。なので
p ⇨ a[0]
p + 1 ⇨ a[1]になる。
なのでポインタpはあたかも配列aそのものであるかのように振る舞う。
p + iはpがさす要素の i 個後方の要素へのポインタそれに間接演算子を適用した式*( p + i )はその要素のエイリアスになる。
*( p + i ) は a [ i ]と等しい
(例) p + 2がa[2]を指すため、*(p + 2)はa[2]です。