Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Два входа в одну функцию на C
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
ViKo
На ассемблере у меня были подпрограммы, в которых сначала идет управление, а потом индикация. И можно было при желании вызвать только индикацию, и вернуться обычным возвратом в конце подпрограммы. Можно ли так сделать на C? Иметь в середине функции метку, и вызвать функцию по указателю на эту метку?
MALLOY2
Код
void Indication(void)
{
   ...
}

void Control(void)
{
  ...
}

void Combo(void)
{
   Control();
   Indication();
}


Без комментариев.
The Best smile.gif
MrYuran
Цитата(ViKo @ Dec 2 2010, 14:28) *
Можно ли так сделать на C? Иметь в середине функции метку, и вызвать функцию по указателю на эту метку?

Можно, но не так.
Сделать внутри switch(), а переключатель передавать снаружи.
Если тупо перейти по метке, то при операции RET произойдёт возврат хз куда, но не туда, куда нужно.
И вообще, пролог, эпилог - это всё нельзя пропускать.
ViKo
Цитата(MALLOY2 @ Dec 2 2010, 13:43) *
The Best smile.gif

Это - понятно. Так и делается, естественно. Вопрос, скорее, теоретический.
Имеется некоторая избыточность в виде лишнего вызова, если принять во внимание, что Contol без Indication не бывает.
MALLOY2
Цитата
Имеется некоторая избыточность в виде лишнего вызова


От лишнего вызова можно избавится поигравшись с компилятором (я не знаю какой он у вас, и какой проц, от этого многое зависит), попробовать функции объявить inlinе и .т.д. Но то что вы хотите сделать это гомнокод еще тот, даже и не думайте о таких подходах.

В самом крайнем случае заменить функции дефайнами, и то прравельней будет.

Код
#define   Indication() \
{\
   ...\
}\

#define  Control()\
{\
  ...\
}\

void Combo(void) //и никакого оверхеда только можно таких багов нахвататься
{
   Control();
   Indication();
}

ViKo
Цитата(MALLOY2 @ Dec 2 2010, 13:57) *
я не знаю какой он у вас, и какой проц

И проц крутой - STM32F103, и компилятор крутой - Keil RealView MDK-ARM 4.12
Цитата
Но то что вы хотите сделать это гомнокод еще тот, даже и не думайте о таких подходах.

И делать так не хочу. А подумать не помешает... smile.gif
Цитата
В самом крайнем случае заменить функции дефайнами, и то правильней будет ... и никакого оверхеда только можно таких багов нахвататься

Оверхед остается, если принять во внимание, что иногда мне нужно сразу Control-Indication(), а иногда только Indication().

Цитата(MrYuran @ Dec 2 2010, 13:47) *
Можно, но не так.
Сделать внутри switch(), а переключатель передавать снаружи.

Если с переключателем, так это еще более избыточно. Лучше уж 2 независимые функции.
MrYuran
Цитата(MALLOY2 @ Dec 2 2010, 14:57) *
В самом крайнем случае заменить функции дефайнами, и то прравельней будет.

Но только не так, как у вас!
Что, по-вашему, скажет компилер на конструкцию {};

Обычно макросы объявляют в скобках do{}while(0)

А лучше всё-таки макросами не злоупотреблять, во избежание.
vvs157
Цитата(ViKo @ Dec 2 2010, 14:28) *
Иметь в середине функции метку, и вызвать функцию по указателю на эту метку?
Метка в С имеет область видимость только внутри функции, поэтому переход в общем случае непосредственно по метке извне, вообще говоря, невозможен. В некоторых реализациях С есть нелокальный goto, но реализовано это как первоначальное запоминание контекста вызовом setjmp, с последующим переходом на запомненное состояние вызовом longjmp.
kosyak©
2MrYuran
А чем компилятору могут не понравиться скобки?
MALLOY2
Цитата
Но только не так, как у вас!
Что, по-вашему, скажет компилер на конструкцию {};


Про все компиляторы не скажу, но те с которыми я работаю такую конструкцию хавают.
MrYuran
Цитата(kosyak© @ Dec 2 2010, 15:34) *
А чем компилятору могут не понравиться скобки?

Есть нюансы.
Например, в конструкциях с if()
ViKo
Есть вариант без switch
Код
void Control_Indication(bool ctrl)
{
if (ctrl) {
... // Control
  }
... // Indication
}
kosyak©
Цитата(MrYuran @ Dec 2 2010, 15:55) *
Есть нюансы.
Например, в конструкциях с if()

При do{}while(0) ньюансы с if пропадают?
MrYuran
Цитата(kosyak© @ Dec 2 2010, 16:16) *
При do{}while(0) ньюансы с if пропадают?

Цитата
Обратим внимание, что использована конструкция do {…} while (0). Если бы мы ее не использовали, то постановка else в нашем условии:

if (...) I2C_CLOCK(); else return;

привела бы к сообщению компилятора об ошибке "inappropriate else". Все дело в том, что мы перед else и после '}' ставим ';', которая воспринимается компилятором как конец оператора if. Поэтому и использованы скобки в виде do {…} while.

отсюда
MALLOY2
Так и есть это и есть всякие баги с макросами smile.gif,

Лично я исторически так сложилось всегда записываю только так

Код
if(...)
{
  //даже если только 1 оператор используется.
}
else
{

}
kosyak©
Занятная статья.
do{}while(0) я видел в чужих исходниках пару раз - и никак не мог понять зачем? Спасибо, разъяснили..

Сам я тоже сторонник везде ставить фигурные скобки..потому ниразу на подобную ошибку и не натыкался.
ViKo
Цитата(kosyak© @ Dec 2 2010, 15:39) *
Сам я тоже сторонник везде ставить фигурные скобки..потому ниразу на подобную ошибку и не натыкался.

А я, наоборот, сторонник минимализма. Не хочу писать ничего лишнего. Так мне красивее кажется.
А за ссылку на статью - спасибо, MrYuran!
sergeeff
Ну можно еще более универсальную штуку сочинить, типа:

Код
typedef void (*pVoidFun)(void);          

void Control_Indication(pVoidFun p)
{
   if(p)
      p();
  
   Indication();
}


Тогда можно вызывать Control_Indication(NULL) - будет вызываться только Indication(), а при вызове типа Control_Indication(Control) сначала вызовется функция, адрес которой вы передали вызывающей функции, затем Indication().
ViKo
Цитата(sergeeff @ Dec 2 2010, 16:12) *
...а при вызове типа Control_Indication(Control) сначала вызовется функция, адрес которой вы передали вызывающей функции...

Только Control() при этом должна быть отдельной функцией, иначе указатель на нее не создать. sad.gif
sergeeff
Вы боретесь за экономию одной команды вызова и одной команды возврата?
_Pasha
Цитата(MALLOY2 @ Dec 2 2010, 14:43) *
Без комментариев.
The Best smile.gif

Почему же без комментариев? их есть!
ТХЕ БЕСТ оно будет когда все указанные функции объявим static.
_Pasha
Цитата(MALLOY2 @ Dec 2 2010, 14:43) *
Без комментариев.
The Best smile.gif

Почему же без комментариев? их есть!
ТХЕ БЕСТ оно будет когда все указанные функции объявим static.
ViKo
Цитата(sergeeff @ Dec 2 2010, 17:21) *
Вы боретесь за экономию одной команды вызова и одной команды возврата?

Я не борюсь, а интересуюсь. Мне уже очевидно, что сделать нельзя.

Цитата(_Pasha @ Dec 2 2010, 17:46) *
ТХЕ БЕСТ оно будет когда все указанные функции объявим static.

А вот здесь желательны комментарии. smile.gif
sergeeff
Цитата(_Pasha @ Dec 2 2010, 19:46) *
Почему же без комментариев? их есть!
ТХЕ БЕСТ оно будет когда все указанные функции объявим static.


Если их все объявить как static, то они на фиг никому будут не нужны, так как доступа к ним не будет. Можно (и идеологически правильно) Control() и Indication() объявить как static, чтобы к ним никто не совался кроме как через посредство Control_Indication().
zltigo
QUOTE (kosyak© @ Dec 2 2010, 16:39) *
Занятная статья.
do{}while(0) я видел в чужих исходниках пару раз - и никак не мог понять зачем? Спасибо, разъяснили..

Трюк, конечно, хороший, только надо тоже с аккуратностью применять, ибо:
1) Хороший компилятор выдаст предупреждение о том, что условие никогда НЕ выполняется, хотя все сделает правильно,
но лишние вопли придется давить локальными прагамами - некузяво sad.gif.
2) Поганый компилятор (например, вот прямо сейчас работаю с рекордно поганым ImageCraft для M8C ) - такое НЕ соптимизирует -
вообще гнусно.

kosyak©
2zltigo:
Хм...логично. Тогда напрашивается вопрос - и как жить? crying.gif
Не использовать препроцессор прошу не предлагать smile.gif
ViKo
Цитата(_Pasha @ Dec 2 2010, 17:46) *
ТХЕ БЕСТ оно будет когда все указанные функции объявим static.

Прокомментирую сам. Квалификатор static будет иметь смысл, если я эти функции заброшу в отдельный файл. Таких функций у меня несколько десятков, что ж, мне, столько файлов создавать? Не вижу смысла.
P.S. Где-то здесь и зарыта собака "неполной структурированности" языка C.
P.P.S. По поводу do {...} while (0) - а чем плохо показанное в той же статье строчкой выше {...} ? Разве кто-то заставляет писать I2C_CLOCK(); с точкой с запятой в конце?
Или в дефайне можно было задать
#define I2C_CLOCK(); {NOP(); SCL = 1; NOP(); SCL = 0;}
Да, это не работает (кажется, в Hi-Tech PICC у меня получалось такое, а в Keil - нет)
Dog Pawlowa
Цитата(ViKo @ Dec 2 2010, 21:17) *
Или в дефайне можно было задать
#define I2C_CLOCK(); {NOP(); SCL = 1; NOP(); SCL = 0;}

Дык боролись за точку с запятой перед else - Где она?
zltigo
QUOTE (kosyak© @ Dec 2 2010, 21:09) *
Хм...логично. Тогда напрашивается вопрос - и как жить? crying.gif

Думать по обстоятельствам.
QUOTE
Не использовать препроцессор прошу не предлагать smile.gif

Почему? В числе решений по обстоятельствам, если компилятор не совсем дебильный, как вышеупомяутый, использовать принудительный inline.
kosyak©
Понятн...
Вот интересно - доживу я до момента когда в стандарт "С201x" включат шаблоны (template) на манер С++? И самое главное доживу ли я до появления компилятора, который будет это все поддерживать smile.gif
XVR
Цитата(MALLOY2 @ Dec 2 2010, 14:43) *
Код
void Combo(void)
{
   Control();
   Indication();
}


Без комментариев.
The Best smile.gif
Еще один коментарий - если компилятор достаточно умный, то он из функции Combo сделает именно то, что было нужно ТС. Ну, может быть, с оверхедом на один jmp
(И даже для не статических функций Control и Indication)

PS. Пишите на FORTRAN'е - там это можно средствами языка biggrin.gif
Tanya
Цитата(XVR @ Dec 3 2010, 10:03) *
PS. Пишите на FORTRAN'е - там это можно средствами языка biggrin.gif

Или на Форте. Только там такая задача почти лишена смысла... Вот один раз только понадобилось для использования части (хвоста) ядреного слова.
_Pasha
Цитата(ViKo @ Dec 2 2010, 21:17) *
Прокомментирую сам. Квалификатор static будет иметь смысл, если я эти функции заброшу в отдельный файл. Таких функций у меня несколько десятков, что ж, мне, столько файлов создавать? Не вижу смысла.

Пардон, не было времени на развернутый ответ.
Код
void Combo(void);
void Control(void);
void Indication(void);
//...............................................
static void Control_prim(void);
static void Indication_prim(void);

Имеем заготовки для того чтобы контрол и индикация вызывались как извне так и максимально оптимизированно
Код
void Combo(void)
{
Control_prim();
Indication_prim();
}

void Control(void)
{
Control_prim();
}

void Indication(void);
{
Indication_prim();
}


Теперь великий смысел оберток, надеюсь понятен. smile.gif
ViKo
Цитата(_Pasha @ Dec 3 2010, 10:29) *
Теперь великий смысел оберток, надеюсь понятен. smile.gif

Увы... crying.gif Не могли бы вы словами описать, за счет чего будет оптимизация. Пока что вижу разрастание кода...
Как будет у меня:
Код
int main(void) { ... Control(); Indication(); ... Indication(); }
void Control(void) { ... }
void Indication(void) { ... }

Другой вариант:
Код
int main(void) {... Control(); ... Indication(); }
void Control(void) { ... Indication(); }
void Indication(void) { ... }
_Pasha
Цитата(ViKo @ Dec 3 2010, 12:32) *
Пока что вижу разрастание кода...

За счет разворачивания статических функций. Запретите инлайнизацию.
ViKo
Цитата(kosyak© @ Dec 2 2010, 20:09) *
Хм...логично. Тогда напрашивается вопрос - и как жить? crying.gif

Например, так, без точки с запятой после макрофункции, хватит и ПРОПИСНЫХ_БУКВ_СО_СКОБКАМИ()
Код
#define I2C_CLOCK() {NOP(); SCL = 1; NOP(); SCL = 0;}
if (...) I2C_CLOCK() else return;
Сергей Борщ
QUOTE (ViKo @ Dec 3 2010, 11:41) *
Например, так, без точки с запятой после макрофункции,
Потом почесали за ухом и решили, что в процессе кования программы макрос разросся и лучше сделать из него обычную функцию. И помчались вставлять точку с запятой везде, где было использование этого макроса. И все ради чего? Ради того, чтобы по религиозным соображениям не использовать конструкцию do {} while(0) ? Да пожалуйста.
_Pasha
Собсна делов-то:
Код
#define Begin___Macro do{
#define End___Macro }while(0);

И даже самый страшный и больной компилер пойдет туда, куда нада. ЗЫ: если дописать break;}while(0); например

По сабжу: вообще-то логика организации программ на сях отличается от асмового. Например, если завести нечто типа машины Даффа и расположить жизненно важные части по раз и навсегда определенному приоритету, типа
Код
void system(char prio)
{
  switch(prio)
{
   case 0: remote_command();
   case 1: indication();
   case 2: control();
   case 3: critical();
}
}

можно вызывать такое отовсюду и отрубать не актуальные функции входным параметром - приоритетом.
Хотя я приверженец protothreads...
ViKo
Цитата(Сергей Борщ @ Dec 3 2010, 14:53) *
Потом почесали за ухом и решили, что в процессе кования программы макрос разросся и лучше сделать из него обычную функцию. И помчались вставлять точку с запятой везде, где было использование этого макроса.

Когда я захочу вместо макроса применить функцию, я даже имени такого не оставлю, а изменю, согласно своим "религиозным" убеждениям. Заменить не трудно, при желании, можно даже автоматом. Я стараюсь не плодить лишних сущностей.

Цитата(_Pasha @ Dec 3 2010, 15:39) *
можно вызывать такое отовсюду и отрубать не актуальные функции входным параметром - приоритетом.

Об этом написал MrYuran в посте №3
_Pasha
Цитата(ViKo @ Dec 3 2010, 18:58) *
Об этом написал MrYuran в посте №3

Переключатель - это еще не все. Вопрос, где break ставить, и ставить ли вообще.
ViKo
Цитата(_Pasha @ Dec 3 2010, 17:07) *
Переключатель - это еще не все. Вопрос, где break ставить, и ставить ли вообще.

Да, вы правы. В вашем методе есть смысл (именно так я писал на ассемблере, но без параметра - приоритета). Только все равно перед вызовом такой функции нужно готовить параметр. И внутри его проверять.
Я лучше буду вызывать функции независимо.
MrYuran
Цитата(ViKo @ Dec 3 2010, 18:20) *
Да, вы правы. В вашем методе есть смысл (именно так я писал на ассемблере, но без параметра - приоритета). Только все равно перед вызовом такой функции нужно готовить параметр. И внутри его проверять.

Если функции инлайновые, а параметр константный, то компилятор сразу подставит по месту, без всяких лишних проверок.
_Pasha
Цитата(MrYuran @ Dec 3 2010, 18:47) *
то компилятор сразу подставит по месту, без всяких лишних проверок.

нормальный компилятор smile.gif
sigmaN
Цитата
Если функции инлайновые, а параметр константный,
константный, это const? У меня есть некоторые сомнения по поводу const. Тут наверно правильнее сказать как-то так: определен на этапе компиляции. Но смысл Вашего поста понятен...Это я так, придираюсь ))

Хотя, действительно продвинутый компилятор наверное и с const сработается...но это только догадки..
_Pasha
Цитата(sigmaN @ Dec 4 2010, 02:11) *
константный, это const? У меня есть некоторые сомнения по поводу const. Тут наверно правильнее сказать как-то так: определен на этапе компиляции. Но смысл Вашего поста понятен...Это я так, придираюсь ))

Код
void dispatch(const char prio)
{
switch(prio)
{
// doobeedoo
}
}

void otherfunc(char param)
{
  dispatch(0); // тут оптимизирует
dispatch(param);// тут нет
char pp=1;
dispatch(pp);// тут опять(снова) да!
}

где-то так.
_Pasha
Цитата(sigmaN @ Dec 4 2010, 02:11) *
константный, это const? У меня есть некоторые сомнения по поводу const. Тут наверно правильнее сказать как-то так: определен на этапе компиляции. Но смысл Вашего поста понятен...Это я так, придираюсь ))

Код
void dispatch(const char prio)
{
switch(prio)
{
// doobeedoo
}
}

void otherfunc(char param)
{
  dispatch(0); // тут оптимизирует
dispatch(param);// тут нет
char pp=1;
dispatch(pp);// тут опять(снова) да!
}

где-то так.
sergeeff
TO _Pasha :

Сдается мне, что вы формулируете свои советы, основываясь на работе какого-то конкретного компилятора. То, про что вы пишете может быть так, а может и нет.

Даже функции, явно объявленные как inline, могут и не подставляться, если компилятор сочтет это не оптимальным решением. Александреску в одной из последних книг подробно все это рассматривает.
_Pasha
Цитата(sergeeff @ Dec 4 2010, 15:32) *
TO _Pasha :

Сдается мне, что вы формулируете свои советы, основываясь на работе какого-то конкретного компилятора. То, про что вы пишете может быть так, а может и нет.

Даже функции, явно объявленные как inline, могут и не подставляться, если компилятор сочтет это не оптимальным решением. Александреску в одной из последних книг подробно все это рассматривает.

Совершенно верно. Сильное влияние гцц. Однако, там хоть все эти пляски дают детерминированный результат. Чего не скажешь про остальной зоопарк. Вообще-то, как только замечаю "скользкости" - стараюсь их обходить раз и навсегда. Построением программы. Чуть позже потестирую, чтоб не быть голословным, наверняка будет интересно.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.