teratail header banner
teratail header banner
質問するログイン新規登録
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

3回答

876閲覧

関数の配列入力について、配列のサイズ

cgen

総合スコア17

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

2クリップ

投稿2022/08/25 16:10

0

2

前提

関数の配列入力、配列のサイズについて疑問があります。

実現したいこと

標準入力でのサイズをそのまま関数の入力にしてしまうやり方と、関数の中でサイズを求めるやり方を二つ試すとサイズが違ってしまったのですがなぜかわかりません。
関数の中でサイズを求めると1になってしまったのでsizeof(a)がsizeof(int)と同じサイズになったようです。

そもそもメイン関数で配列を与えて、関数の入力でその配列を与えるやり方においてfor文のような配列の要素を比べる操作はできるのに、なぜ配列のサイズは求められないのでしょうか。

発生している問題・エラーメッセージ

$ g++ maximumprofit.cpp maximumprofit.cpp: In function 'int size(int*)': maximumprofit.cpp:17:20: warning: 'sizeof' on array function parameter 'a' will return size of 'int*' [-Wsizeof-array-argument] 17 | int s= sizeof(a)/sizeof(int); | ^ maximumprofit.cpp:16:14: note: declared here 16 | int size(int a[]){ | ~~~~^~~ maximumprofit.cpp:19:1: warning: no return statement in function returning non-void [-Wreturn-type] 19 | }

該当のソースコード

c++

1#include <iostream> 2#include <type_traits> 3using namespace std; 4 5int maxv(int a[], int size){ 6 int i, j; 7 int max=a[1]-a[0]; 8 for(j=2; j<size; j++){ 9 for(i=0; i<j; i++){ 10 if(max < a[j]-a[i]) max = a[j]-a[i]; 11 } 12 } 13 return max; 14} 15 16int size(int a[]){ 17 int s= sizeof(a)/sizeof(int); 18 cout << s << "\n"; 19} 20 21int main(){ 22 int a[200000], n; 23 cin >> n; 24 for(int i=0; i<n; i++){ 25 cin >> a[i]; 26 } 27 cout << maxv(a, n) << "\n"; 28 size(a); 29 return 0; 30}

試したこと

実際にサイズを出力してみるとsizeof(a)/sizeof(int)は1になったためsizeof(a)は1であるとわかった

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

C/C++では、配列をかなり「いい加減」な方法で扱います。

そもそも配列ってなに? というのはわかっていますか? 何らかの要素を、密に詰め合わせてメモリ上に配置したものです。密に詰め合わせているのですから、一つの要素の場所がわかれば隣、その隣の要素の場所もわかりますよね?
だったら、「配列」の最初の要素の場所だけ知れればあとの要素へのアクセスもできるじゃない...というのがCの配列のやりとりの考え方。これに則って、int size(int a[])として引数に「配列のつもり」で書いたaは、配列の先頭要素へのポインタとして解釈、運用されます。つまり、int size(int *a)と等価です(もちろん、これに合わせて関数の呼び出し側でもint a[N];についてsize(a)と記述されたら&a[0]を引数に渡します)。なので、sizeof(a)sizeof(int*)と同じ値になります。これは、必ずではありませんがsizeof(int)と同じ値になる処理系は多いですね。

なお、これは配列のサイズは別途明示的に渡さないと関数には渡らない、ということも意味しています。

どうしてもsizeofで配列サイズを取りたいなら、引数リストについて以下のような記述をすることはできます。
int size(int (*b)[200000])
そうすると、bはint200000要素の配列へのポインタということですから、sizeof(*b)sizeof(int)*200000と同じ値になります。ただし、このとき呼び出し側はsize(&a);とする必要がありますし、呼び出し側の配列のa[0]にアクセスしたければ(*b)[0]とする必要があります。(*bはすなわちポインタが指している「配列」そのもの)

-余談1-
これと合わせて、C/C++では以下のような決まりがあります。
・配列名が単独で記述されたら、それは配列の先頭アドレスとして解釈される
int a[N];について、int* b=a;と記述されたときa&a[0]と等価。
・配列aと整数Mがあったとき、
a[M]*(a+M) は等価 (a+Maは先の決まりによって&a[0]と解釈されています)
つまり、int a[N]; について int *b=a;としたら
a[M]b[M]*(a+M)*(b+M)は皆同じものにアクセスすることになります(ついでにM[a]とかM[b]なんてものも派生しますが、まぁそれは実用に供することはないでしょう)。

覚えておくといいことがあるかも知れません。

-余談2-
ローカルの自動変数はスタック領域にとられます。スタック領域は、あくまで「一時的なデータの保持」に使われる意図であるため、決して大きな領域を確保していません。まぁ、現実として今どきのWindowsやLinuxではint型200000個のデータを格納できないことはないのですが、しかしキモチとしてはそれは「スタックにとるには大きいなぁ」という気がします。

-余談3-
「配列の先頭」と書きましたが受け取った側からすれば必ずしも「先頭」ではなくて基準点といった方がいい場合もあるかもしれません。
char str[]="world";があったとして、関数func(char s[]);の引数に&str[3]を渡せば関数内ではs[-1]で呼び出し元のstr[2]つまり'r'にアクセスできたりするので。もちろん、インデックスの範囲が有効であることはプログラマが保証しなければいけませんけれど。

投稿2022/08/26 00:20

編集2022/08/27 01:35
thkana

総合スコア7738

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

回答がついてるので終わっているとは思うのですが、そもそも質問に書かれているエラーの内容に

warning: 'sizeof' on array function parameter 'a' will return size of 'int*'

↓ (DeepLで翻訳)

警告:配列関数パラメータ 'a' の 'sizeof' は 'int*' のサイズを返します。

と書かれています。まずはエラーを確認すべきだと思います。

あとはおまけ
・sizeofはコンパイル時に計算される演算子なので、「実行時に実行内容に応じて結果が変動する」ことに使うものではなかったはず。
・sizeof(a)は、32ビットか64ビットかなどで結果が変わることにも注意

vector使おう。

投稿2022/08/26 13:45

yominet

総合スコア187

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

thkana

2022/08/27 01:17

> sizeofはコンパイル時に計算される演算子なので、「実行時に実行内容に応じて結果が変動する」ことに使うものではなかったはず。 特にそんな縛りはなかったと思いますが? ソースコード中に(1+2)と書いたらコンパイル時に3になるとしても、+演算子は「実行時に実行内容に応じて結果が変動する」ことに使うのは普通でしょ? それと何ら変わりません。まぁ、変数の型は実行中に変わったりはしないからコンパイル時に確定していることがほとんど、というのは言えるでしょうけれど。
yominet

2022/08/27 02:16

「演算子であるので、コンパイル時に決まる」 ではなく 「コンパイル時に計算される(タイプの)演算子」 という意味で書きたかったのでこの順番で書きました(関数ではないことが言いたかった)。 すいません。
guest

0

int s= sizeof(a)/sizeof(int);

ここでsizeof(a)の結果は、ポインタのサイズでしかありません
ぶっちゃけこういう方法で配列のサイズは求めることはできません。

これで求めることができるのは、配列の定義を行って、コンパイラが配列のサイズを把握しているときのみです

投稿2022/08/25 22:22

y_waiwai

総合スコア88180

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.30%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問