Создание указателя на функцию alloca вызывает ошибки компоновщика? - PullRequest
3 голосов
/ 15 марта 2019

Я пытаюсь написать функцию, которой передается функция для использования в качестве аргумента для выделения;он должен принимать любой допустимый распределитель типа void *(*)(size_t).Однако я испытываю странное поведение при попытке использовать alloca в качестве распределителя - создание указателя на функцию alloca компилируется нормально, но приводит к ошибкам компоновщика:

#include <stdlib.h>
#include <alloca.h>

int main() {
  void *(*foo)(size_t) = alloca;
}

приводит к

/tmp/cc8F67yC.o: In function `main':
test15.c:(.text+0x8): undefined reference to `alloca'
collect2: error: ld returned 1 exit status

Это как-то связано с встраиванием alloca?Но встраивание не будет выполнено только как оптимизация, когда функции не нужен адрес.Фактически, с помощью GCC я даже могу написать свою собственную версию, которая работает, как и ожидалось, в приведенном выше коде:

static inline void *alloca(size_t n) {
  return __builtin_alloca(n);
}

Есть ли причина, по которой стандартная версия не ведет себя так же?

Ответы [ 3 ]

6 голосов
/ 15 марта 2019

Кто говорит вашу функцию

static inline void *alloca(size_t n) {
    return __builtin_alloca(n);
}

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

5 голосов
/ 15 марта 2019

Цитирование справочных страниц из здесь :

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

На странице также упоминается:

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

2 голосов
/ 15 марта 2019

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

Обратите внимание, что нет стандартной версии из alloca. Ни Стандарт C, ни POSIX не описывают эту функцию.

Альтернатива, которую вы предоставляете, с alloca, переопределенным как встроенная функция, вызывающая __builtin_alloca, не работает: среди других проблем указатель, возвращаемый __builtin_alloca(), действителен только до тех пор, пока вызывающая программа не вернется, будет ли она встроена или нет .

Страница man linux очень явная:

[...]

* * ОПИСАНИЕ тысяча двадцать-один

Функция alloca() выделяет размер байта пространства в стеке. рамка звонящего. Это временное пространство автоматически освобождается когда функция, вызвавшая alloca(), возвращается к своему вызывающему.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

Функция alloca() возвращает указатель на начало выделенное пространство. Если выделение вызывает переполнение стека, программа поведение не определено.

[...]

СООТВЕТСТВУЮЩИМ

Эта функция отсутствует в POSIX.1.

Есть доказательства того, что функция alloca() появилась в 32 В, PWB, PWB.2, 3BSD и 4BSD. В 4.3BSD для этого есть справочная страница. Linux использует версию GNU.

ПРИМЕЧАНИЯ

Функция alloca() зависит от машины и компилятора. За В некоторых случаях его использование может повысить эффективность по сравнению с использование malloc(3) плюс free(3). В некоторых случаях это может также упростить освобождение памяти в приложениях, использующих longjmp(3) или siglongjmp(3). В противном случае его использование не рекомендуется.

Поскольку пространство, выделенное alloca(), выделено в стеке кадр, это пространство автоматически освобождается, если функция возвращает перепрыгнул через звонок на longjmp(3) или siglongjmp(3).

Пространство, выделенное alloca(), не освобождается автоматически, если указывающий на него указатель просто выходит из области видимости.

Не пытайтесь free(3) место, выделенное alloca()!

Замечания по версии GNU

Обычно gcc (1) переводит вызовы на alloca() с встроенным кодом. Это не делается, когда -ansi, -std=c89, -std=c99 или Опция -std=c11 указана, а заголовок <alloca.h> не включен. В противном случае (без опции -ansi или -std=c*) версия glibc <stdlib.h> включает <alloca.h> и содержит строки:

      #ifdef  __GNUC__
       #define alloca(size)   __builtin_alloca (size)
       #endif

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

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

Встроенный код часто состоит из одной инструкции, регулирующей указатель стека и не проверяет переполнение стека. Таким образом, есть нет NULL ошибка возврата.

ОШИБКА

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

Во многих системах alloca() нельзя использовать внутри списка аргументов. вызова функции, потому что пространство стека зарезервировано alloca() появится в стеке в середине пространства для функции аргументы.

...