LoginSignup
5
9

More than 5 years have passed since last update.

C言語でポインタ渡し・ポインタ演算をいろいろ試した

Last updated at Posted at 2018-12-30

はじめに

ポインタ渡し・ポインタ演算の復習というか勉強のためにいろいろ書いて試したことがあるので,それを公開しておきます.
自分の勉強ノートとしてと,初心者向けに「こう書くとこうなる」の例を紹介できればという記事です.

一連の関数へのポインタ渡しの話の最後の記事という位置付けでもあります.

なお,以下の説明にはあまり自信がないので,鵜呑みにされるとまずいかも知れないですし,よく分かってらっしゃる方に「合ってる」「間違ってる」等コメントいただけると幸いです.

まず変数のポインタ渡しをしてみた

まずは簡単と思われる方から.配列をあとでやります.

書いてみたコードはこれです.

sample1.c
#include <stdio.h>

// int型変数のアドレスを受ける
void func1(int* pt){
    *pt = 5; // ポインタが指す先の変数の中身を5に
}

// int型ポインタのアドレスを受ける
void func2(int** pt){
    **pt = 6; // ポインタが指す先のポインタが指す先の変数の中身を6に
}

int main(void){
    int a = 0;
    func1(&a); // 変数のアドレスを渡す
    printf("call func1(&a)\n");
    printf("a=%d\n\n",a);

    int* b = &a;
    func2(&b); // 変数のアドレスを格納したポインタのアドレスを渡す
    printf("call func2(&b)\n");
    printf("a=%d *b=%d\n\n", a, *b);

    func1(b); // 変数のアドレスを格納したポインタを渡す
    printf("call func1(b)\n");
    printf("a=%d *b=%d\n\n", a, *b);

    return 0;
}
output1
$ ./sample1
call func1(&a)
a=5

call func2(&b)
a=6 *b=6

call func1(b)
a=5 *b=5

コードとコメントを見てもらえればだいたいわかってもらえるでしょうか.

main()内の最初のfunc1()にはptに変数aのアドレスを渡していて,func()内で*ptと書くことで変数aの中身を操作できます.

func2()では,ptがポインタbのアドレスを格納し,ポインタbが変数aのアドレスを格納しているので,*ptbの中身を,**ptaの中身を操作できます.

最後のfunc1()にはポインタbを渡すことでbが格納しているaのアドレスを渡しています.

次に配列をポインタ渡ししてみた,そしてポインタ演算もしてみた

配列についてはこんなコードを試してみました.

sample2.c
#include <stdio.h>

// char型変数のアドレスを受ける
void func1(char* pt){
    pt[0] = '1';
    pt[1] = '2';
    pt[2] = '3';
}

// char型ポインタのアドレスを受ける
void func2(char** pt){
    // ポインタ演算
    **pt = '4';
    *(*pt + 1) = '5';
    *(*pt + 2) = '6';
}

// char型ポインタのアドレスを受ける
void func3(char** pt){
    // 添え字
    (*pt)[0] = '7';
    (*pt)[1] = '8';
    (*pt)[2] = '9';
}


int main(void){
    char a[4] = "000";
    func1(a); // 配列の先頭アドレスを渡す
    printf("call func1(a)\n");
    printf("a=%s\n\n",a);

    char* b = a;
    func2(&b); // 配列の先頭アドレスを格納したポインタのアドレスを渡す
    printf("call func2(&b)\n");
    printf("a=%s b=%s\n\n", a, b);

    func1(b); // 配列の先頭アドレスを格納したポインタを渡す
    printf("call func1(b)\n");
    printf("a=%s b=%s\n\n", a, b);

    func3(&b); // 配列の先頭アドレスを格納したポインタのアドレスを渡す
    printf("call func3(&b)\n");
    printf("a=%s b=%s\n\n", a, b);

    return 0;
}
output2
$ ./sample2
call func1(a)
a=123

call func2(&b)
a=456 b=456

call func1(b)
a=123 b=123

call func3(&b)
a=789 b=789

今度は配列なのでaはchar型配列の先頭アドレスになります.
なのでポインタに代入する際,先ほどはb = &aでしたが,今度はb = aになっています.

コードとコメントから「こう書くとこうなる」を感じ取ってもらえるでしょうか.

ちなみに,func2()func3()内の

func2()
*(*pt + 1) = '5';

func3()
(*pt)[1] = '8';

()を書かないと,前者はコンパイル時にエラーになり,後者は実行時にコアダンプします.
演算に優先順位があり,それが変わってしまうからです.

()を書かなかった場合の優先順位を()で表現するとおそらくこうです.

func2()
(**pt) + 1 = '5'; // 代入する式になっていない
func3()
*(pt[1]) = '8'; // ptに2番目の要素はない

func3()ptについて,添え字が0*pt[0]だけは()無しでも大丈夫です.

まとめ

ポインタについていろいろな例を見てきました.
何かしら理解が深まったり発見があったりすれば幸いです.

ちなみに,ポインタ型の宣言はint* b;int *b;の2通りの書き方がありますが,僕は前者が好きです.

以前は後者で書いていたのですが,どうも間接演算子の**pt = 5とかの*)と混同して覚えてしまっているような気がして,それからは前者で書いて自分に別物だと言い聞かせています.どちらで書いても構いませんが,別物だということを覚えておいてください.

5
9
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
9