Непрерывная гарантия памяти с параметрами функции C ++ - PullRequest
9 голосов
/ 29 марта 2011

Appel [App02] очень кратко упоминает, что C ( и, вероятно, C ++) предоставляют гарантии относительно расположения фактических параметров в смежной памяти, а не регистров, когда применяется оператор адреса один из формальных параметров внутри функционального блока.

, например

void foo(int a, int b, int c, int d)
{
    int* p = &a;
    for(int k = 0; k < 4; k++)
    {
       std::cout << *p << " ";
       p++;
    }
    std::cout << std::endl;
}

и вызов, такой как ...

foo(1,2,3,4);

выдаст следующий вывод "1 2 3 4"

Мой вопрос «Как это взаимодействует с соглашениями о вызовах?»

Например, __fastcall в GCC попытается поместить первые два аргумента в регистры, а остальные - в стек. Эти два требования противоречат друг другу, есть ли способ формально рассуждать о том, что произойдет, или это зависит от капризного характера поведения, определяемого реализацией?

[App02] Современная реализация компилятора на Java, Andrew w. Аппель, глава 6, стр. 124

Обновление: Я полагаю, что на этот вопрос дан ответ. Я думаю, что был неправ, когда основывал весь вопрос на непрерывном распределении памяти, когда то, что я искал (и о чем говорит ссылка) - это явное несоответствие между необходимостью сохранения параметров в памяти из-за использования адреса в отличие от регистров из-за соглашений о вызовах, может быть, это вопрос для другого дня.

Кто-то в Интернете не прав, а иногда это кто-то я.

Ответы [ 4 ]

3 голосов
/ 29 марта 2011

Прежде всего ваш код не всегда выдает 1, 2, 3, 4. Просто отметьте это: http://ideone.com/ohtt0 Правильный код, по крайней мере, похож на this :

void foo(int a, int b, int c, int d)
{
    int* p = &a;
    for (int i = 0; i < 4; i++)
    {
        std::cout << *p;
        p++;
    }
}

Так что теперь давайте попробуем с fastcall, здесь :

void __attribute__((fastcall)) foo(int a, int b, int c, int d)
{
    int* p = &a;
    for (int i = 0; i < 4; i++)
    {
        std::cout << *p << " ";
        p++;
    }
}

int main()
{
        foo(1,2,3,4);
}

Результат неясен: 1 -1216913420 134514560 134514524

Так что я действительно сомневаюсь, что что-томожно гарантировать здесь.

1 голос
/ 29 марта 2011

В стандарте нет ничего о соглашениях о вызовах или о том, как передаются параметры.

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

Это определенно не влияет на другие переменные, чьи адреса не берутся.

1 голос
/ 29 марта 2011

Стандарт C ++ не имеет понятия соглашения о вызовах.Осталось разобраться с компилятором.

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

Компилятору решать, что делать.Однако я думаю, что большинство компиляторов отдают предпочтение вашим требованиям над стандартами.

0 голосов
/ 29 марта 2011

Ваше основное предположение неверно.На моей машине foo(1,2,3,4) с использованием вашего кода распечатывает:

1 -680135568 32767 4196336

Использование g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3 на 64-битной x86.

...