Объясните разницу

User avatar
thinker
Уже с Приветом
Posts: 26871
Joined: 29 Aug 2000 09:01

Объясните разницу

Post by thinker »

Объясните разницу в следуших кусках C кода (меняется только одна строчка):

Code: Select all

  while(pf_init_arr[i] != NULL)
  {
    //call the init function in index 'i' of the array
    (pf_init_arr[i])();
    i++;
  }

Code: Select all

  while(pf_init_arr[i] != NULL)
  {
    //call the init function in index 'i' of the array
    (*pf_init_arr[i])();
    i++;
  }

Code: Select all

  while(pf_init_arr[i] != NULL)
  {
    //call the init function in index 'i' of the array
    *pf_init_arr[i]();
    i++;
  }
All rights reserved, all wrongs revenged.
User avatar
M. Ridcully
Уже с Приветом
Posts: 12017
Joined: 08 Sep 2006 20:07
Location: Силиконка

Re: Объясните разницу

Post by M. Ridcully »

Вызываются функции по указателям в массиве, как я понимаю.

Проверять лень, но первое и второе вроде одно и то же.
А в третьем случае после вызова ф-ции ещё результат is dereferenced. Он отбрасывается, так что в обычном случае эффект будет, что и в первых двух случаях, но будет undefined behavior, если возвращаемый ф-цией указатель невалидный.
Мир Украине. Свободу России.
User avatar
thinker
Уже с Приветом
Posts: 26871
Joined: 29 Aug 2000 09:01

Re: Объясните разницу

Post by thinker »

Почему-то когда я гуглю на тему "функции по указателям в массиве", появляются только примеры со вторым куском. А первый нет. А третий - вообще может ошибка? и нужно писать так: pf_init_arr)(); ???
All rights reserved, all wrongs revenged.
User avatar
M. Ridcully
Уже с Приветом
Posts: 12017
Joined: 08 Sep 2006 20:07
Location: Силиконка

Re: Объясните разницу

Post by M. Ridcully »

Про гугл у гугла спросите.
Третий вариант будет ошибкой, если ф-ция не указатель возвращает.
Мир Украине. Свободу России.
User avatar
Ion Tichy
Уже с Приветом
Posts: 13339
Joined: 07 Dec 2004 04:00
Location: Москва->CO

Re: Объясните разницу

Post by Ion Tichy »

А можно глянуть на определение pf_init_arr?
Как же это вы без гравицаппы пепелац выкатываете из гаража? Это непорядок...
User avatar
thinker
Уже с Приветом
Posts: 26871
Joined: 29 Aug 2000 09:01

Re: Объясните разницу

Post by thinker »

Ion Tichy wrote:А можно глянуть на определение pf_init_arr?
В нем таблица с функциями, примерно так:

Code: Select all

func_type pf_init_arr[] = { 
foo1,
foo2,
foo3,
NULL
 };
п.с. добавил NULL
Last edited by thinker on 07 May 2016 02:58, edited 1 time in total.
All rights reserved, all wrongs revenged.
User avatar
Ion Tichy
Уже с Приветом
Posts: 13339
Joined: 07 Dec 2004 04:00
Location: Москва->CO

Re: Объясните разницу

Post by Ion Tichy »

thinker wrote:
Ion Tichy wrote:А можно глянуть на определение pf_init_arr?
В нем таблица с функциями, примерно так:

Code: Select all

func_type pf_init_arr[] = { 
foo1,
foo2,
foo3,
 };
Зашибись :)
func_type - сразу все стало ясно. :great:
А убрать всю препроцную шелуху можно?

Я это к чему? Было у меня в С - кошмарик что-то типа "массив указателей на массивы адресов функций возвращающих указатели на структуры типа... и принимающих агрументы ...". Ну почти как сложносочиненноподчиненномногоэтажнопереходные языковые конструкции в русском мате.
Я сдался, корешей позвал, хором все же разобрали декларацию и сбацали код, компилящийся без ворнингов.
Как же это вы без гравицаппы пепелац выкатываете из гаража? Это непорядок...
User avatar
thinker
Уже с Приветом
Posts: 26871
Joined: 29 Aug 2000 09:01

Re: Объясните разницу

Post by thinker »

Code: Select all

// Structures
typedef void (*func_type)(void);

// Global Variables
extern const CODE func_type pf_init_arr[];
All rights reserved, all wrongs revenged.
User avatar
Ion Tichy
Уже с Приветом
Posts: 13339
Joined: 07 Dec 2004 04:00
Location: Москва->CO

Re: Объясните разницу

Post by Ion Tichy »

Хмм... Последний раз пользовал С три года назад. Предпоследний - лет 14 назад. Ну да ладно.
Итак, надо отпарсить выражение. Берем первую попавшуюся таблицу приоретеов операторов - http://www.tutorialspoint.com/cprogramm ... edence.htm" onclick="window.open(this.href);return false;.
Начали.

1.

Code: Select all

(pf_init_arr[i])();
()() - слева направо.
Левые () сводятся к выражению внутри. Берем что внутри (pf_init_arr) и трактуем как точку входа в функцию. Парсинг закончен, дальнейшая компиляция зависит от определения pf_init_arr. Если это массив адресов точек входа в функции, то все д.б. пучком.

2.

Code: Select all

(*pf_init_arr[i])()
()() - слева направо.
Левые () сводятся к выражению внутри. Берем что внутри *pf_init_arr - звездочка-выр. Звездочка - справа налево. Смотрим что справа - справа pf_init_arr. Берем это справа и из-за впередистоящей звездочки трактуем это как указатель. Опять-таки - упираемся в "а что такое pf_init... Если это массив адресов, по которым хранятся адреса точек входа в функции, то все д.б. пучком.

3.

Code: Select all

*pf_init_arr[i]()
*выр - слева направо.
След - токен[]: слева направо. Сводится к: взять i-й эл-т массива, инте


Блин, Мыслитель, задрал - тяпница вечер, у меня уже пол-6пака тю-тю, а я как последний ботан тебе домашку делаю.
Себя не узнаю :(
Как же это вы без гравицаппы пепелац выкатываете из гаража? Это непорядок...
User avatar
Relict17
Уже с Приветом
Posts: 573
Joined: 15 Jan 2016 02:50

Re: Объясните разницу

Post by Relict17 »

Первый и второй - одно и то же, функцию можно вызывать и через обычный, и через разыменованный указатель.
Третий вариант должен дать ошибку компиляции "void value not ignored" или подобное.
User avatar
thinker
Уже с Приветом
Posts: 26871
Joined: 29 Aug 2000 09:01

Re: Объясните разницу

Post by thinker »

Relict17 wrote:Первый и второй - одно и то же, функцию можно вызывать и через обычный, и через разыменованный указатель.
Третий вариант должен дать ошибку компиляции "void value not ignored" или подобное.
А вот это:
pf_init_arr();
будет аналогично первому и второму варианту?
All rights reserved, all wrongs revenged.
User avatar
M. Ridcully
Уже с Приветом
Posts: 12017
Joined: 08 Sep 2006 20:07
Location: Силиконка

Re: Объясните разницу

Post by M. Ridcully »

thinker компилятор пишет, наверное ;)
Мир Украине. Свободу России.
User avatar
Relict17
Уже с Приветом
Posts: 573
Joined: 15 Jan 2016 02:50

Re: Объясните разницу

Post by Relict17 »

thinker wrote:
Relict17 wrote:Первый и второй - одно и то же, функцию можно вызывать и через обычный, и через разыменованный указатель.
Третий вариант должен дать ошибку компиляции "void value not ignored" или подобное.
А вот это:
pf_init_arr();
будет аналогично первому и второму варианту?

Да, аналогично.
User avatar
thinker
Уже с Приветом
Posts: 26871
Joined: 29 Aug 2000 09:01

Re: Объясните разницу

Post by thinker »

Relict17 wrote:
thinker wrote:А вот это:
pf_init_arr();
будет аналогично первому и второму варианту?

Да, аналогично.

Спасибо всем ответившим.
All rights reserved, all wrongs revenged.
User avatar
AndreyT
Уже с Приветом
Posts: 3000
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Re: Объясните разницу

Post by AndreyT »

thinker wrote:Почему-то когда я гуглю на тему "функции по указателям в массиве", появляются только примеры со вторым куском. А первый нет. А третий - вообще может ошибка? и нужно писать так: pf_init_arr)(); ???


thinker wrote:
А вот это:
pf_init_arr();
будет аналогично первому и второму варианту?


На самом деле в языке С (в отличие от С++) вызов функции всегда производится через указатель. Оператор вызова функции в С `()` требует аргумента типа "именно указатель на функцию". Даже если вы напрямую вызываете обычную функцию, скажем, `strlen("Hello World")`, то в этом выражении первым делом применяется неявное преобразование типа, приводящее выражение `strlen` к типу `size_t (*)(const char *)`, т.е. оно втихаря интерпретируется компилятором как `(&strlen)("Hello World")`. Таким же образом функциональный тип "деградирует" до указательного типа во всех контекстах, кроме унарного `&` и `sizeof`.

По этой причине вызывать функции напрямую вы можете как `strlen("Hello World")` и как `(strlen)("Hello World")` и как `(&strlen)("Hello World")` и как `(*strlen)("Hello World")` и как `(**strlen)("Hello World")` и как `(*****strlen)("Hello World")`. Все эти варианты эквивалентны.

http://coliru.stacked-crooked.com/a/ec835407e2113ef9" onclick="window.open(this.href);return false;

По этой же причине, при наличии некоего указателя на функцию `ptr` (или, в вашем случае, `pf_init_arr`) выполнять вызов функции через такой указатель вы можете как `ptr()` и как `(ptr)()` и как `(*ptr)()` и как `(*******ptr)()`. Все эти варианты эквивалентны. Ваши первые два варианта - именно это.
Best regards,
Андрей
User avatar
thinker
Уже с Приветом
Posts: 26871
Joined: 29 Aug 2000 09:01

Re: Объясните разницу

Post by thinker »

AndreyT wrote:По этой же причине, при наличии некоего указателя на функцию `ptr` (или, в вашем случае, `pf_init_arr`) выполнять вызов функции через такой указатель вы можете как `ptr()` и как `(ptr)()` и как `(*ptr)()` и как `(*******ptr)()`. Все эти варианты эквивалентны. Ваши первые два варианта - именно это.

Для меня, начинающего учить C, есть проблема различать, где мы имеем дело с вызовом по указателю на функцию или же с вызовом самой функции. По написанию оба вызова одинаковы, ptr(), но определить, что есть что, можно только копая глубже. Т.е. нужно смотреть как определялась ранее ptr().

Далее, также
(ptr)() vs. (*ptr)()
вызвало проблему. Ожидал увидеть со звездочкой, но у нас в коде было без неё. Стал искать такую конструкцию в гугле, но искалось в основном со звездочкой. Вообщем, темный лес. :-)
Спасибо.
All rights reserved, all wrongs revenged.
User avatar
AndreyT
Уже с Приветом
Posts: 3000
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Re: Объясните разницу

Post by AndreyT »

thinker wrote:Ожидал увидеть со звездочкой, но у нас в коде было без неё.
Это вопрос соглашения, и относится он как к синтаксису инициализации/присваивания значения указателю на функцию, так и к синтаксису вызова через указатель.

Одни авторы настаивают на том, чтобы в коде было хорошо видно, где идет работа с указателем на функцию, а где - напрямую с функцией. Они, как правило, упорно стараются явно применять операторы `&` и `*` при работе с указателями

Code: Select all

size_t (*ptr)(const char *) = &strlen; // Инициализация с явным `&`
...
size_t len = (*ptr)("Hello World"); // Вызов с явным `*`
Другие авторы считают, что нет необходимости подчеркивать это различие и правильнее (и удобочитаемее) обходиться без `&` и `*`

Code: Select all

size_t (*ptr)(const char *) = strlen;
...
size_t len = ptr("Hello World");
А кто-то вообще бессистемно мешает и то, и другое...

Разницы никакой нет. Выбирайте, как вам больше нравится.
Best regards,
Андрей

Return to “Вопросы и новости IT”