Возврат в инкапсулированную встроенную функцию - PullRequest
0 голосов
/ 25 августа 2018

Я очищаю некоторый код в драйвере (Netgear A6210), написанном на C, и натолкнулся на вспомогательную функцию VIRTUAL_IF_DOWN(), которая принудительно встроена (например, __inline вместо inline)и содержит, казалось бы, произвольный оператор return в конце.

__inline void VIRTUAL_IF_DOWN(void *pAd)
{
    /* Some code here */
    return ;
}

Однако эта вспомогательная функция вызывается в теле двух других функций, прежде чем управление возвращается к остальной части программы, поэтомуМой вопрос заключается в том, является ли это выражение возврата встроенным в остальную часть функции, таким образом, выпадает из функции большего размера или просто ничего не делает?Мое общее правило для встроенных функций заключается в том, что я всегда должен рассматривать их как отдельные функции и не предполагать, что они будут встроены как есть, в любом случае, я привел одну из инкапсулирующих функций в качестве примера:

static void rtusb_disconnect(struct usb_interface *intf)
{
    /* Some code here and then an ugly looking preprocessor branch */       
#ifdef IFUP_IN_PROBE
    VIRTUAL_IF_DOWN(pAd); // Function is used here
#endif
    /* Other code here */
}

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

Другая часть моего вопроса была бы такова: определяется ли встраивание на этапе препроцессора или позже, например на этапе ассемблера или компоновщика?

Ответы [ 2 ]

0 голосов
/ 25 августа 2018

Встраивание - это больше, чем просто копирование кода вставки в отличие от предварительной обработки макроса.

Когда компилятор сталкивается с какой-либо директивой inlining, он оценивает, что возвращает или делает встроенная функция.

Вы можете использовать такие сайты, как godbolt.org , чтобы увидеть, какая сборка генерируется для кода C. Например, следующие функции оценивают один и тот же код сборки:

#include <stdio.h>

inline void test1(int number){
    printf("%d", number);
    return;
}

inline int test2(){
    return 1+1;
}

void doSomething() {
    test1(test2());
}

void doSomethingElse() {
    printf("%d", 2);
}

и сборка:

.LC0:
        .string "%d"
_Z11doSomethingv:
        sub     rsp, 8
        mov     esi, 2
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        add     rsp, 8
        ret
_Z15doSomethingElsev:
        sub     rsp, 8
        mov     esi, 2
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        add     rsp, 8
        ret

Вы можете увидеть этот пример на https://godbolt.org/z/rBULdo

Следует также отметить, что встраивание - это оптимизация компилятора. Различные флаги компиляции могут привести к различным результатам от встраивания.

Это также зависит от того, сколько информации доступно во время компиляции в зависимости от времени выполнения. Если компилятор знает больше информации во время компиляции, он может оптимизировать лучше, чем если бы все было известно только во время выполнения.

См. ссылку для поведения GCC при обнаружении атрибута inline

0 голосов
/ 25 августа 2018

return; в конце функции void является избыточной.

Встраивание сохраняет семантику: если return возвращается из не встроенного вызова, то return во встроенной функции возвращается извстроенный «вызов», т. е. он переходит к концу встроенного тела.

Встраивание происходит во время компиляции, т. е. после предварительной обработки и анализа, но до генерации и сборки кода.

...