作者poyenc (髮箍)
看板C_and_CPP
標題Re: [問題] 求問一題指標題輸出
時間Fri Jul 23 01:50:40 2021
※ 引述《ericerix (我的帥,在於臉)》之銘言:
: int main(){
: int a[5] = {1, 2, 3, 4, 5};
: int *p = (int *)(&a + 1);
: printf("%d, %d",*(a+1), (*p-1));
: return 0;
: }
在說明以前, 先定義以下巨集
(macro) 來讓編譯器告訴我們敘述的
型別是什麼:
#define TYPENAME_OF(var)
_Generic((var), \
int(*)[
5]:
"int(*)[5]", \
int* :
"int*", \
int :
"int", \
default :
"unknown" \
)
然後我們就可以試著印出以下幾個敘述的型別:
printf(
"TYPENAME_OF(&a): %s\n", TYPENAME_OF(
&a));
printf(
"TYPENAME_OF(&a + 1): %s\n", TYPENAME_OF(
&a + 1));
printf(
"TYPENAME_OF(p): %s\n", TYPENAME_OF(
p));
// output:
// TYPENAME_OF(&a): int(*)[5]
// TYPENAME_OF(&a + 1): int(*)[5]
// TYPENAME_OF(p): int*
從這可以看到其實 (&a + 1) 的型別和 p 是不相容的, 所以在不加
轉型時編譯器會報 warning. 不是說不能做這樣的轉型, 但你要清
楚知道對陣列或是變數 var 來說, 能夠
取值的位址範圍落在
[&var, &var + 1) 之間
(6.5.6/9), 除此之外都是
undefined be-
havior.
printf(
"valid address range: [%p, %p)\n", &a, &a + 1);
printf(
"a + 1: %p\n", a + 1);
printf(
"p : %p\n", p);
// (possible) output:
// valid address range: [0x7fffffb2ede0, 0x7fffffb2edf4)
// a + 1: 0x7fffffb2ede4
// p : 0x7fffffb2edf4
因為 p 指到界外, 所以原本程式裡的 *p 是不合法的; 如果改成
p - 1 雖然指標值
剛好和最後一個元素的位址相同, 但因為 p 的語
意已經和 (&a + 1) 不一樣
(one past the last element), 原則
上拿 p 往前減再取值依然是
undefined behavior. 不管對哪個陣
列而言, p 都是外部指標,
不能透過外部指標直接或間接存取陣列
元素.
如果想避免
undefined behavior, 那記得當你想存取
int 元素時,
從任意
int 元素的位址
(內部指標) 開始做計算:
int *p = a + (
sizeof a /
sizeof a[
0]);
printf(
"*(p - 1): %d\n", *(p - 1));
指向 one past the last element 的指標雖然無法用來取值, 但它
仍
屬於陣列的內部指標.
-
References
ISO/IEC 9899:202x (E) (N2596)
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2596.pdf
Pointers are more abstract than you might expect in C
https://pvs-studio.com/en/blog/posts/cpp/0576/
--
[P1389R1] Standing Document for SG20: Guidelines for Teaching
C++ to Beginners
https://wg21.link/p1389r1
SG20 Education and Recommended Videos for Teaching C++
https://www.cjdb.com.au/sg20-and-videos
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 118.233.156.253 (臺灣)
※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1626976246.A.CB6.html
推 ericerix: 太詳細了,謝謝大大替我解答。那個巨集的寫法我沒讀到過 07/23 09:49
→ ericerix: ,請問有關鍵字能夠查詢嗎? 07/23 09:49
_Generic (generic selection) 是 C11 時加入的特性唷, 用來根
據型別選取特定的敘述
https://en.cppreference.com/w/c/language/generic
→ LPH66: C11 的新東西, 讓編譯器用式子的形態選式子執行 07/23 09:56
推 Jockey66666: 又偷學一招,棒 07/23 10:48
推 ericerix: 謝謝你! 07/23 11:22
推 sarafciel: 推 07/23 11:39
※ 編輯: poyenc (118.233.156.253 臺灣), 07/23/2021 15:27:36
推 F04E: 板主怎麼都沒m 07/23 15:40
推 DLHZ: 推用心 07/26 19:05