C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ツリー一括表示

string配列を関数に渡したい /marusa (20/07/28(Tue) 15:04) #95392
Re[1]: string配列を関数に渡したい /Hongliang (20/07/28(Tue) 15:23) #95393
│├ Re[2]: string配列を関数に渡したい /ぶなっぷ (20/07/28(Tue) 15:35) #95395
││└ Re[3]: string配列を関数に渡したい /marusa (20/07/28(Tue) 15:43) #95397 解決済み
│└ Re[2]: string配列を関数に渡したい /marusa (20/07/28(Tue) 15:39) #95396
Re[1]: string配列を関数に渡したい /Azulean (20/07/29(Wed) 06:11) #95401 解決済み


親記事 / ▼[ 95393 ] ▼[ 95401 ]
■95392 / 親階層)  string配列を関数に渡したい
□投稿者/ marusa (9回)-(2020/07/28(Tue) 15:04:23)

分類:[C/C++] 

お世話になっております。
VisualStudio2015 c++で開発をしております。

String配列について、配列を関数に渡し、関数内で要素数を求める処理をする以下のようなコードを作成したのですが、
要素数が0として表示されてしまいます。
main関数内で要素数を求めた場合には正しく求められるため、配列の引き渡しがうまくいっていないのだと思うのですが、
内部ではどのように渡されているのでしょうか?
初歩的な質問でしたら申し訳ございません。
よろしくお願いいたします。

以下コード
#include <string>
#include <iostream>

#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))

int main(){
int len = 0;
std::string arr[2] = { "foo" , "bar" };
len = getLen(arr);
std::cout << len << std::endl; // 0
std::cout << ARRAY_LEN(arr) << std::endl; // 2
}

int getLen(std::string *strArray){
int ret = ARRAY_LEN(strArray);
return ret;
}
[ □ Tree ] 返信 編集キー/

▲[ 95392 ] / ▼[ 95395 ] ▼[ 95396 ]
■95393 / 1階層)  Re[1]: string配列を関数に渡したい
□投稿者/ Hongliang (1068回)-(2020/07/28(Tue) 15:23:57)
> int getLen(std::string *strArray){
引数は配列ではなくポインタなので、sizeof(strArray)は単にポインタサイズを取得しているにすぎません。
C/C++で配列を配列として渡すことはできず、ポインタとして渡すことになるので、配列サイズを呼び出し先で計測するのは現実的ではありません。
// なので、たいていのAPIは要素数も一緒に渡す形のシグネチャをしています。

C++なのでしたら、静的配列ではなくstd::vector<std::string>を使うようにしたほうがいいのではないでしょうか。
これならばマクロだの別関数だの使うまでもなく、.size()メソッドで要素数を習得できますし。
[ 親 95392 / □ Tree ] 返信 編集キー/

▲[ 95393 ] / ▼[ 95397 ]
■95395 / 2階層)  Re[2]: string配列を関数に渡したい
□投稿者/ ぶなっぷ (234回)-(2020/07/28(Tue) 15:35:21)
#defineマクロはコンパイル前に展開されるので、
自分で展開したコードを書いてデバッグしてみると分かります。

getLen()関数を以下のように書き換えてデバッグします。

int getLen(std::string *strArray)
{
	int ret = ARRAY_LEN(strArray);
	int ret2 = sizeof(strArray);
	int ret3 = sizeof(strArray[0]);
	return ret;
} 

ret2 の値は4, ret3の値は28であることが分かります。
(Win32, Unicodeの場合)
これでピンときますか?
ちょっと考えてみてください。

つまり、getLen()関数に渡した時点で、
  std::string arr[2]
は
  std::string* arr
に型が変わっちゃうんです。

どちらでも、strArray, strArray[0]を値としてみたときは変わりませんが、
型は変わっちゃうんです。

なので、sizeof(strArray) はポインタ変数のサイズである4を返してくるんです。

getLen()の戻り値は 4 / 28 で 0 になります。

[ 親 95392 / □ Tree ] 返信 編集キー/

▲[ 95395 ] / 返信無し
■95397 / 3階層)  Re[3]: string配列を関数に渡したい
□投稿者/ marusa (11回)-(2020/07/28(Tue) 15:43:40)
No95395 (ぶなっぷ さん) に返信
詳細な解説ありがとうございます。
> つまり、getLen()関数に渡した時点で、
> std::string arr[2]
> は
> std::string* arr
> に型が変わっちゃうんです。
関数に渡した時点でどうしてもポインタになってしまうのですね。利用する場合は配列のように利用できるので気付いていませんでした。
解決済み
[ 親 95392 / □ Tree ] 返信 編集キー/

▲[ 95393 ] / 返信無し
■95396 / 2階層)  Re[2]: string配列を関数に渡したい
□投稿者/ marusa (10回)-(2020/07/28(Tue) 15:39:29)
No95393 (Hongliang さん) に返信

お早い返信ありがとうございます。
> C/C++で配列を配列として渡すことはできず、ポインタとして渡すことになるので、配列サイズを呼び出し先で計測するのは現実的ではありません。
これは難しいのですね...定義した関数内では配列として持っているようなのでもしかしたらと思ったのですが諦めます。

> C++なのでしたら、静的配列ではなくstd::vector<std::string>を使うようにしたほうがいいのではないでしょうか。
これもありですね。今回は要素数を渡すことにしようと思いますが、様々な案を提案していただきありがとうございます。
[ 親 95392 / □ Tree ] 返信 編集キー/

▲[ 95392 ] / 返信無し
■95401 / 1階層)  Re[1]: string配列を関数に渡したい
□投稿者/ Azulean (1162回)-(2020/07/29(Wed) 06:11:34)
2020/07/29(Wed) 06:12:03 編集(投稿者)

No95392 (marusa さん) に返信
> int main(){
> int len = 0;
> std::string arr[2] = { "foo" , "bar" };
> len = getLen(arr);
// 略
> int getLen(std::string *strArray){

この例で C++ の場合はテンプレートを書けばできなくはないかなぁ。

参考
https://www.techscore.com/blog/2013/02/08/how-to-calculate-array-length-in-c-and-cpp/

※あくまで arr[2] のようにコンパイル時に要素数が確定した配列として定義されている場合に限られるので、動的配列に対応したいならすでに提案のあるとおり、要素数や std::vector の出番。
解決済み
[ 親 95392 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -