質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.62%

C++ 関数の引数の渡し方や返り値の受け取り方はこれで合っていますか?(レビュー依頼)

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 3,678

opyon

score 991

回答やコメントでアドバイス頂いた皆様ありがとうございました。
色々なことが知れてとても参考になりました。

まとめ

生配列は関数から戻せないクラスなどでは可能
今回の例ではstd::vector
参考情報ではstd::arrayなど

出力の書式はC++で統一する(printfを混ぜない)
std::cout

配列出力などは他言語のfor-eachでスッキリ
range-based for

負の値になりえない要素数はunsignedな型を利用
今回の例ではstd::size_t 変数名

関数から戻り値を返す時に今どきのコンパイラなら
自動判別して無駄なコピーはほぼ発生しない模様
キーワードRVO,NRVO

0で埋める作業のほうが1で埋める作業より高速
今回の例では0埋めしたあとに1を代入する

修正最終

#include <cstdio>    //名前空間に必須
#include <iostream>  //入出力に必須
#include <vector>    //std::vectorに必須
using namespace std; //名前空間指定

vector<int> get_primes(std::size_t n)
{
    // 戻り値用
    vector<int> primes(1, 2);
    // 篩用
    vector<int> sieve(n, 0);

    std::size_t i, j, start, step;
    sieve[2] = 1;

    for (i = 3; i < n; i = i + 2)
    {
        sieve[i] = 1;
    }

    for (i = 3; i < n; i = i + 2)
    {
        if (sieve[i] == 1)
        {
            primes.push_back(i);
            start = i * i;
            step = i * 2;
            for (j = start; j < n; j = j + step)
            {
                sieve[j] = 0;
            }
        }
    }

    // 確認用
    for (auto &&prime : primes)
    {
        cout << prime << " ";
    }
    cout << endl;

    return primes;
}

// 出力
void prt_primes(const vector<int> &primes)
{

    //書き直し(リストのみ)
    int i = 0;
    for (auto &&prime : primes)
    {
        cout << prime << " ";
    }
    cout << endl;
}

int main()
{
    const int n = 10;
    prt_primes(get_primes(n));

    return 0;
}

修正3

初期値0,1の書き換えを逆にしてみました。

#include <cstdio>   //名前空間に必須
#include <iostream> //入出力に必須
#include <vector>   //std::vectorに必須
using namespace std;//名前空間指定

vector<int> get_primes(int n){

    // 戻り値用
    vector<int> primes(1,2);
    // 篩用
    vector<int> sieve(n,0);

    int i,start,step;
    sieve[2] = 1;

    for(i=3;i<n;i=i+2){
        sieve[i] = 1;
    } 

    for(i=3;i<n;i=i+2){
        if (sieve[i] == 1){
            primes.push_back(i);
            start = i*i;
            step = i*2;
            for(int j = start;j<n;j=j+step){
                sieve[j] = 0;
            }
        }
    } 

    // 確認用
    for(auto&& prime : primes) {
        cout << prime << " ";
    }
    cout << endl;

    return primes;
}

// 出力
void prt_primes(const vector<int>& primes){

    //書き直し(リストのみ)
    int i = 0;
    for(auto&& prime : primes) {
        cout << prime << " ";
    }
    cout << endl;
}

int main(){
    const int n = 10;
    prt_primes(get_primes(n));

    return 0;
}

修正2

#include <cstdio>   //名前空間に必須
#include <iostream> //入出力に必須
#include <vector>   //std::vectorに必須
using namespace std;//名前空間指定

vector<int> get_primes(int n){

    // 戻り値用
    vector<int> primes(1,2);
    // 篩用
    vector<int> sieve(n,1);

    int i,start,step;
    sieve[0] = 0;
    sieve[1] = 0;

    for(i=4;i<n;i=i+2){
        sieve[i] = 0;
    } 

    for(i=3;i<n;i=i+2){
        if (sieve[i] == 1){
            primes.push_back(i);
            start = i*i;
            step = i*2;
            for(int j = start;j<n;j=j+step){
                sieve[j] = 0;
            }
        }
    } 

    // 確認用
    for(auto&& prime : primes) {
        cout << prime << " ";
    }
    cout << endl;

    return primes;
}

// 出力
void prt_primes(const vector<int>& primes){

    //書き直し(リストのみ)
    int i = 0;
    for(auto&& prime : primes) {
        cout << prime << " ";
    }
    cout << endl;
}

int main(){
    const int n = 10;
    prt_primes(get_primes(n));

    return 0;
}

修正1

(素数のみのリストが帰るものを作り直す予定ですが取り急ぎここまで。)

#include <cstdio>   //名前空間に必須
#include <iostream> //入出力に必須
#include <vector>   //std::vectorに必須
using namespace std;//名前空間指定

vector<int> get_primes(int n){
    std::size_t;
    vector<int> primes(n,1);
    int i,start,step;

    primes[0] = 0;
    primes[1] = 0;

    for(i=4;i<n;i=i+2){
        primes[i] = 0;
    } 

    for(i=3;i<n;i=i+2){
        if (primes[i] == 1){
            start = i*i;
            step = i*2;
            for(int j = start;j<n;j=j+step){
                primes[j] = 0;
            }
        }
    } 

    // 確認用
    for(int i=0;i<n;i++){
        if (primes[i] == 1){
            cout << i << " ";
        }
    }
    cout << endl;

    return primes;
}

// 出力
void prt_primes(const vector<int>& primes){

    //書き直し(本来は素数のみのリストで良かったかも本題ではないので後回し)
    int i = 0;
    for(auto&& prime : primes) {
        if (prime){
            cout << i << " ";
        }
        i++;
    }
    cout << endl;
}

int main(){
    const int n = 10;
    prt_primes(get_primes(n));

    return 0;
}

こういうことでしょうか???

ちょっとまで自信ないのでコメント頂けると助かります。

#include <cstdio>   //名前空間に必須
#include <iostream> //入出力に必須
#include <vector>   //std::vectorに必須
using namespace std;//名前空間指定

vector<int> get_primes(int n){
    vector<int> primes(n,1);
    int i,start,step;
    n = primes.size();
    primes[0] = 0;
    primes[1] = 0;

    for(i=4;i<n;i=i+2){
        primes[i] = 0;
    } 

    for(i=3;i<n;i=i+2){
        if (primes[i] == 1){
            start = i*i;
            step = i*2;
            for(int j = start;j<n;j=j+step){
                primes[j] = 0;
            }
        }
    } 

    // 確認用
    for(int i=0;i<n;i++){
        if (primes[i] == 1){
            cout << i << " ";
        }
    }
    printf("\n");

    return primes;
}

// 出力
void prt_primes(const vector<int>& primes){
    for(int i=0;i<primes.size();i++){
        if (primes[i] == 1){
            printf("%d ",i);
        }
    }
    printf("\n");
}

int main(){
    const int n = 10;
    prt_primes(get_primes(n));

    return 0;
}

追記

(質問が分かりづらかったようなので追記します。)

例えば他言語ですがPythonなら呼び出し先の関数で配列を生成してそれを戻り値で返せます
下記サンプルでしたらresult配列です。
これと同じことが出来そうにないので質問させていただきました。

C言語だと呼び出し先の関数で配列を生成して戻り値で返せないとなると呼び出し元で生成するのがいいのかなと思ったのですが、間違っていたり何か他に良い方法などあればご教示頂けると助かります。

def calc(n):
    result = []
    for i in range(n):
        result.append(i*i)
    return result

def main():
    print(*calc(10))

main()
# 0 1 4 9 16 25 36 49 64 81

知りたいこと

配列データを関数で計算して処理したい場合は呼び出し元のmain()などで配列を生成して呼び出し先では参照渡し&変数名で受け取る方法が一般的標準的な方法という認識で合っていますか?
まだ勉強を始めて3日目なので今のうちにセオリーのようなものを知っておきたいです。
自分でも検索などしていますが客観的にお気づきの点などありましたらご教示お願いします。

現状

C言語では関数から配列を返り値として戻せないと知るまで直感的に動かせなくて戸惑いました。
下記サンプルではmain()で宣言した配列vector<int> primes(n,1);int get_primes(vector<int> &primes){に渡して計算処理して次にvoid prt_primes(const vector<int> &primes){に渡して出力しています。

10までの素数を出力するサンプルコード

#include <cstdio>   //名前空間に必須
#include <iostream> //入出力に必須
#include <vector>   //std::vectorに必須
using namespace std;//名前空間指定

int get_primes(vector<int> &primes){
    int i,n,start,step;
    n = primes.size();
    primes[0] = 0;
    primes[1] = 0;

    for(i=4;i<n;i=i+2){
        primes[i] = 0;
    } 

    for(i=3;i<n;i=i+2){
        if (primes[i] == 1){
            start = i*i;
            step = i*2;
            for(int j = start;j<n;j=j+step){
                primes[j] = 0;
            }
        }
    } 

    // 確認用
    for(int i=0;i<n;i++){
        if (primes[i] == 1){
            cout << i << " ";
        }
    }
    printf("\n");

    return 0;
}

// 出力
void prt_primes(const vector<int> &primes){
    for(int i=0;i<primes.size();i++){
        if (primes[i] == 1){
            printf("%d ",i);
        }
    }
    printf("\n");
}

int main(){
    const int n = 10;
    vector<int> primes(n,1);
    get_primes(primes);
    prt_primes(primes);

    return 0;
}
2 3 5 7 
2 3 5 7 
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+4

こんにちは。

追記の内容で合ってますよ。

C++は、このあたりが最近非常に進化したのでややこしいです。
ですが、C++の基本はint型等の基本型だけでなく、クラスや構造体をコピーすると中身が全てコピーされます。(代入演算子の働きを同じにできるように設計されていますし、それが推奨されています。)
return インスタンス;も、基本ルールとしてはその内容が戻り先のインスタンスへコピーされます。

ただし、コピーする必要がないことをコンパイラが把握できる時は最適化によりコピーされないケースが非常に多いです。代表的な例としては、ローカル変数はreturn後に破棄されますから、それを戻す際にコピーしなくて済ませる術があることが多く、しかもコンパイラがそれを把握できることも多いので多くの場合コピーされません。

std::vector等のSTLコンテナは、ローカル変数をreturnする際、そのデータ領域はコピーされないと思って概ね大丈夫です。(要素数等の管理領域さえコピーせずに済まされることも少なくないです。)


ところで、

    n = primes.size();

は消し忘れでしょうか?

   primes[0] = 0;
   primes[1] = 0;

はnが2未満の時に危険です。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/18 22:41

    ありがとうございます。
    RVO,(他回答でもNRVO)というキーワード知らなかったので参考になります。
    ざっくり過ぎるかもしれませんがコンパイラが自動的に判断してくれて無駄なコピーはされないということのようですね。
    >はnが2未満の時に危険です。
    そういう判定が必要な時には気をつけます。

    キャンセル

  • 2018/10/18 22:52

    RVOについてはC++17で必ずされることになってなんとcopy/move ctorなくてもいいとなりました。(NRVOは保証なし)

    キャンセル

checkベストアンサー

+3

  • C系の標準入出力であるprintfなどとC++系の標準入出力であるstd::cout、どちらを使うか揃えたほうがいいのでは・・・?
  • get_primesは引数経由でvectorを返すのではなく、単に戻り値として返しましょう。NRVOくらい今どきのコンパイラならやってくれます。
  • prt_primesはrange-based forを使うともう少しスッキリかけます。

追記のコードを見て

  • 負の値になりえない要素数はunsignedな型を利用しましょう。今回ならstd::size_t
  • n = primes.size();は謎です。消しましょう。
  • 0で埋める作業のほうが1で埋める作業より高速なので、0埋めしたあとに1に入れていくほうがいいかもしれません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/18 23:30

    std::size_t i,start,step;
    こういうことでしょうか??

    キャンセル

  • 2018/10/18 23:32

    それもそうかもしれないけどそもそもget_primesの引数

    キャンセル

  • 2018/10/18 23:40

    ありがとうございます。

    キャンセル

+1

・関数の戻り値はどういう意味を持つと想定されてますか?
0しか戻さないようですが
・引数になんで&をつけていますか?
引数にクラスを指定したときにどういう挙動をするのか調べてみては

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/18 21:53

    >std::array
    ありがとうございます。
    ちょっと調べることが増えてきたので順番に調べてみます^^;

    キャンセル

  • 2018/10/18 21:54

    ># &つける意味ないってのもそこに起因してます
    ありがとうございます。

    追記に修正したコード書いてみましたのでもしよろしければご指摘お願いします。

    キャンセル

  • 2018/10/18 22:02

    >クラス、というのは基本そのアドレスがやり取りされます。

    イマイチなにを言いたいのかわからないのですが・・・

    引数経由で返す以上、`get_primes`はlvalue referenceでvectorを受け取っておかなければならないですし、`prt_primes`もcopy/moveコストを削減するためにconst lvalue referenceを用いるというのは何も間違っていないのでは・・・

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.62%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • C++に関する質問
  • C++ 関数の引数の渡し方や返り値の受け取り方はこれで合っていますか?(レビュー依頼)