Автор Гілка: вказівник на функцію у C  (Прочитано 5163 раз)

Михайло Даниленко

  • Гість

#include <stdio.h>

void a ( void )
{
        puts ( "a called" );
}

int main ( void )
{
void (*b) ( void ) = a;

        (*****b)();
}

Хто мені пояснить, чому працює цей код?
Вхідні точки у функції C є самовказуючими вказівниками?

Відсутній Kovyar

  • Кореспондент
  • ***
  • дописів: 130
  • Карма: +0/-0
  • Студент
Re: вказівник на функцію у C
« Відповідей #1 : 2008-10-12 20:14:58 »
Я трошки переписав програму:

#include <stdio.h>

void a ( void )
{
       puts ( "a called" );
}

int main ( void )
{
void (*b) ( void ) = a;

       (*b)();
       printf("%x %x %x %x %x\n",&b,b,*b,**b,***b);
       return 0;
}

вивід програми:

[yar@localhost ~]$ ./a.out
a called
bfc4432c 80483f4 80483f4 80483f4 80483f4

думаю, все зрозуміло  ;)
[Fedora 10 Cambridge] [Debian Lenny] [GNOME user]

Михайло Даниленко

  • Гість
Re: вказівник на функцію у C
« Відповідей #2 : 2008-10-12 21:12:40 »
Ага, я теж таке робив, мені, власне, цікаво, чи це офіційний стандарт, чи особливість gcc?

Відсутній Kovyar

  • Кореспондент
  • ***
  • дописів: 130
  • Карма: +0/-0
  • Студент
Re: вказівник на функцію у C
« Відповідей #3 : 2008-10-12 21:27:40 »
Ага. Ну не знаю, можу перевірити у MS Visual Studio завтра в університеті. А де знайти офіційні стандарти, я не знаю  :-[
[Fedora 10 Cambridge] [Debian Lenny] [GNOME user]

Praporshic

  • Гість

Відсутній Веприк

  • Дописувач
  • **
  • дописів: 58
  • Карма: +0/-0
  • Pythonic man
Re: вказівник на функцію у C
« Відповідей #5 : 2008-11-06 00:04:27 »
Ага, я теж таке робив, мені, власне, цікаво, чи це офіційний стандарт, чи особливість 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)

Унарний оператор * означає розіменування. Якщо операнд вказує на функцію, результат є вказівником на функцію;...


Мої рефлексії на довкілля http://blog.sasnyk.name