int arr[5]
関数に渡される配列がありますfillarr(int arr[])
:
int fillarr(int arr[])
{
for(...);
return arr;
}
- どうすればその配列を返すことができますか?
- ポインターを返したとしたら、どのように使用しますか?
std::vector
。
5
関数のシグネチャでは、コンパイラによって破棄されます。
int arr[5]
関数に渡される配列がありますfillarr(int arr[])
:
int fillarr(int arr[])
{
for(...);
return arr;
}
std::vector
。
5
関数のシグネチャでは、コンパイラによって破棄されます。
回答:
この場合、配列変数arr
は、暗黙的な変換によって、メモリ内の配列のブロックの先頭へのポインターとして実際に処理することもできます。あなたが使用しているこの構文:
int fillarr(int arr[])
一種の構文糖です。あなたは本当にこれに置き換えることができ、それでも動作します:
int fillarr(int* arr)
したがって、同じ意味で、関数から返したいのは、実際には配列の最初の要素へのポインタです。
int* fillarr(int arr[])
また、通常の配列と同じように使用できます。
int main()
{
int y[10];
int *a = fillarr(y);
cout << a[0] << endl;
}
array
そして&array
多くのケースで交換可能です。
a
ではint a[10]
ありますint[10]
。あなたが言うことができることは、それらの最初の要素へのポインターへの配列「崩壊」です。(これは暗黙の配列からポインターへの変換です。)次に、あなたの答えは私のものに沿ったものになります。配列、配列からポインターへの変換、およびポインターを区別するために回答を編集する場合、それらは同じコア情報を持ち、あなたが最初だったので、私の回答を削除します。
C ++関数は、Cスタイルの配列を値で返すことはできません。最も近いのは、ポインタを返すことです。さらに、引数リストの配列型は、単純にポインターに変換されます。
int *fillarr( int arr[] ) { // arr "decays" to type int *
return arr;
}
引数と戻り値の配列参照を使用して、減衰を防ぐことにより、これを改善できます。
int ( &fillarr( int (&arr)[5] ) )[5] { // no decay; argument must be size 5
return arr;
}
BoostまたはC ++ 11では、参照渡しはオプションであり、構文はそれほど難しくありません。
array< int, 5 > &fillarr( array< int, 5 > &arr ) {
return arr; // "array" being boost::array or std::array
}
array
テンプレートは、単純に発生struct
しますが、オブジェクト指向のセマンティクスを適用し、まだ配列のオリジナルのシンプルさを維持することができますので、含むCスタイルの配列を。
typedef int array[5]; array& foo();
ただしint (&foo())[5] { static int a[5] = {}; return a; }
、これを記述したい場合はtypedefも必要ありませんint (&foo( int (&a)[5] ))[5] { return a; }
。質問の例は次のようになります。簡単ですね。
error: function returning array is not allowed
typedef以外の構文で外側の括弧を省略した場合に発生するコモーメッセージから誤解を受けました。幸いなことに、今日、私は別の質問の右左規則を確認し、正しいことを構築することができました...可能だとあなたが見た後...コード:vPを与えることを見る前に。
C ++ 11では、を返すことができますstd::array
。
#include <array>
using namespace std;
array<int, 5> fillarr(int arr[])
{
array<int, 5> arr2;
for(int i=0; i<5; ++i) {
arr2[i]=arr[i]*2;
}
return arr2;
}
(...) you can consider the array returned arr2, totally another array (...)
$ 8.3.5 / 8州-
「関数は、型の戻り値の型の配列または関数を持たないものとします。ただし、戻り値の型のポインターまたはそのようなものへの参照が含まれる場合があります。関数へのポインターの配列があっても、関数の配列は存在しません。」
int (&fn1(int (&arr)[5]))[5]{ // declare fn1 as returning refernce to array
return arr;
}
int *fn2(int arr[]){ // declare fn2 as returning pointer to array
return arr;
}
int main(){
int buf[5];
fn1(buf);
fn2(buf);
}
int
、配列ではなくへのポインタを返します。
答えは、その関数をどのように使用するかによって多少異なります。最も単純な答えとして、配列の代わりに本当に必要なのはベクトルであると決定しましょう。退屈で通常のポインタに格納できる通常の値のように、すべての世界を探すことができるので、ベクトルは素晴らしいです。他のオプションとその理由を後で説明します。
std::vector<int> fillarr( std::vector<int> arr ) {
// do something
return arr;
}
これにより、期待どおりの結果が得られます。利点は、std::vector
すべてがクリーンに処理されるようにすることです。欠点は、配列が大きい場合、非常に大量のデータをコピーすることです。実際には、配列のすべての要素を2回コピーします。最初に、関数がパラメーターとして使用できるようにベクターをコピーします。次に、それを再度コピーして、呼び出し元に返します。ベクトルの管理を自分で処理できれば、かなり簡単に処理を実行できます。(呼び出し側がより多くの計算を行うために何らかの種類の変数に保存する必要がある場合、3回目にコピーする可能性があります)
あなたが本当にやろうとしているのは、単にコレクションにデータを追加することです。コレクションの新しいインスタンスを返す特別な理由がない場合は、そうしないでください。このようにできます
void fillarr(std::vector<int> & arr) {
// modify arr
// don't return anything
}
この方法では、関数のプライベートコピーではなく、関数に渡された配列への参照を取得します。パラメータに加えた変更は、呼び出し元に表示されます。必要に応じてそれへの参照を返すこともできますが、これは実際には素晴らしいアイデアではありません。これは、渡したものとは異なるものを取得していることを意味しているためです。
コレクションの新しいインスタンスが本当に必要であるが、それをスタック(およびそれに伴うすべてのコピー)に配置したくない場合は、そのインスタンスの処理方法について何らかの契約を作成する必要があります。これを行う最も簡単な方法は、スマートポインターを使用することです。これにより、誰かがそのインスタンスを保持している限り、参照されているインスタンスが保持されます。範囲外になると、問題なく削除されます。それはこのようになります。
std::auto_ptr<std::vector<int> > fillarr( const std::vector<int> & arr) {
std::auto_ptr<std::vector<int> > myArr(new std::vector<int>);
// do stuff with arr and *myArr
return myArr;
}
ほとんどの場合、使用*myArr
方法は、単純なバニラベクトルを使用する方法と同じです。この例では、const
キーワードを追加してパラメーターリストを変更します。これで、コピーせずに参照を取得できますが、変更することはできないため、呼び出し元は、関数が取得する前と同じであることを認識しています。
これらすべてはうねりですが、慣用的なc ++がコレクション全体で動作することはほとんどありません。より一般的には、これらのコレクションに対してイテレータを使用します。それはこのように見えるでしょう
template <class Iterator>
Iterator fillarr(Iterator arrStart, Iterator arrEnd) {
Iterator arrIter = arrStart;
for(;arrIter <= arrEnd; arrIter++)
;// do something
return arrStart;
}
このスタイルを見るのに慣れていない場合は、少し奇妙に見えます。
vector<int> arr;
vector<int>::iterator foo = fillarr(arr.begin(), arr.end());
fooは変更されたの先頭を「指す」ようになりましたarr
。
これの本当に素晴らしいところは、ベクトルでもプレーンC配列や他の多くのタイプのコレクションと同じようにうまく機能することです。たとえば、
int arr[100];
int *foo = fillarr(arr, arr+100);
これは今、この質問の他の場所で示されている単純なポインタの例に非常によく似ています。
&
シンボルがタイプした後に表示されなければなりません:void fillarr(std::vector<int> & arr)
この:
int fillarr(int arr[])
実際には次と同じように扱われます:
int fillarr(int *arr)
本当に配列を返したい場合は、その行を次のように変更できます
int * fillarr(int arr[]){
// do something to arr
return arr;
}
実際には配列を返していません。配列アドレスの先頭へのポインタを返しています。
ただし、配列を渡すときは、ポインタのみを渡すことを覚えておいてください。したがって、配列データを変更すると、実際には、ポインターが指しているデータが変更されます。したがって、配列を渡す前に、変更した結果がすでに外側にあることを理解する必要があります。
例えば
int fillarr(int arr[]){
array[0] = 10;
array[1] = 5;
}
int main(int argc, char* argv[]){
int arr[] = { 1,2,3,4,5 };
// arr[0] == 1
// arr[1] == 2 etc
int result = fillarr(arr);
// arr[0] == 10
// arr[1] == 5
return 0;
}
このような長さをfillarr関数に入れることを検討することをお勧めします。
int * fillarr(int arr[], int length)
そうすれば、長さがどのようなものであっても、配列をその長さに満たすことができます。
実際にそれを適切に使用するため。このようなことをしてください:
int * fillarr(int arr[], int length){
for (int i = 0; i < length; ++i){
// arr[i] = ? // do what you want to do here
}
return arr;
}
// then where you want to use it.
int arr[5];
int *arr2;
arr2 = fillarr(arr, 5);
// at this point, arr & arr2 are basically the same, just slightly
// different types. You can cast arr to a (char*) and it'll be the same.
配列をデフォルト値に設定するだけの場合は、組み込みのmemset関数の使用を検討してください。
次のようなもの:memset((int *)&arr、5、sizeof(int));
でも、私はその話題についています。あなたはC ++を使っていると言います。stlベクトルの使用をご覧ください。コードはより堅牢になる可能性があります。
チュートリアルはたくさんあります。これらは、それらを使用する方法のアイデアを与えるものです。 http://www.yolinux.com/TUTORIALS/LinuxTutorialC++STL.html
std::copy
以上を使用するとmemset
、より安全で簡単になります。(そして、より速くなくても同じくらい速く。)
関数から配列を返すには、その配列を構造体で定義してみましょう。だからこんな感じ
struct Marks{
int list[5];
}
次に、タイプ構造の変数を作成します。
typedef struct Marks marks;
marks marks_list;
次の方法で配列を関数に渡し、それに値を割り当てることができます。
void setMarks(int marks_array[]){
for(int i=0;i<sizeof(marks_array)/sizeof(int);i++)
marks_list.list[i]=marks_array[i];
}
配列を返すこともできます。配列を返すには、関数の戻り値の型は構造体型、つまりマークでなければなりません。これは、実際には配列を含む構造体を渡すためです。したがって、最終的なコードは次のようになります。
marks getMarks(){
return marks_list;
}
これはかなり古い質問ですが、答えはたくさんあるので、2セントで説明しますが、すべての可能な方法を明確かつ簡潔な方法で示していません(簡潔なビットについてはわかりません。少し手に負えない。TL; DR😉)。
OPが、コピーせずに渡された配列を、呼び出し元に直接渡して別の関数に渡してコードをきれいに見せるための手段として返したいと思っていると思います。
ただし、このような配列を使用することは、それをポインターに減衰させ、コンパイラーに配列のように処理させることです。これは、5つの要素があることを期待する関数などの配列を渡す場合、微妙なバグを引き起こす可能性がありますが、呼び出し元は実際には他の数値を渡します。
これをよりよく処理できる方法がいくつかあります。std::vector
orを渡しますstd::array
(std::array
質問が行われたときに2010年頃かどうかはわかりません)。その後、オブジェクトをコピー/移動せずに、オブジェクトを参照として渡すことができます。
std::array<int, 5>& fillarr(std::array<int, 5>& arr)
{
// (before c++11)
for(auto it = arr.begin(); it != arr.end(); ++it)
{ /* do stuff */ }
// Note the following are for c++11 and higher. They will work for all
// the other examples below except for the stuff after the Edit.
// (c++11 and up)
for(auto it = std::begin(arr); it != std::end(arr); ++it)
{ /* do stuff */ }
// range for loop (c++11 and up)
for(auto& element : arr)
{ /* do stuff */ }
return arr;
}
std::vector<int>& fillarr(std::vector<int>& arr)
{
for(auto it = arr.begin(); it != arr.end(); ++it)
{ /* do stuff */ }
return arr;
}
ただし、C配列を使用する場合は、配列内の項目数の情報を保持するテンプレートを使用してください。
template <size_t N>
int(&fillarr(int(&arr)[N]))[N]
{
// N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
for(int* it = arr; it != arr + N; ++it)
{ /* do stuff */ }
return arr;
}
それを除いて、それはお尻が醜く、非常に読みにくいです。私は今、2010年にはなかったものを支援するために何かを使用します。これは関数ポインターにも使用します。
template <typename T>
using type_t = T;
template <size_t N>
type_t<int(&)[N]> fillarr(type_t<int(&)[N]> arr)
{
// N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
for(int* it = arr; it != arr + N; ++it)
{ /* do stuff */ }
return arr;
}
これは、1つは、この作り、それがために期待するタイプの移動はるかに読みやすくします。もちろん、5つの要素以外を使用する予定がない場合は、テンプレートを使用する必要はありません。そのため、当然、ハードコードすることができます。
type_t<int(&)[5]> fillarr(type_t<int(&)[5]> arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
私が言ったように、type_t<>
この質問がなされたとき、私のトリックはうまくいかなかったでしょう。当時あなたが望んでいた最高のものは、構造体で型を使用することでした:
template<typename T>
struct type
{
typedef T type;
};
typename type<int(&)[5]>::type fillarr(typename type<int(&)[5]>::type arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
見た目はかなり醜くなりますが、少なくとも読みやすいtypename
ですが、コンパイラによってはオプションであったため、次のような結果になりました。
type<int(&)[5]>::type fillarr(type<int(&)[5]>::type arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
そしてもちろん、私のヘルパーを使用するのではなく、特定のタイプを指定することもできます。
typedef int(&array5)[5];
array5 fillarr(array5 arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
当時、自由な機能std::begin()
とstd::end()
存在していなかったが、しかし容易に実装されている可能性が。これは、C配列では意味があるがポインタでは意味がないため、より安全な方法で配列を反復できるようになります。
配列へのアクセスに関しては、同じパラメーター型を取る別の関数に渡すか、またはエイリアスを作成することができます(スコープに元のオブジェクトがあるため、あまり意味がありません)。配列参照へのアクセスは、元の配列へのアクセスと同じです。
void other_function(type_t<int(&)[5]> x) { /* do something else */ }
void fn()
{
int array[5];
other_function(fillarr(array));
}
または
void fn()
{
int array[5];
auto& array2 = fillarr(array); // alias. But why bother.
int forth_entry = array[4];
int forth_entry2 = array2[4]; // same value as forth_entry
}
要約すると、配列を繰り返し処理する場合は、配列がポインタに分解されないようにすることをお勧めします。コンパイラーが足元から自分を撃たれるのを防ぎ、コードが読みにくくなるため、これは単に悪い考えです。コンパイラは、そうしないようにする十分な理由がない限り、型をできる限り長く保つことで、コンパイラが役立つように常に努めてください。
ああ、そして完全を期すために、ポインターに分解することを許可できますが、これにより、配列が保持する要素の数から配列が分離されます。これはC / C ++で多く行われ、通常は配列の要素数を渡すことで軽減されます。ただし、誤って要素の数に誤った値を渡した場合、コンパイラーは役に立ちません。
// separate size value
int* fillarr(int* arr, size_t size)
{
for(int* it = arr; it != arr + size; ++it)
{ /* do stuff */ }
return arr;
}
サイズを渡す代わりに、配列の終わりを過ぎたところを指す終了ポインターを渡すことができます。これは、開始ポインタと終了ポインタを取るstdアルゴリズムに近いものを作成するのに役立ちますが、返されるのは覚えておかなければならないものだけです。
// separate end pointer
int* fillarr(int* arr, int* end)
{
for(int* it = arr; it != end; ++it)
{ /* do stuff */ }
return arr;
}
または、この関数は5つの要素しかとらないことを文書化し、関数のユーザーが愚かなことをしないようにできます。
// I document that this function will ONLY take 5 elements and
// return the same array of 5 elements. If you pass in anything
// else, may nazal demons exit thine nose!
int* fillarr(int* arr)
{
for(int* it = arr; it != arr + 5; ++it)
{ /* do stuff */ }
return arr;
}
戻り値は元の型を失い、ポインターに劣化していることに注意してください。このため、アレイをオーバーランしないようにする必要があります。
std::pair<int*, int*>
beginとendに使用できるを渡し、それを渡すことができますが、実際には配列のように見えなくなります。
std::pair<int*, int*> fillarr(std::pair<int*, int*> arr)
{
for(int* it = arr.first; it != arr.second; ++it)
{ /* do stuff */ }
return arr; // if you change arr, then return the original arr value.
}
void fn()
{
int array[5];
auto array2 = fillarr(std::make_pair(&array[0], &array[5]));
// Can be done, but you have the original array in scope, so why bother.
int fourth_element = array2.first[4];
}
または
void other_function(std::pair<int*, int*> array)
{
// Can be done, but you have the original array in scope, so why bother.
int fourth_element = array2.first[4];
}
void fn()
{
int array[5];
other_function(fillarr(std::make_pair(&array[0], &array[5])));
}
面白いことに、これはstd::initializer_list
機能(c ++ 11)と非常によく似ていますが、このコンテキストでは機能しません。
template<typename T, size_t N>
using ARR_REF = T (&)[N];
template <typename T, size_t N>
ARR_REF<T,N> ArraySizeHelper(ARR_REF<T,N> arr);
#define arraysize(arr) sizeof(ArraySizeHelper(arr))
ソース:https : //www.tutorialspoint.com/cplusplus/cpp_return_arrays_from_functions.htm
C ++では、配列全体を関数の引数として返すことはできません。ただし、インデックスなしで配列の名前を指定することにより、配列へのポインターを返すことができます。
int * myFunction() { . . . }
これらのルールを現在の質問に適用すると、次のようにプログラムを作成できます。
# include <iostream> using namespace std; int * fillarr( ); int main () { int *p; p = fillarr(); for ( int i = 0; i < 5; i++ ) cout << "p[" << i << "] : "<< *(p + i) << endl; return 0; } int * fillarr( ) { static int arr[5]; for (int i = 0; i < 5; ++i) arr[i] = i; return arr; }
出力は次のようになります。
p[0]=0
p[1]=1
p[2]=2
p[3]=3
p[4]=4
実際に、関数内で配列を渡すと、元の配列へのポインターが関数パラメーターで渡されるため、その関数内の配列に加えられた変更は、実際には元の配列に対して行われます。
#include <iostream>
using namespace std;
int* func(int ar[])
{
for(int i=0;i<100;i++)
ar[i]=i;
int *ptr=ar;
return ptr;
}
int main() {
int *p;
int y[100]={0};
p=func(y);
for(int i=0;i<100;i++)
cout<<i<<" : "<<y[i]<<'\n';
}
それを実行すると、変更が表示されます
y
自体はそれ自体のコピーとして渡されますが、これはポインターであるため、配列を直接操作します。回答を編集してください。
type []を戻り値として定義するだけです:
private string[] functionReturnValueArray(string one, string two)
{
string[] x = {one, two};
x[0] = "a";
x[1] = "b";
return x;
}
。。。関数呼び出し:
string[] y;
y = functionReturnValueArray(stringOne, stringTwo)