4
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

引数のポインタ周り(配列,ベクタ,ベクタのコピー)

Last updated at Posted at 2018-01-21

※この記事では,Ubuntu16.04にインストールしたg++コンパイラで実行しています.

C++で関数にポインタを渡して計算するときに,いつも試行錯誤しながら(エラー出しながら)やっているので,整理して,メモ.

基本

まず,はじめに基本編.int型の受け渡し.

#include <iostream>
#include <vector>
using namespace std;   /// この三行は以下共通

void sum(int n){    /// 値を引数とする
    n = n + 3;
}
void sump(int* n){  // アドレスを引数とする
    *n = *n + 3;    // 値で足し算
}

int main(int argc, char** argv) { // 以降は省略
    int n = 5;
    cout << "0) n= " << n << endl; // n=5
    sum(n);     // 値の足し算
    cout << "1) n= " << n << endl; // 反映されない(n=5のまま)

    sump(&n);   // アドレスで渡す
    cout << "2) n= " << n << endl;// 反映される(n=8)

    int* m; // アドレスで宣言
    m = &n; // nのアドレスをコピー
    cout << "3) m= " << *m << endl;
    sump(m);    // "m"がアドレスを示す
    cout << "4) m= " << *m << endl;
    cout << "5) n= " << n << endl;// nにも反映されている(同じアドレスなので)
    
    return 0; // 以降は省略
}

関数sumは値で受け渡しているのでmain関数では反映なし,sumpのようにアドレスで渡すと反映できます.

配列の要素

つづいて,同じく,sum, sumpを使って,配列の要素について計算.

main.cpp
    int ar[2] = {20, 18};
    cout << "ar[0]= " << ar[0] << endl;
    sum(ar[0]);     // 値の足し算
    cout << "ar[0]= " << ar[0] << endl; // 反映されない

    sump(&ar[0]);   // アドレスで渡す
    cout << "ar[0]= " << ar[0] << endl;
    sump(ar);   // 先頭アドレスを渡す
    cout << "ar[0]= " << ar[0] << endl;
    
    cout << "ar[1]= " << ar[1] << endl;
    sump(ar+1);   // アドレスで渡す
    cout << "ar[1]= " << ar[1] << endl;

配列要素のアドレスの渡し方は,ここでは,"&ar[x]"と"ar+x"の二つを使っています.
"&ar[x]"と関数に渡して,関数の引数としては"(int* n)"と受ける.関数内では,"(int)*n"と使う.この辺でよく混乱します.
ちなみに,アドレス,リスクはあたいと覚えるそうです.あとは,独自ですが,"int* n"とみてnはintのポインタ,"int n"とみてnはintとイメージしています.

ベクタの要素

つづいて,sum, sumpを使って,ベクタの要素について計算.

main.cpp
    std::vector<int> vec(2);
    vec[0] = 111;
    vec[1] = 121;
    cout << "vec[0]= " << vec[0] << endl;
    sum(vec[0]);
    cout << "vec[0]= " << vec[0] << endl; // 反映されない
    sump(&vec[0]);   // アドレスで渡す
    cout << "vec[0]= " << vec[0] << endl;
    sump(vec.data());   // 先頭アドレスを渡す
    cout << "vec[0]= " << vec[0] << endl;
    cout << "vec[1]= " << vec[1] << endl;
    sump(vec.data()+1);   // アドレスを渡す
    cout << "vec[1]= " << vec[1] << endl;

ベクタ要素のアドレスの渡し方は,ここでは,"&vec[x]"と"vec.data()+x"の二つを使っています.

配列・ベクタ

次に,配列,ベクタごとの計算.新たに関数を定義して,

void sumarray(int ar[], int length){ // 配列の長さは自分で指定
    for(int i = 0; i < length; ++i) ar[i] += 3;
}
void sumvec(vector<int> &vec){
    for(int i = 0; i < vec.size(); ++i) vec[i] += 3;
}
int main(int argc, char** argv) {
    // 初期化はそのまま

    cout << "ar[0]= " << ar[0] << " ar[1]= " << ar[1] << endl;
    sumarray(ar, 2); // 先頭のアドレスを渡す
    cout << "ar[0]= " << ar[0] << " ar[1]= " << ar[1] << endl;
    cout << "vec[0]= " << vec[0] << " vec[1]= " << vec[1] << endl;
    sumvec(vec); // 先頭のアドレスを渡す
    cout << "vec[0]= " << vec[0] << " vec[1]= " << vec[1] << endl;

配列ではsump(ar)もsumarray(ar, 2)もOKですが,ベクタではsump(vec)はダメで,sumvec(vec.data())もダメです.引数の指定が違うので.

ベクタの場合は,イテレータを使う方法もあります.

void sumvecitr(vector<int> &vec){
    vector<int>::iterator itrvec = vec.begin(); // イテレータ
    for(; itrvec != vec.end(); ++itrvec){
        *itrvec += 3;
    }
}
//
sumvecitr(vec);
//

で計算できます.

####ベクタ(関数内コピーあり)
やっと本題.関数内でベクタをコピーして計算する場合.

void sum_ncpvec(vector<int> &vec){
    vector<int> cpvec = vec; // コピー
    for(int i = 0; i < cpvec.size(); ++i){
        cpvec[i] += 3;
    }
}
void sum_cpvec(vector<int> &vec){
    vector<int>* cpvec = &vec; // アドレスコピー
    for(int i = 0; i < (*cpvec).size(); ++i){
        (*cpvec)[i] += 3;
    }
}

int main(int argc, char** argv) {
    // 初期化はそのまま
    sum_ncpvec(vec);
    cout << "vec[0]= " << vec[0] << " vec[1]= " << vec[1] << endl;  // 反映されない
    sum_cpvec(vec);
    cout << "vec[0]= " << vec[0] << " vec[1]= " << vec[1] << endl;

当然ですが,sum_ncpvecは関数内でのコピー・計算なので,mainでは反映されません.反映したい場合は,アドレス(&vec)をvector<int>* cpvecにコピーする必要があります.関数内の計算では,(*cpvec)をvector<int>として扱います.

コピーしなくてもよい場合は多いと思いますが,vector<クラス>を扱っていて,自作クラス内のメンバ変数を読んだりすると,どんどん文が長くなるのでコピーしたら嵌ったので,整理しました.

####参考
直接的な引用はないですが,参照はこちら.
C言語の引数に多次元配列を渡す
C++ 動的配列クラス std::vector 入門 - イテレータ
新C言語入門 ビギナー編

4
8
0

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
4
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?