Как компиляторы C++ на самом деле передают параметры ссылки?

Этот вопрос появился в результате некоторого многоязыкового программирования. У меня была стандартная программа Фортрана, которую я хотел назвать из кода C++. Фортран передает все свои параметры ссылкой (если Вы не говорите это иначе).

Таким образом, я думал, что буду умен (плохо запускаются тут же) в моем C++, кодируют и определяют стандартную программу Фортрана что-то вроде этого:

extern "C" void FORTRAN_ROUTINE (unsigned & flag);

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

Другой инженер приехал позади меня и решил проблему, объявив, что стандартная программа должна была быть определена в C++ как

extern "C" void FORTRAN_ROUTINE (unsigned * flag);

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

Таким образом, вопрос, как C++ на самом деле передает параметры ссылки? Это, возможно, свободно сделать копию - в, копию для маленьких значений или чего-то? Другими словами, действительно ли параметры ссылки совершенно бесполезны в многоязыковом программировании? Я хотел бы знать, таким образом, я не делаю эту ту же уничтожающую код ошибку никогда снова.

15
задан sbi 30 May 2010 в 09:24
поделиться

4 ответа

Просто чтобы вмешаться, я считаю, что вы правы. Я постоянно использую ссылки для передачи параметров в функции Fortran. По моему опыту, использование ссылок или указателей на интерфейсе Fortran-C++ эквивалентно. Я пробовал это с помощью GCC/Gfortran и Visual Studio/Intel Visual Fortran. Возможно, это зависит от компилятора, но я думаю, что практически все компиляторы реализуют ссылки через передачу указателей.

4
ответ дан 1 December 2019 в 04:17
поделиться

C++ не определяет, какими должны быть реализации, это просто язык. Поэтому не существует "реализации" ссылок.

Тем не менее, ссылки реализуются с помощью указателей. Это приводит к большой путанице ("ссылки - это просто указатели", "ссылки - это просто указатели с убранными мирскими частями"), но это не так. Ссылки - это псевдонимы и всегда будут псевдонимами.

Компилятор будет передавать адрес переменной и работать с этим указателем. Это имеет тот же эффект (но не ту же семантику!). Чтобы быть более конкретным, компилятор может "заменить" this:

void make_five(int& i)
{
    i = 5;
}

int main(void)
{
    int i = 0;
    make_five(i);
}

на this:

void make_five(int* const i)
{
    *i = 5;
}

int main(void)
{
    int i = 0;
    make_five(&i);
}

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

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

Чтобы получить аналогичную семантику (поскольку вы теперь указываете на переменную, а не псевдоним), вы должны убедиться, что значение указателя не равно null:

extern "C" void FORTRAN_ROUTINE (unsigned * flag)
{
    assert(flag); // this is normally not a problem with references, 
                  // since the address of a variable cannot be null.

    // continue...
}

На всякий случай.

9
ответ дан 1 December 2019 в 04:17
поделиться

Никакой разницы.

unsigned & flag

точно так же, как вы бы написали

unsigned * const flag

за исключением операторов доступа к членам объекта ("." и "->" соответственно).

1
ответ дан 1 December 2019 в 04:17
поделиться

Теоретически в C ++ ссылки реализуются как обычные указатели. Затем компилятор изменяет код функции tonehave как ссылка, но загружает адрес и затем косвенно перемещает адрес.

Вот небольшое приложение:

void foo( int & value )
{
    value = 3;
}


void bar( int *value )
{
    *value = 3;
}

void do_test()
{
    int i;
    foo(i);
    bar(&i);
}

Давайте соберем его и посмотрим на сгенерированную сборку gcc (gcc -s):

        .file   "test-params.cpp"
        .text
.globl _Z3fooRi
        .type   _Z3fooRi, @function
_Z3fooRi:
.LFB0:
        .cfi_startproc
        .cfi_personality 0x0,__gxx_personality_v0
        pushl   %ebp
        .cfi_def_cfa_offset 8
        movl    %esp, %ebp
        .cfi_offset 5, -8
        .cfi_def_cfa_register 5
        movl    8(%ebp), %eax
        movl    $3, (%eax)
        popl    %ebp
        ret
        .cfi_endproc
.LFE0:
        .size   _Z3fooRi, .-_Z3fooRi
.globl _Z3barPi
        .type   _Z3barPi, @function
_Z3barPi:
.LFB1:
        .cfi_startproc
        .cfi_personality 0x0,__gxx_personality_v0
        pushl   %ebp
        .cfi_def_cfa_offset 8
        movl    %esp, %ebp
        .cfi_offset 5, -8
        .cfi_def_cfa_register 5
        movl    8(%ebp), %eax
        movl    $3, (%eax)
        popl    %ebp
        ret
        .cfi_endproc
.LFE1:
        .size   _Z3barPi, .-_Z3barPi
.globl _Z7do_testv
        .type   _Z7do_testv, @function
_Z7do_testv:
.LFB2:
        .cfi_startproc
        .cfi_personality 0x0,__gxx_personality_v0
        pushl   %ebp
        .cfi_def_cfa_offset 8
        movl    %esp, %ebp
        .cfi_offset 5, -8
        .cfi_def_cfa_register 5
        subl    $20, %esp
        leal    -4(%ebp), %eax
        movl    %eax, (%esp)
        call    _Z3fooRi
        leal    -4(%ebp), %eax
        movl    %eax, (%esp)
        call    _Z3barPi
        leave
        ret
        .cfi_endproc
.LFE2:
        .size   _Z7do_testv, .-_Z7do_testv
        .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
        .section        .note.GNU-stack,"",@progbits

Как вы можете видеть, в обеих функциях компилятор читает стек ( movl 8 (% ebp),% eax ), и в обоих вызовах компилятор сохраняет адрес в стек ( leal -4 (% ebp),% eax) .

Ответ GMan - "Сохраните Unico" о декларации C может быть проблемой. Кажется, проблема в совместимости между C и fortran (по крайней мере, теми двумя компиляторами, которые вы используете).

2
ответ дан 1 December 2019 в 04:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: