C99 inline, почему мы не можем просто продолжать использовать его? - PullRequest
0 голосов
/ 24 марта 2020

Я только что узнал, что inline делает мой код быстрее при работе с функциями, но я удивляюсь, почему бы нам просто не встроить каждую функцию, какой смысл писать функции обычным способом, почему у нас такое мощное ключевое слово (я Вы знаете, что компилятор выбирает встроить функцию или нет, но было бы лучше предложить встроить каждую функцию? *

#include <stdio.h>
#include <time.h>


inline double maxf(int a,int b)
{
    if (a>b)
        return a;
    return b;
}

int main() {
    clock_t begin = clock();
    double arr[999999]={0};
    double max=arr[0];
    arr[9898]=99999999;
    for (int i=1;i<999999;i++)
    {
            max=maxf(arr[i],max);
    }
    clock_t end = clock();
    double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
    printf("%lf",time_spent);
    return 0;
}

[ 50%] Building C object CMakeFiles/untitled.dir/main.c.o
[100%] Linking C executable untitled
Undefined symbols for architecture x86_64:
  "_maxf", referenced from:
      _main in main.c.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[3]: *** [untitled] Error 1
make[2]: *** [CMakeFiles/untitled.dir/all] Error 2
make[1]: *** [CMakeFiles/untitled.dir/rule] Error 2
make: *** [untitled] Error 2

Есть идеи, что вызывает эту проблему?

Ответы [ 2 ]

2 голосов
/ 24 марта 2020

Причина, по которой вы получаете ошибку компоновщика в этой программе, заключается в том, что ваша программа использует функцию maxf, которая находится в математической библиотеке, и вы не связываетесь с -lm. Это происходит только при определенных уровнях оптимизации.

Обратите внимание, что компилятор может принять решение не использовать ни inline, ни вызвать maxf, потому что он может видеть, что переменная max не используется, и, следовательно, фактически сохраняет значение в этом нет необходимости. При условии, что компилятор знает, что maxf не имеет видимых побочных эффектов (и он знает, что, поскольку maxf является стандартной библиотечной функцией), он может поэтому исключить присваивание max и, следовательно, вызов maxf. Фактически, компилятор может даже знать, какова семантика maxf, и избежать вызова путем предварительного вычисления во время компиляции.

Чтобы избежать этого случая в своем тесте, вы должны выполнить оба следующих :

  • Измените имя maxf, чтобы оно не совпадало с именем стандартной функции библиотеки. (Ниже я предполагаю, что вы переименовали его в myMax.
  • . Либо объявите переменную max как volatile, либо используйте ее непредсказуемым образом, чтобы хранилище произошло.

Вы также должны исправить прототип своей функции, поскольку она вызывается с аргументом double.

Теперь давайте предположим, что вы соответствующим образом исправили свой тест и скомпилировали его. будет по-прежнему получать ошибку компоновщика при определенных уровнях оптимизации, потому что без оптимизации (или в случае Clang, при уровне оптимизации менее 2) встраивание не выполняется. (В G CC на уровне -O1 функция только встроенный, потому что в модуле перевода есть только один вызов. Если бы он был вызван в двух местах, вам понадобился бы уровень оптимизации 2 для запуска встраивания, как в Clang.)

Но ясно, что есть определение maxf (или, в исправленном коде, myMax или что-то подобное). Так почему компилятор не может его использовать?

Ответ связан с фактической семантикой inline, что может помочь ответить на ваш первый вопрос. Как мы все знаем (и можем видеть в длительном обсуждении выше), компилятор принимает решение о встраивании функций более или менее независимо от предложения программиста в спецификаторе inline. (Сравните это с устаревшим спецификатором register, который компиляторы долгое время игнорировали.)

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

Возможно, в каком-то другом модуле перевода функция myMax вызывается как внешняя функция. В этом случае компилятор должен будет включать в себя компиляцию myMax независимо от того, решил он встроить все варианты использования в этом файле.

С другой стороны, возможно, что myMax предназначен для встроенный в каждую единицу перевода, в которой он появляется; другими словами, все единицы перевода включают определение myMax. Но это может привести к проблеме, потому что эти различные определения столкнутся, когда единицы перевода будут связаны друг с другом, что приведет к другой ошибке компоновщика. (Дублирующее определение имени.) Вы можете обойти это, объявив функцию static, но в этом случае вы можете обнаружить, что каждый из модулей в вашем исполняемом файле имеет свое собственное определение функции c, потому что компилятор выбрал не встроить его в эти модули. Это может значительно увеличить размер исполняемого файла.

И вот тут появляется объявление inline. Объявление функции inline означает «это определение для использования в тех случаях, когда эта функция используется встраиваемый ». Если компилятор видит функцию, объявленную inline без явного объявления extern, он не выдает определение функции , даже если он решает не включать ее . Он полагается на функцию, определяемую в некотором внешнем модуле перевода.

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

В случае из inline функций, вы указываете, что определение функции обязательно должно быть скомпилировано с использованием замедления extern. Если вы объявили myMax как inline в своем заголовочном файле, вы можете удовлетворить это требование, добавив в ровно один файл реализации:

#include "myMax.h"
extern double myMax(double a, double b);

Обратите внимание, что вы определение не нужно - будет использоваться тот, что в заголовке, - но вам нужно получить правильный прототип.

0 голосов
/ 24 марта 2020

проблема в том, что компилятор не видит функцию: maxf() в точке, где код вызывает эту функцию.

Это связано с ключевым словом inline.

Предложите удалить это ключевое слово из сигнатуры функции и вставить прототип, содержащий это ключевое слово, аналогично:

inline double maxf( double a, double b);

double maxf( double a, double b)
{
    if (a>b)
        return a;
    return b;
} 
...