ポインタ
ポインタを利用して二つの値を交換する
#include<stdio.h> void swap(int *nx,int *ny){ int temp = *nx; *nx = *ny; *ny = temp; } int main(void){ int na,nb; puts("二つの整数を入力してください。"); printf("整数1:"); scanf("%d",&na); printf("整数2:"); scanf("%d",&nb); swap(&na,&nb); puts("これらの値を交換しました。"); printf("整数1は%dです。\n",na); printf("整数2は%dです。\n",nb); return 0; }
値を間接的に交換してやるってことか。。
和と差を求める
#include<stdio.h> void sum_diff(int n1,int n2,int *sum,int *diff){ *sum = n1 + n2; *diff = ( n1 > n2 ) ? n1 - n2:n2 - n1; } int main(void){ int na,nb; int wa = 0 ,sa = 0; puts("二つの整数を入力してください。"); printf("整数A:"); scanf("%d",&na); printf("整数B:"); scanf("%d",&nb); sum_diff(na,nb,&wa,&sa); printf("和は%dです。\n差は%dです。\n",wa,sa); return 0; }
変数のアドレスを格納する変数がポインタってことでいいのかな。。。
関数にポインタを渡して、関数の中で間接演算子*を適用させて、そのポインタの指すオブジェクトを間接的に扱う。
何となくは分かるんだけど。C言語の難関ポインタを勉強するぞ
ポインタ
アドレス
オブジェクトが、記憶域上のどこにあるのかを表すのがポインタ。
オブジェクトのアドレスとは、それが格納されている記憶域上の番地のことである。
実際にアドレスを調べてみる
#include<stdio.h> int main(void){ int nx; double dx; int vc[3]; printf("nx のアドレス:%p\n",&nx); printf("dx のアドレス:%p\n",&dx); printf("vc[0]のアドレス:%p\n",&vc[0]); printf("vc[1]のアドレス:%p\n",&vc[1]); printf("vc[2]のアドレス:%p\n",&vc[2]); return 0; }
実行結果
nx のアドレス:0xbfaa3fbc dx のアドレス:0xbfaa3fb0 vc[0]のアドレス:0xbfaa3fa4 vc[1]のアドレス:0xbfaa3fa8 vc[2]のアドレス:0xbfaa3fac
何だかよく分からないな。
ポインタと配列
ポインタと配列
#include<stdio.h> int main(void){ int i; int vc[5] = {10,20,30,40,50}; int *ptr = &vc[0]; for(i=0;i<5;i++){ printf("vc[%d] = %d ptr[%d] = %d *(ptr + %d) = %d\n", i,vc[i],i,ptr[i],i,*(ptr + i)); } return 0; }
実行結果
vc[0] = 10 ptr[0] = 10 *(ptr + 0) = 10 vc[1] = 20 ptr[1] = 20 *(ptr + 1) = 20 vc[2] = 30 ptr[2] = 30 *(ptr + 2) = 30 vc[3] = 40 ptr[3] = 40 *(ptr + 3) = 40 vc[4] = 50 ptr[4] = 50 *(ptr + 4) = 50
ptrはvc[0]を指すので、*ptrはvc[0]のエイリアス(別名)となる。
ポインタptrに対して、整数iを加えたり、減じたりした式も、ポインタになる。
- ptr + iは、ptrが指すオブジェクトのi個後ろの要素を指すポインタとなる。
- ptr - iは、ptrが指すオブジェクトのi個前の要素を指すポインタとなる。
- *(ptr + i)はptr[i]と表記できる。
入出力と文字
数字文字のカウント
getchar()関数は、文字列を読み込んで、それを返す関数。
読み込み時にエラーが発生したり、入力の終了に達すると、EOFという値を返す。
#include<stdio.h> int main(void){ int i,ch; int cnt[10] = {0}; while(1){ ch = getchar(); if(ch == EOF){ break; } switch(ch){ case '0': cnt[0]++; break; case '1': cnt[1]++; break; case '2': cnt[2]++; break; case '3': cnt[3]++; break; case '4': cnt[4]++; break; case '5': cnt[5]++; break; case '6': cnt[6]++; break; case '7': cnt[7]++; break; case '8': cnt[8]++; break; case '9': cnt[9]++; break; } } puts("数字文字の出現回数"); for(i=0;i<10;i++){ printf("'%d' : %d\n",i,cnt[i]); } return 0; }
このプログラムは次のように簡潔に記述できる。
#include<stdio.h> int main(void){ int i,ch; int cnt[10] = {0}; while(1){ ch = getchar(); if(ch == EOF){ break; } if(ch >= '0' && ch <= '9'){ cnt[ch - '0']++; } } puts("数字文字の出現回数"); for(i=0;i<10;i++){ printf("'%d' : %d\n",i,cnt[i]); } return 0; }
C言語での"文字"とは、その文字に与えられた文字コードすなわち整数値である。
グラフにしてみた
#include<stdio.h> int main(void){ int i,j,ch; int cnt[10] = {0}; while(1){ ch = getchar(); if(ch == EOF){ break; } if(ch >= '0' && ch <= '9'){ cnt[ch - '0']++; } } puts("数字文字の出現回数の分布グラフ"); for(i=0;i<10;i++){ printf("'%d':",i); for(j=0;j<cnt[i];j++){ putchar('*'); } putchar('\n'); } return 0; }
再帰関数呼び出し
階乗を求めるプログラム
#include <stdio.h> int factorial(int n){ if(n>0){ return (n * factorial(n-1)); }else{ return 1; } } int main(void){ int num; printf("整数を入力してください。"); scanf("%d",&num); printf("その数の階乗は%dです。\n",factorial(num)); return 0; }
何らかの計算・操作を行いたいとき、それを実現するのがたまたま自分自身と同じ関数であれば、その関数を呼び出すことができる。
列挙体
#include<stdio.h> #define putsa(str) (putchar('\a'),puts(str)) enum animal {Dog,Cat,Monkey,Invalid}; void dog(void){ puts("わんわん"); } void cat(void){ puts("にゃんにゃん"); } void monkey(void){ puts("きっきっ"); } enum animal select(void){ int tmp; do{ printf("0… 犬 1… 猫 2… 猿 3… 終了:"); scanf("%d",&tmp); if(tmp<Dog || tmp>Invalid){ putsa("不正な入力です。"); } }while(tmp<Dog || tmp>Invalid); return tmp; } int main(void){ enum animal selected; do{ switch(selected = select()){ case Dog: dog(); break; case Cat: cat(); break; case Monkey: monkey(); break; } }while(selected != Invalid); return 0; }
enum animal {Dog,Cat,Monkey,Invalid};
この部分は、列挙体の宣言。その列挙体に与える識別子であるanimalをタグと呼ぶ。
{}の中にかかれた、Dog,Cat,Monkey,Invalidは列挙定数で、それぞれの列挙定数には、先頭から順に0,1,2,3という整数値が与えられる。
enum animal型は、その値の集合を表す型になる。
enum animal selected;
この部分は、enum animal型をもつ変数selectedの宣言。
この宣言によって、selectedは,0,1,2,3という値をもち得る変数となる。
関数形式マクロ
関数形式マクロを利用したプログラムの例
#include<stdio.h> #define sqr(x) ( (x) * (x) ) int main(void){ int nx; double dx; printf("整数を入力してください。"); scanf("%d",&nx); printf("その数の2乗は%dです。\n",sqr(nx)); printf("実数を入力してください。"); scanf("%lf",&dx); printf("その数の2乗は%fです。\n",sqr(dx)); return 0; }
#define sqr(x) ( (x) * (x) )
この部分は、これ以降にsqr(x)のという形の式があれば、それを( (x) * (x) )と展開せよ。というような指示。
関数と同じ感覚で、しかも型に依存せずに使える。
二つの値の差を返す関数形式マクロ
#include<stdio.h> #define diff(x,y) ((x)>(y)?(x)-(y):(y)-(x)) int main(void){ int x,y; double dx,dy; puts("二つの整数を入力してください。"); printf("1:"); scanf("%d",&x); printf("2:"); scanf("%d",&y); printf("二つの整数の差は%dです。\n",diff(x,y)); puts("二つの実数を入力してください。"); printf("1:"); scanf("%lf",&dx); printf("2:"); scanf("%lf",&dy); printf("二つの実数の差は%fです。\n",diff(dx,dy)); return 0; }