Ага, я теж таке робив, мені, власне, цікаво, чи це офіційний стандарт, чи особливість gcc?
Якщо мова йде про стандарт саме на С (не С++), то рекомендую користуватися
http://www.knosof.co.uk/cbook/cbook.html . Це фундаментальна праця на 1600 сторінок, стандарт С з коментарями. Він пише не лише як має бути, але й чому.
Ось результати тесту в VC++2008
D:\work\2>cl /TC test.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
test.c
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:test.exe
test.obj
D:\work\2>test
a called
12ff74 401000 401000 401000 401000
Далі вирубаємо оптимізацію, включаємо Release конфігурацію і дивимося що згенерував ассемблер:
call DWORD PTR _b$[ebp]
; 18 : printf("%x %x %x %x %x\n",&b,b,*b,**b,***b);
mov eax, DWORD PTR _b$[ebp]
push eax
mov ecx, DWORD PTR _b$[ebp]
push ecx
mov edx, DWORD PTR _b$[ebp]
push edx
mov eax, DWORD PTR _b$[ebp]
push eax
lea ecx, DWORD PTR _b$[ebp]
push ecx
push OFFSET $SG-6
call DWORD PTR __imp__printf
add esp, 24 ; 00000018H
Бачимо що незалежно від кількості вказаних операторів розіменування вказівника генерується той самий код:
mov eax, DWORD PTR _b$[ebp]
push eax
Сторінка 91. стандарту, розділ 6.5.3.2 пункт 4 каже:
The unary * operator denotes indirection.
If the operand points to a function, the result is
a function designator; if it points to an object, the result is an lvalue designating the
object. If the operand has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an
invalid value has been assigned to the pointer, the behavior of the unary * operator is
undefined.83)
Унарний оператор * означає розіменування.
Якщо операнд вказує на функцію, результат є вказівником на функцію;...