Re: В чём разница ссылки от указателя?
От: shrkn Россия  
Дата: 11.05.06 18:06
Оценка:
Доброго времени суток всем. Почитал диалоги на форуме, да впринципе почти все уже сказали и
выяснили, что есть ссылка что есть указатель, остается только просуммировать и подытожить.
Тем не менее были и грубые ошибки типа:


class TC
{
 public:
  //ссылка x будет занимать место в стековой памяти
  // ... если компилятор не встроит конструктор в точку вызова (я про inline)
  TC(int& x):m_rx(x){;}

 private:
  int& m_rx;//вот тут память под ссылку тоже однозначно будет выделена
};//class TC



Этот пример даже не скомпилируется, потому что ссылка инициализируется сразу.
Но это не суть важно важно то что в споре рождается истина а истина — новое
знание, другое дело имеет это знание практическое применение.

Это тоже не откомпилируется:


struct    STR{ 
            int    a;
            float  b;
            int&   la=a;
            float& lb=b;
            }



ссылки как оказывается имеют ограниченную область применения. Об этом позже.

Вопрос был такой:

"Недавно один товарищ на мой вопрос по языку С++ заметил «У… батенька мы даже не знаем разницы между
ссылкой и указателем! Темнота!..» Для меня, честно говоря, это оказалось откровением. В литературе я
не встречал чёткого разграничения этих понятий и воспринимал их как синонимы. Поиски ничего не дали.
Так в чём же разница?"

Здесь можно подойти к ответу с двух основных точек зрения:
— как, зачем и когда использовать указатели и ссылки в программе, каковы их сходства и отличия;
— как компилятор оптимизирует представление этих объектов в машинном коде (напр исп памяти);

Рассмотрим по порядку.

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

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

Главный идеолог языка С++ Бьерн Страуструп в своей общеизвестной книге "Язык программирования С++"
отмечает следующее (цитирую):

1) Ни один оператор не выполняет действий над ссылкой;
2) Инициализация ссылки отличается от присваивания ей значения;
3) Реализована ссылка как константный указатель при каждом использовании которого происходит разыменование;
4) В некоторых случаях компилятор может оптимизировать ссылку таким образом, что во время выполнения вообще не
будет существовать объекта представляющего ссылку;


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

Но все же интересно, как представляются в памяти ссылки если они вообще представляются. Бьерн говорит об
оптимизации компилятором кода, но не исключает возможности размещения ссылок в памяти.
Что ж лучший ответ на этот вопрос даст сам компилятор. Обратимся к нему. Ниже я привожу код на С++ который
мы проанализируем после дизасемблирования. Цель посмотреть что представляют собой ссылки при передаче
значений параметров из функций сравнить это же с указателями. Как ссылки определяются в программе и что этому
соответствует в машинном коде. Пример создавался и тестился в среде VS2005, (пример с классами и прочими
конструкциями только усложнит анализ) все ключи МАKE-раздела компиляции и сборки оставлены по умолчанию.
Каждый может проверить это собственноручно. Итак:


void link_use(int& l1, int& l2, float& l3)
{
  l1 = 100;
  l2 = 200;
  l3 = 0.3;
}

void point_use(int* l1, int* l2, float* l3)
{
  *l1 = 1000;
  *l2 = 2000;
  *l3 = 0.03;
}

void noret_use(int l1, int l2, float l3)
{
  l1 = 100;
  l2 = 200;
  l3 = 0.3;
}

int _tmain(int argc, _TCHAR* argv[])
{   
     int i1=0, 
         i2=0;
  float  i3=0;
  float& l3=i3;
  const  int& li2 = 10;
    
  link_use(i1, i2, i3);
  point_use(&i1, &i2, &i3);
  noret_use(i1, i2, i3);

  return 0;
}




Дизасемблируем (>Debug.Disassembly) и получаем:


--- c:\documents and settings\aan\my documents\visual studio 2005\projects\links\links\links.cpp 

int _tmain(int argc, _TCHAR* argv[])
{   
1  004135E0  push        ebp  
2  004135E1  mov         ebp,esp 
3  004135E3  sub         esp,108h 
4  004135E9  push        ebx  
5  004135EA  push        esi  
6  004135EB  push        edi  
7  004135EC  lea         edi,[ebp-108h] 
8  004135F2  mov         ecx,42h 
9  004135F7  mov         eax,0CCCCCCCCh 
10 004135FC  rep stos    dword ptr es:[edi] 

    int i1=0, 

11 004135FE  mov         dword ptr [i1],0 

        i2=0;

12 00413605  mov         dword ptr [i2],0 

    float i3=0;

13 0041360C  fldz             
14 0041360E  fstp        dword ptr [i3] 

    float& l3=i3;

15 00413611  lea         eax,[i3] 
16 00413614  mov         dword ptr [l3],eax 

    const int& li2 = 10;

17 00413617  mov         dword ptr [ebp-44h],0Ah 
18 0041361E  lea         eax,[ebp-44h] 
19 00413621  mov         dword ptr [li2],eax 
    
    link_use(i1, i2, i3);

20 00413624  lea         eax,[i3] 
21 00413627  push        eax  
22 00413628  lea         ecx,[i2] 
23 0041362B  push        ecx  
24 0041362C  lea         edx,[i1] 
25 0041362F  push        edx  
26 00413630  call        link_use (411118h) 
27 00413635  add         esp,0Ch 

    point_use(&i1, &i2, &i3);

28 00413638  lea         eax,[i3] 
29 0041363B  push        eax  
30 0041363C  lea         ecx,[i2] 
31 0041363F  push        ecx  
32 00413640  lea         edx,[i1] 
33 00413643  push        edx  
34 00413644  call        point_use (4111C7h) 
35 00413649  add         esp,0Ch 

    noret_use(i1, i2, i3);

36 0041364C  push        ecx  
37 0041364D  fld         dword ptr [i3] 
38 00413650  fstp        dword ptr [esp] 
39 00413653  mov         eax,dword ptr [i2] 
40 00413656  push        eax  
41 00413657  mov         ecx,dword ptr [i1] 
42 0041365A  push        ecx  
43 00 41365B  call        noret_use (4111CCh) 
44 00413660  add         esp,0Ch 

    return 0;

45 00413663  xor         eax,eax 
}


Что же здесь мы видим? Для определения float& l3=i3;
в строках 15 и 16 ясно видно что никакого места
под ссылку не выделяется, (если конечно вспомним что делает команда lea)
А вот далее интереснее

Для конструкции const int& li2 = 10;
в строке 17 используется память для
хранения константы. А далее в строке 18 получаем смещение этой ячейки,
а встроке 19 (внимание) используется ячейка памяти для хранения ссылки
на константу. Причем рамер ее как видно 32 бита. Наталкивает на мысль
что это ни что иное как указатель, который нельзя поменять. Конечно
кто-то может поспорить что это некорректное определение константы, но это
работает и Бьерн приводит такой же пример задания ссылки.

Далее анализируя вызовы функций приходим к выводу что они абсолютно идентичны
для указателей и для ссылок. Для передачи параметров из функций используется стек.
Очевидно не так ли.

Что же мы имеем в итоге. Оказывается память под ссылку может как выделяться так и не
выделяться. Это зависит от написанного кода. В общем случае конечно.
Есть еще такое как оптимизация и около 200 различных С компиляторов. И каждый
может по своему это оптимизировать.
Но передачей параметров ясно, что стек используется.

Кроме оптимизации и определенно написанного исходника есть еще стандарт, но это
уже тема отдельного разговора. Отмечу только то что в компиляторах
реализованных по стандарту ISO/IEC 14882, ссылки задаются и работают корректно,
так что приведенные вначале примеры вообще не скомпилируются, а пример со структурой
в которой ссылки, компилятор VS2005 выведет вот что:

Error 1 error C2327: 'STR::a' : is not a type name, static, or enumerator
c:\documents and settings\aan\my documents\visual studio 2005\projects\links\links\links.cpp 10

Error 2 error C2065: 'a' : undeclared identifier
c:\documents and settings\aan\my documents\visual studio 2005\projects\links\links\links.cpp 10

Error 3 error C2864: 'STR::la' : only static const integral data members can be initialized within a class
С:\documents and settings\aan\my documents\visual studio 2005\projects\links\links\links.cpp 10

Error 4 error C2327: 'STR::b' : is not a type name, static, or enumerator
c:\documents and settings\aan\my documents\visual studio 2005\projects\links\links\links.cpp 11

Error 5 error C2065: 'b' : undeclared identifier
c:\documents and settings\aan\my documents\visual studio 2005\projects\links\links\links.cpp 11

Error 6 error C2864: 'STR::lb' : only static const integral data members can be initialized within a class
c:\documents and settings\aan\my documents\visual studio 2005\projects\links\links\links.cpp 11



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

В заключении отмечу, что ни один C++ компилятор на 100% не соответствует ни одному стандарту, более того
часто отступают от него, добавляют свои конструкции, и майкросовтовский тут не исключение.
Re[2]: В чём разница ссылки от указателя?
От: igna Россия  
Дата: 12.05.06 06:58
Оценка:
Здравствуйте, shrkn, Вы писали:

S>class TC
S>{
S> public:
S>  //ссылка x будет занимать место в стековой памяти
S>  // ... если компилятор не встроит конструктор в точку вызова (я про inline)
S>  TC(int& x):m_rx(x){;}

S> private:
S>  int& m_rx;//вот тут память под ссылку тоже однозначно будет выделена
S>};//class TC


S> Этот пример даже не скомпилируется ...


Но попробовать можно. Здесь: http://www.comeaucomputing.com/tryitout/
Re[3]: В чём разница ссылки от указателя?
От: shrkn Россия  
Дата: 12.05.06 12:55
Оценка:
Здравствуйте, igna, Вы писали:

I>Здравствуйте, shrkn, Вы писали:


I>
S>>class TC
S>>{
S>> public:
S>>  //ссылка x будет занимать место в стековой памяти
S>>  // ... если компилятор не встроит конструктор в точку вызова (я про inline)
S>>  TC(int& x):m_rx(x){;}

S>> private:
S>>  int& m_rx;//вот тут память под ссылку тоже однозначно будет выделена
S>>};//class TC
I>


S>> Этот пример даже не скомпилируется ...


I>Но попробовать можно. Здесь: http://www.comeaucomputing.com/tryitout/


Да согласен этот пример откомпилиться, вполне, даже на VS2005, я что-то наверно не досмотрел
когда проверял, так что извиняйте. Но про ссылки и выделение памяти все так же в силе.

В классах конструктор может выполнять функции инициализации ссылок.
Re[4]: В чём разница ссылки от указателя?
От: igna Россия  
Дата: 12.05.06 13:21
Оценка:
Здравствуйте, shrkn, Вы писали:

S>... Но про ссылки и выделение памяти все так же в силе.


S> Это тоже не откомпилируется:


S>struct    STR{ 
S>            int    a;
S>            float  b;
S>            int&   la=a;
S>            float& lb=b;
S>            }


А это?:

struct    STR{ 
            int    a;
            float  b;
            int*   la=&a;
            float* lb=&b;
            }


Или даже это:

struct    STR{ 
            int    a;
            float  b;
            int*   la=0;
            float* lb=0;
            }


Re[2]: В чём разница ссылки от указателя?
От: igna Россия  
Дата: 12.05.06 13:29
Оценка:
Здравствуйте, shrkn, Вы писали:

S> Это тоже не откомпилируется:


S>struct    STR{ 
S>            int    a;
S>            float  b;
S>            int&   la=a;
S>            float& lb=b;
S>            }

S> ссылки как оказывается имеют ограниченную область применения.

Может быть должно быть так?:

struct    STR{ 
            int    a;
            float  b;
            int&   la;
            float& lb;
            STR() : la(a), lb(b) {}
            };
Re[2]: В чём разница ссылки от указателя?
От: Naruto  
Дата: 02.07.06 10:40
Оценка:
Здравствуйте, shrkn, Вы писали:


S>
S>class TC
S>{
S> public:
S>  //ссылка x будет занимать место в стековой памяти
S>  // ... если компилятор не встроит конструктор в точку вызова (я про inline)
S>  TC(int& x):m_rx(x){;}

S> private:
S>  int& m_rx;//вот тут память под ссылку тоже однозначно будет выделена
S>};//class TC
S>



S> Этот пример даже не скомпилируется, потому что ссылка инициализируется сразу.


vis c++.net 7.1 компилиться за милую душу..
может это я что то не так делаю?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: В чём разница ссылки от указателя?
От: D.Lans Россия  
Дата: 02.07.06 19:40
Оценка:
Здравствуйте, alex_ant, Вы писали:

_>Недавно один товарищ на мой вопрос по языку С++ заметил «У… батенька мы даже не знаем разницы между ссылкой и указателем! Темнота!..» Для меня, честно говоря, это оказалось откровением. В литературе я не встречал чёткого разграничения этих понятий и воспринимал их как синонимы. Поиски ничего не дали. Так в чём же разница?
Re[2]: В чём разница ссылки от указателя?
От: D.Lans Россия  
Дата: 02.07.06 19:46
Оценка:
Глюк случился..
Вместо моей мессаги отправилась какая-то левая.
Тем не менее, повторю вопрос.
Я долгое время работал только на бейсике, паскале, знал что такое переменная, константа и радовался жизни.
Теперь же, изучая C++, меня подвергли в шок такие понятия как ссылка, указатель. Я пытался понять что это такое, ища ответ в трех разных учебниках, но так и не понял (после паскаля, бейсика,мне сложно все это понять).
Поясните плиз или подскажите, где можно объяняются эти понятия человеческим, нормальным языком.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.