C言語で関数の引数にポインタを渡す【ポインタの値渡し】

330, 2021-09-25

目次

関数の引数にポインタを渡す方法

C言語の関数の引数にポインタを渡す方法を解説します。
関数の引数にポインタを使えるといろいろな処理のバリエーションが増えます。
たとえば関数呼び出し側の変数に関数の結果を保存するなどの処理も可能です。
この記事では具体的に関数の引数にポインタを渡す方法を見ていきたいと思います。

関連記事
C言語の文字列を初期化する方法: 文字配列、文字列ポインタの初期化
C言語の配列の宣言方法: 配列、可変長配列、内部結合な配列、外部結合な配列、ポインタ配列
C言語の構造体のポインタの使い方
C言語の文字列のポインタの使い方
C言語の関数ポインタの使い方

ポインタ変数とアドレス値の定義

最初にポインタ変数アドレス値の用語の定義です。
ポインタ変数は変数のアドレス値を保存する変数
アドレス値は変数のメモリ上のアドレスのことを言います。

関数にポインタを渡す場合、これらの用語の把握が一番重要です。

たとえば関数呼び出し時に、呼び出し側で取り出すのが変数のアドレス値です。
そのアドレス値を関数の引数(ポインタ)に渡します。

これらの用語を意識するようにしてください。

関数の引数にポインタを使うメリット・デメリットは?

関数の引数にポインタを使うメリットは↓になります。

  • 設計がシンプルになる

  • 処理が高速になる

逆にデメリットは↓になります。

  • 不正なポインタでセグフォする可能性がある

関数の引数にポインタを使うと、関数などの設計がシンプルになります。
これはポインタの柔軟性によるものです。
またポインタを使うと処理が高速になります。
これはコピーよりポインタを使ったほうが早いからです。

具体的には構造体を使った関数が考えられます。
構造体変数に処理の結果を保存したい場合、関数に構造体のポインタを渡します。
そうすると関数はそのポインタから構造体のメンバにアクセスできます。
こうすることで設計がシンプルになり、処理が高速になります。

関数の引数にポインタを使う場合、設計はシンプルになり処理は高速になりますが、まさに至れり尽くせりですね。
ただし不正なポインタを渡した場合は解決しづらいバグになる場合もありますので注意が必要です。

関数の引数をポインタで定義する方法

それでは関数の引数をポインタで定義してみましょう。
実際のC言語のコードを紹介します。

int型のポインタを引数にする

int型のポインタを関数の引数にする場合は↓のようにします。

void f1(int *arg) {
}

f1という関数にはint型のポインタargが引数になっています。
これでこの関数にはint型の変数のアドレス値を渡すことができます。

文字列のポインタを引数にする

const char型のポインタを関数の引数にするには↓のようにします。

void f2(const char *arg) {
}

f2と言う関数はconst char型のポインタargが引数になっています。
これでC言語の文字列をこの関数に渡すことが出来ます。

複数のポインタを引数にする

複数のポインタを引数にする場合は例えば↓のような関数が考えられます。

void f3(int *arg1, double *arg2, const char *arg3) {
}

↑の場合、arg1int型のポインタ、arg2double型のポインタです。
arg3const char型のポインタになります。

関数に変数のアドレスを渡す

ポインタの引数を持った関数に変数のアドレス値を渡す方法を見てみます。
&(アンパサンド)を使って変数のアドレス値を取り出し、そのアドレス値を関数に渡します。
具体的に見ていきます。

アンパサンドで変数のアドレス値を取り出す

変数の頭に&(アンパサンド)を付けると、その変数のアドレス値を取り出すことができます。
たとえばint型の変数aがあって、その変数aのアドレス値を取り出したい場合は↓のようにします。

    int a = 1;

    printf("%p\n", &a);
    // 000000000062FE1C

↑では変数aのアドレス値を取り出して、printf()関数に渡しています。
書式指定子%pでアドレス値を出力すると↑のような結果になります。
(結果は環境によって変わります)

アドレス値を関数の引数に渡す

では&(アンパサンド)で取り出した変数のアドレス値を関数の引数に渡してみます。
関数funcがあり、その関数の引数にint型の変数のアドレス値を渡します。

void func(int *a) {
}

int main(void) {
    int a = 1;

    func(&a);

    return 0;
}

↑のようにすると関数funcint型の変数aのアドレス値を渡すことができます。

関数にポインタ変数を渡す

では次に関数にポインタ変数を渡してみましょう。
ポインタ変数は普通に参照するとポインタ変数に保存されているアドレス値が参照されます。
そのためポインタ変数を関数の引数に渡すだけで関数にアドレス値を渡すことが出来ます。
具体的に見ていきます。

int型のポインタ変数を渡す

int型のポインタ変数を関数に渡す例です。

void func(int *arg) {
}

int main(void) {
    int a = 1;
    int *p = &a;

    func(p);

    return 0;
}

↑の場合、main関数内のポインタ変数pには変数aのアドレス値が入っています。
そのポインタ変数pを関数funcに渡しています。

文字列のポインタ変数を渡す

文字列のポインタ(const char型のポインタ)変数を関数に渡す例です。

void func(const char *arg) {
}

int main(void) {
    const char *a = "hi";

    func(a);

    return 0;
}

C言語の文字列の場合は変数はポインタか配列になります。
そのためそのまま関数の引数に渡すことができます。

複数のポインタ変数を渡す

複数のポインタ変数を関数に渡したい場合は↓のようなコードが考えられます。

void func(int *a, double *b, const char *c) {
}

int main(void) {
    int a = 1;
    int *ap = &a;
    double b = 2.3;
    double *bp = &b;
    const char *c = "hi";

    func(ap, bp, c);

    return 0;
}

↑の場合、関数funcint型のポインタである引数adouble型のポインタである引数bを持っています。
またconst char型の文字列のポインタcも持っています。
これらの引数にそれぞれ同じ型のポインタ変数を渡しています。

ポインタの引数を関数内で使う方法

ポインタの引数を関数内で参照するにはどうしたらいいのでしょうか?
ポインタを通して変数の値を書き換えるには?
具体的に見ていきたいと思います。

アスタリスクで実体化して参照する

*(アスタリスク)でポインタを参照することでポインタを通して変数の値を参照できます。
↓の場合を見てみます。

#include <stdio.h>

void show(int *p) {
    printf("%d\n", *p);  // 1
}

int main(void) {
    int a = 1;

    show(&a);

    return 0;
}

main関数内の変数aのアドレスをshow()関数に渡しています。
このshow()関数内ではポインタpをアスタリスクで実体化して変数aの値を参照しています。
その結果をprintf()で出力しています。
結果は「1」になります。

アスタリスクで実体化して代入する

*(アスタリスク)でポインタ変数を参照し、そこに値を代入すれば、関数の呼び出し側の変数の値を書き変えることが可能です。

#include <stdio.h>

void change(int *p) {
    *p = 2;
}

int main(void) {
    int a = 1;

    change(&a);

    printf("%d\n", a);  // 2

    return 0;
}

↑の場合、main関数内の変数aのアドレス値をchange()関数に渡しています。
change()関数は内部でポインタp*で参照し、値2を代入しています。
こうすることでmain関数側の変数aの値が2に書き変わります。
printf()の結果は「2」になります。

関数の引数にポインタを使った具体例

関数の引数にポインタを使う処理の具体例を紹介します。

1~10の合計を計算する関数sum

「1~10」の間の値の合計を計算する関数sum()です。

#include <stdio.h>

void sum(int *result, int beg, int end) {
    *result = 0;
    for (int i = beg; i <= end; i += 1) {
        *result += i;
    }
}

int main(void) {
    int result;

    sum(&result, 1, 10);

    printf("result[%d]\n", result);  // 55

    return 0;
}

sum()を使うにはまず呼び出し側で結果を保存する変数(result)を定義しておきます。
そしてその変数のアドレス値をsum()関数の第1引数に渡します。
sum()関数は内部でbegendの間だけfor文を回し、そのカウント変数をポインタ変数resultに加算していきます。
sum関数の呼び出し側で変数resultの値を出力します。
結果は「55」になります。

構造体のメンバを設定する関数

関数の引数には構造体のポインタも使うことができます。
構造体のポインタを使えば、関数内で構造体のメンバにアクセスできます。

#include <stdio.h>

struct Animal {
    int age;
    double weight;
};

void set_animal_params(struct Animal *animal, int age, double weight) {
    animal->age = age;
    animal->weight = weight;
}

int main(void) {
    struct Animal animal;

    set_animal_params(&animal, 20, 32.1);

    printf("%d %f\n", animal.age, animal.weight);  // 20 32.100000

    return 0;
}

↑のset_animal_params()関数は構造体struct Animalのメンバ(age, weight)を変更する関数です。
set_animal_params()の第1引数に構造体変数のアドレス値を渡しています。

このような設計で構造体を扱うのはC言語では非常にポピュラーな方法です。
代表例としてはfopen()系の関数などが挙げられます。

おわりに

今回はC言語の関数の引数にポインタを使う方法を解説しました。
関数とポインタを組み合わせるとC言語では以上のような便利な処理が可能になります。
ぜひマスターしておきたいところですね。

関数とポインタはマブダチ

俺らの相性抜群だぜ