ポインタ

ポインタを利用して二つの値を交換する

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