Аллока реализация

Многие объяснения уже присутствуют, чтобы объяснить, как это происходит и как это исправить, но вы также должны следовать рекомендациям, чтобы избежать NullPointerException вообще.

См. также: A хороший список лучших практик

Я бы добавил, очень важно, хорошо использовать модификатор final. Использование "окончательной" модификатор, когда это применимо в Java

Сводка:

  1. Используйте модификатор final для обеспечения хорошей инициализации.
  2. Избегайте возврата null в методы, например, при возврате пустых коллекций.
  3. Использовать аннотации @NotNull и @Nullable
  4. Быстрое завершение работы и использование утверждений, чтобы избежать распространения нулевых объектов через все приложение, когда они не должен быть пустым.
  5. Сначала используйте значения с известным объектом: if("knownObject".equals(unknownObject)
  6. Предпочитают valueOf() поверх toString ().
  7. Используйте null safe StringUtils StringUtils.isEmpty(null).

27
задан dsimcha 3 April 2009 в 17:41
поделиться

8 ответов

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

sub esp, <size>

который является, к сожалению, только половиной изображения. Да, который "выделил бы место на стеке", но существует несколько глюков.

  1. если компилятор испустил код, который ссылается на другие переменные относительно esp вместо ebp (типичный, если Вы компилируете без указателя кадра). Затем те ссылки должны быть скорректированы. Даже с указателями кадра, компиляторы иногда делают это.

  2. что еще более важно, по определению, место, выделенное с alloca должен быть "освобожден", когда функция выходит.

Большой является точкой № 2. Поскольку Вам нужен компилятор для испускания кода для симметричного добавления <size> кому: esp в каждой точке выхода функции.

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

Править:

На самом деле, в glibc (реализация GNU libc). Реализация alloca просто это:

#ifdef  __GNUC__
# define __alloca(size) __builtin_alloca (size)
#endif /* GCC.  */

Править:

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

Править:

Таким образом, я сделал некоторое экспериментирование с вещами как это:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define __alloca(p, N) \
    do { \
        __asm__ __volatile__( \
        "sub %1, %%esp \n" \
        "mov %%esp, %0  \n" \
         : "=m"(p) \
         : "i"(N) \
         : "esp"); \
    } while(0)

int func() {
    char *p;
    __alloca(p, 100);
    memset(p, 0, 100);
    strcpy(p, "hello world\n");
    printf("%s\n", p);
}

int main() {
    func();
}

который, к сожалению, не работает правильно. После анализа блока, произведенного gcc. Кажется, что оптимизация мешает. Проблема, кажется, что, так как оптимизатор компилятора полностью не знает о моем встроенном ассемблерном коде, он имеет привычку к выполнению вещей в неожиданном порядке и все еще ссылке на вещи через esp.

Вот результирующий ASM:

8048454: push   ebp
8048455: mov    ebp,esp
8048457: sub    esp,0x28
804845a: sub    esp,0x64                      ; <- this and the line below are our "alloc"
804845d: mov    DWORD PTR [ebp-0x4],esp
8048460: mov    eax,DWORD PTR [ebp-0x4]
8048463: mov    DWORD PTR [esp+0x8],0x64      ; <- whoops! compiler still referencing via esp
804846b: mov    DWORD PTR [esp+0x4],0x0       ; <- whoops! compiler still referencing via esp
8048473: mov    DWORD PTR [esp],eax           ; <- whoops! compiler still referencing via esp           
8048476: call   8048338 <memset@plt>
804847b: mov    eax,DWORD PTR [ebp-0x4]
804847e: mov    DWORD PTR [esp+0x8],0xd       ; <- whoops! compiler still referencing via esp
8048486: mov    DWORD PTR [esp+0x4],0x80485a8 ; <- whoops! compiler still referencing via esp
804848e: mov    DWORD PTR [esp],eax           ; <- whoops! compiler still referencing via esp
8048491: call   8048358 <memcpy@plt>
8048496: mov    eax,DWORD PTR [ebp-0x4]
8048499: mov    DWORD PTR [esp],eax           ; <- whoops! compiler still referencing via esp
804849c: call   8048368 <puts@plt>
80484a1: leave
80484a2: ret

Как Вы видите, это не настолько просто. К сожалению, я поддерживаю свое исходное утверждение, что Вам нужна помощь компилятора.

52
ответ дан Evan Teran 28 November 2019 в 02:42
поделиться

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

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

7
ответ дан Michael Burr 28 November 2019 в 02:42
поделиться

Для языка программирования D исходный код для alloca () идет с загрузкой. То, как это работает, довольно хорошо прокомментировано. Для dmd1 это находится в/dmd/src/phobos/internal/alloca.d. Для dmd2 это находится в/dmd/src/druntime/src/compiler/dmd/alloca.d.

4
ответ дан Walter Bright 28 November 2019 в 02:42
поделиться

C и стандарты C++ не указывают это alloca() имеет к использованию стек, потому что alloca() не находится в C или стандартах C++ (или POSIX в этом отношении) ¹.

Компилятор может также реализовать alloca() использование "кучи". Например, ARM RealView (RVCT) компилятор alloca() использование malloc() выделить буфер (ссылаемый на их веб-сайте здесь), и также заставляет компилятор испускать код, который освобождает буфер, когда функция возвращается. Это не требует проигрывания с указателем вершины стека, но все еще требует поддержки компилятора.

Microsoft Visual C++ имеет a _malloca() функция, которая использует "кучу", если нет достаточного количества комнаты на стеке, но это требует, чтобы вызывающая сторона использовала _freea(), в отличие от этого, _alloca(), который не нуждается/хочет в явном освобождении.

(С деструкторами C++ в Вашем распоряжении, можно, очевидно, сделать очистку без поддержки компилятора, но Вы не можете объявить локальные переменные в произвольном выражении, таким образом, я не думаю, что Вы могли записать alloca() макрос, который использует RAII. С другой стороны по-видимому, Вы не можете использовать alloca() в некоторых выражениях (как параметры функции) так или иначе.)

¹ Да, законно записать alloca() это просто звонит system("/usr/games/nethack").

4
ответ дан bk1e 28 November 2019 в 02:42
поделиться

alloca непосредственно реализован в ассемблерном коде. Поэтому Вы не можете управлять расположением стека непосредственно с высокоуровневых языков.

Также обратите внимание, что большая часть реализации выполнит некоторую дополнительную оптимизацию как выравнивание стека по причинам производительности. Стандартный способ выделить стековое пространство на X86 похож на это:

sub esp, XXX

Принимая во внимание, что XXX число байтов к allcoate

Править:
Если Вы хотите посмотреть на реализацию (и Вы используете MSVC), см. alloca16.asm и chkstk.asm.
Код в первом файле в основном выравнивает желаемый размер выделения к 16-байтовой границе. Код в 2-м файле на самом деле обходит все страницы, которые принадлежали бы новой области стека и касаются их. Это возможно инициирует исключения PAGE_GAURD, которые используются ОС для роста стека.

3
ответ дан Brad Gilbert 28 November 2019 в 02:42
поделиться

Можно исследовать источники компилятора C с открытым исходным кодом, как Открывают Watcom и находят его сами

1
ответ дан dmityugov 28 November 2019 в 02:42
поделиться

Alloca легок, Вы просто перемещаете указатель вершины стека вверх; затем генерируйте все чтение-записи для указания на этот новый блок

sub esp, 4
-1
ответ дан Ana Betts 28 November 2019 в 02:42
поделиться

Я рекомендую "ввести" инструкцию. Доступный на 286 и более новые процессоры (возможно, было доступно на 186 также, я не могу помнить бесцеремонно, но это не было широко доступно так или иначе).

-2
ответ дан Brian Knoblauch 28 November 2019 в 02:42
поделиться