以下のような機能を持っています
int* foo() {
int temp;
return(&temp); //address of temp variable.
と同じ関数を書く際に何が間違っているのか
int foo() {
int temp;
return(&temp); //address of temp variable.
}
「&」演算子は、整数であるメモリ位置のアドレスを返すためです。
int *はintではなく、cは強い型の言語です。メモリ内のすべてを数値として読み取ることができますが、uはすべてをintとして解釈することはできません。
ところで、local(stack)変数のアドレスを返すのは役に立たないようです。
コンパイラは数論ではありません。そのため、&temp
はポインターでありtemp
、整数であり、両方が下にある数値のストリームであっても異なる型です。
コンパイラにロジックを納得させることができるのは、キャストだけです。
何かのようなもの
return (int)&temp
これは本当ですが、コンパイラはアドレスを表す整数について厳密であり、それらを暗黙的に整数として扱うことはできません。代わりに試してくださいreturn ((int)&temp);
ポインター値は単なる整数ではないためです。はい、ポインター値の整数表現を返すことはできますが、整数とポインターのセマンティクスが異なるため、それはポインターを返すことと同じではありません。たとえば、整数型を逆参照することはできず、ポインター演算は整数演算と同じではありません。
たとえば、次のプログラム (C99 としてコンパイル) を考えてみましょう。
#include <stdio.h>
int main(void)
{
int x = 0;
char *cp = 0;
int *ip = 0;
double (*ap)[10] = 0;
printf("sizeof x = %zu, x = %d, x + 1 = %d\n", sizeof x, x, x + 1);
printf("sizeof cp = %zu, cp = %d, cp + 1 = %d\n", sizeof cp, (int) cp, (int) (cp + 1));
printf("sizeof ip = %zu, ip = %d, ip + 1 = %d\n", sizeof ip, (int) ip, (int) (ip + 1));
printf("sizeof ap = %zu, ap = %d, ap + 1 = %d\n", sizeof ap, (int) ap, (int) (ap + 1));
return 0;
}
ここで、ポインターを単なる整数値と考えると、これらすべての print ステートメントで同じ結果が得られると期待できます。ただし、これが私のシステムの出力です。
sizeof x = 4, x = 0, x + 1 = 1
sizeof cp = 4, cp = 0, cp + 1 = 1
sizeof ip = 4, ip = 0, ip + 1 = 4
sizeof ap = 4, ap = 0, ap + 1 = 80
すべてのポインターは整数として同じサイズと表現を持っていますが (少なくとも私のシステムでは)、各ポインター値に 1 を追加した結果は、ポインターの型に基づいて異なる結果になります。 cp + 1
は次のchar
値の場所を教えip + 1
てくれますが、 は次のint
値の場所ap + 1
を教えてくれますし、次のdouble [10]
( の 10 要素配列double
) 値の場所も教えてくれます。
言うまでもなく、両方の例のロジックには欠陥があります。関数から戻るtemp
と、存在しなくなり、返すポインター値は無効になります。
編集
他のことを思い出しました。制約違反である明示的なキャストなしでポインター値を整数に変換しようとしているため、診断が表示されます。章と節:
6.5.16.1 単純代入
制約
1 次のいずれかが成り立つ: 96)
— 左のオペランドは修飾または非修飾の算術型を持ち、右のオペランドは算術型を持つ。
— 左のオペランドは、右の型と互換性のある構造体または共用体型の修飾または非修飾バージョンを持っています。
— 両方のオペランドが、互換性のある型の修飾または非修飾バージョンへのポインターであり、左が指す型には、右が指す型のすべての修飾子があります。
— 一方のオペランドはオブジェクトまたは不完全型へのポインターであり、もう一方は void の修飾または非修飾バージョンへのポインターであり、左が指す型は右が指す型のすべての修飾子を持ちます。
— 左のオペランドはポインターで、右のオペランドは null ポインター定数です。または
— 左のオペランドは _Bool 型で、右のオペランドはポインターです。
...
6.8.6.4 return 文
...
3 式を伴う return 文を実行すると、式の値が関数呼び出し式の値として呼び出し元に返されます。式の型が関数の戻り値の型と異なる場合、値は、関数の戻り値の型を持つオブジェクトへの代入によって変換されるかのように変換されます。139)
ローカル変数のアドレスを返すため、指定した両方の関数は根本的に間違っています。このようなアドレスは、関数の実行が終了するとすぐに無効になります。それをしないでください。これは未定義の動作です。そのオブジェクトにアクセスしようとすると、何かが起こる可能性があります。
int(integer type) and int*(address) are both integer values but can be of different size. Say if int occupies a byte in memory and if we returned a 32 bit address there would be a memory overflow.
最初の例は temp へのポインターを返します (これは意味がありません。temp は範囲外であるため、削除されます)。
2 番目の例では、temp のアドレスを整数値として関数の戻り値に入れます。これは、何らかの目的でアドレス値を出力したい場合にのみ有効です。
したがって、2 番目の例を書くのは間違っています。なぜなら、int を int* にキャストしてポインターのように機能させる必要があるからです。