Q:

Передача по ссылке - тождественна ли передаче указателя?

Читал не раз, что передача по ссылке - это просто передача указателя, который внутри функции автоматически разыменовывается. Насколько это верно?
Например, я написал такую dll (в Visual Studio):
extern "C"
void __declspec(dllexport) test(int* i)
{
    cout << "test(" << *i << ")\n";
    (*i)++;
}

И вот такую программу для работы с ней:
typedef void (*myfunc)(int&);
int main()
{
    HMODULE dll;
    if ((dll = LoadLibrary("Dll.dll")) != NULL)
    {
        myfunc f = (myfunc)GetProcAddress(dll,"test");
        int q = 5;
        cout << "q = " << q << endl;
        f(q);
        cout << "q = " << q << endl;
        FreeLibrary(dll);
    }
}

Все работает, получается что я и хотел:
q = 5
test(5)
q = 6

Только насколько на эту передачу по ссылке как указателя можно полагаться? Какие могут быть неприятности?
И еще сразу хотел спросить, у меня, например, в dll используется cout - это тот же cout, что и в основной программе или нет? А если я передам на него указатель - то все равно ведь будет использоваться код в dll, а не в программе? Или если я выделю в dll память, то как ее правильно освободить в программе? например, если я верну unique_ptr в функции в dll?

A:

Вы не имеете права предполагать, что ссылка внутри устроена как указатель. Декларация функции, отличающаяся от её настоящего определения, есть undefined behaviour.
То, что это «работает» — случайность, как и при любом UB. С усложнением программы и/или изменением настроек компилятора возможны любые неприятные сюрпризы.

То, что в приведённых вами словах верно — это то, что ссылка ведёт себя во многом как автоматически разыменуемый указатель.
Важное семантическое отличие между указателем и ссылкой — ссылку нельзя переназначить, и она обязательно указывает на какой-то объект (в корректной программе). То есть, не существует «null-ссылок».

A:

По первой части - думаю, что в стандарте никаких указаний нет и не должно быть. Откровенно говоря, не думаю, что если такая передача в VC++ получается успешно, то в какой-то момент перестанет быть таковой... но вполне допускаю, что другой компилятор может работать и иначе. Поэтому, как мне кажется, в пределах одного компилятора - грубо говоря, в своей программе - пользоваться в принципе можно... отдавая отчет в том, что теоретически это может сломаться в каких-то будущих версиях.
Но не проще ли использовать лишний уровень косвенности? Просто написать функцию-посредник, принимающую ссылку, но вызывающую функцию DLL с передачей ей адреса этой ссылки?
По второй части - если вы использовали статические библиотеки, то да, cout у вас разные, если же динамический runtime - то, думаю, один и тот же, из динамических библиотек.
По поводу памяти - насколько я помню советы опытных :), крайне желательно освобождать память там, где она была выделена. Но как с этим согласовать те же интеллектуальные указатели или что-то иное - простой и надежный способ в голову пока не приходит.

A:

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

В случае использования динамически подключаемых библиотек, строгой рекомендацией является использование С интерфейса (в С существуют только указатели), дабы иметь возможность работать с библиотекой из других языков. Не стоит в данном случае возвращать std::unique_ptr<>.  
Также насчёт выделения памяти, если Вы выделяете память внутри библиотеки, то внутри и должны освобождать. 
Использование умных указателей в данном случае - современный подход слежения за памятью. Старайтесь строго избегать явных вызовов delete.
Прежде всего стоит определиться нужно действительно нужно ли выделять память внутри библиотеки или допустимо передать ей буфер и его размер.

Если всё же библиотеке нужно выделять память и возвращать её, то допустим мы имеем две функции:
void* dll_alloc(size_t size) и dll_free(void* ptr)
Тогда для удобной работы и автоматическим освобождением памяти, можем использовать следующее:
std::shared_ptr<void> dllMemory(dll_alloc(1024), dll_free);
В таком случае, нам не нужно явно вызывать dll_free.

