C ++ bir göstərici dəyişən və istinad dəyişən arasında fərqlər hansılardır?

Linklərin sintaktik şəkər olduğunu bilirəm, buna görə kodu oxumaq və yazmaq daha asandır.

Ancaq fərqlər nədir?


Aşağıdakı cavablar və bağlantıların xülasəsi:

  1. Bağlayıcı bağlamadan sonra bir link yenidən təyin edilə bilməyincə, göstərici bir neçə dəfə yenidən təyin edilə bilər.
  2. İşaretçiler hər yerdə ( NULL ) işarə edə bilmirlər, buna isə bir keçid həmişə bir obyektə aiddir.
  3. Bağlantının ünvanını mümkün olduğundan, göstəricilərdən istifadə edə bilməzsiniz.
  4. Heç bir "istinad hesabı" yoxdur (ancaq + 5 də olduğu kimi əlaqə nöqtələrinin olduğu obyektin ünvanını və üzərinə pointer aritmetiğini göstərə bilərsiniz).

Səhvliyi təmizləmək üçün:

C ++ standartları, kompilyatorun referansları necə tətbiq edə biləcəyini diktə etməmək üçün çox diqqətli olur, lakin hər C ++ tərtibçisi istinad göstəriciləri kimi tətbiq edir. Yəni aşağıdakı kimi bir bəyanat:

 int  = i; 

tam optimallaşdırılmırsa , göstərici kimi eyni miqdarda yaddaş ayırır və i qeydini bu depoda yerləşdirir.

Beləliklə, pointer və link eyni miqdarda yaddaş istifadə.

Ümumi qayda olaraq,

  • Faydalı və özünü sənədləşdirən interfeyslər təmin etmək üçün funksiya parametrləri və qaytarma növlərindən istifadə edin.
  • Alqoritmlər və məlumat strukturlarını tətbiq etmək üçün göstəriciləri istifadə edin.

Maraqlı oxu:

2802
11 сент. 11 Sentyabr prakash qurdular 2008-09-11 23:03 '08 at 11:03 pm 2008-09-11 23:03
@ 37 cavab
  • 1
  • 2
  • Göstərici yenidən təyin edilə bilər:

     int x = 5; int y = 6; int *p; p =  p =  *p = 10; assert(x == 5); assert(y == 10); 

    Bağlantı başlanğıc zamanı təyin edilə bilməz və verilməlidir:

     int x = 5; int y = 6; int  = x; 
  • İşarəçi öz ünvanını və yığındakı yaddaşın ölçüsünü (x86-da 4 bayt), link isə eyni yaddaş ünvanına malikdir (orijinal dəyişənlə), həm də yığında bir yer tutur. Əlaqənin orijinal dəyişənlə eyni ünvanı olduğundan, link eyni dəyişən üçün fərqli bir ad olaraq düşünmək təhlükəsizdir. Qeyd Göstərici göstəricinin yığının üstündə və ya yığında ola bilər. Eyni keçid. Bu ifadədə mənim iddiam göstərici yığını göstərməməsi deyil. Göstərici sadəcə bir yaddaş ünvanı olan dəyişəndir. Bu dəyişən yığındadır. Bağlantı öz yığını sahəsi olduğundan və ünvan dəyişən ilə eyni olduğu üçün. Stack vs yığın haqqında ətraflı oxuyun. Bu, derivin sizə xəbər verməyəcəyinə dair real link ünvanı deməkdir.

     int x = 0; int  = x; int *p =  int *p2 =  assert(p == p2); 
  • Əlavə səviyyələr indirection təklif göstərən göstəricilər üçün göstəriciləri ola bilər. Bağlantılar birbaşa indirection təklif edir.

     int x = 0; int y = 0; int *p =  int *q =  int **pp =  pp =  = q **pp = 4; assert(y == 4); assert(x == 0); 
  • Göstərici birbaşa nullptr təyin edilə bilər, ancaq link verə bilməz. Əgər kifayət qədər sərt çalışırsanız və bunu necə edəcəyinizi bilirsinizsə, link nullptr ünvanını edə bilərsiniz. Eynilə, əgər kifayət qədər nullptr , bir göstərici ilə əlaqə qura bilərsiniz və sonra da bu link nullptr ola bilər.

     int *p = nullptr; int  = nullptr; <--- compiling error int  = *p; <--- likely no compiling error, especially if the nullptr is hidden behind a function call, yet it refers to a non-existent int at address 0 
  • Göstəricilər bir sıra üzərində yineleyə bilər, göstəricidən göstərilən növbəti elementə keçmək üçün ++ istifadə edə bilərsiniz və + 4 5-ci maddəə getmək üçün + 4 istifadə edə bilərsiniz. Bu obyektin işarə etdiyinə işarə edən nə ölçüsündən asılı olmayaraq.

  • Göstərici, qeyd etdiyimiz yaddaş ünvanına daxil olmaq üçün * istifadə edərək, birbaşa istifadə edilə bilər. Sinifə / strukturun bir göstəricisi - istifadə etmək üçün istifadə etmək üçün -> istifadə edir . .

  • A pointer yaddaş ünvanı olan dəyişəndir. Bu linkin nə dərəcədə yerinə yetirilməsindən asılı olmayaraq, linkdə istinad etdiyi element kimi eyni yaddaş ünvanı var.

  • Göstəricilər ola bilər (Linkset tərəfindən təyin olunur), linklər bir sıra daxil edilə bilməz

  • Const istinadları müvəqqəti bağlı ola bilər. Göstəricilər (hər hansı bir dolaşıq olmadan):

     const int  = int(12); //legal C++ int *y =  //illegal to dereference a temporary. 

    Bu dəlil siyahılarında istifadə üçün const> daha təhlükəsiz edir.

1469
11 сент. Cavab Brian R. Bondy tərəfindən verilir . 2008-09-11 23:08 '08 at 11:08 pm 2008-09-11 23:08

C ++ istinadı nədir (C programcıları üçün)

Bağlantı daimi göstərici hesab edilə bilər (avtomatik olaraq indirection ilə, məsələn, sabit dəyər göstərici ilə qarışdırılmamalıdır). derleyici sizin üçün * operatorunu istifadə edəcək.

Bütün əlaqələr qeyri-sıfır dəyəri ilə başlanmalıdır, əks halda tərtibat uğursuz olacaq. Əlaqənin ünvanını almaq mümkün deyil - əvəzinə ünvan operatoru istinad dəyərinin ünvanını qaytarır və link hesabını etmək mümkün deyil.

C proqramçıları C ++ referanslarını sevmir, çünki dolaylılıq meydana gəldiyində və ya argument dəyər və ya göstərici ilə işarə imzalarına baxmadan daha aydın ola bilməz.

C ++ proqramçıları göstəricilərin istifadəsini istəmirlər, çünki onlar təhlükəli sayılırlar, baxmayaraq ki, əlaqələr ən əsası olmayan hallar istisna olmaqla, müntəzəm göstəricilərə nisbətdə daha təhlükəsiz deyildir - onlar avtomatik inyeksiya rahatlığı yoxdur və fərqli bir semantik çağırış daşıyırlar.

Aşağıdakı ifadəni C ++- dan tez-tez soruşun :

border=0

Bağlantı tez-tez assemblerin əsas dilindəki ünvanla istifadə edilsə belə, linki bir obyektə məzəli görünən bir pointer kimi düşünməyin. Bağlantı bir obyektdir. bir obyektin göstəricisi deyil, nə də bir obyektin surəti deyildir. Bu bir obyektdir.

Lakin link həqiqətən bir obyekt olsa, necə bir şəkildə pozulmuş linklər ola bilər? İdarə edilməyən dillərdə əlaqələrin göstəricilərə nisbətən "təhlükəsiz" olması qeyri-mümkündür - bir qayda olaraq, bu sadəcə sərhəd sərhədlərinin dəyərlərini etibarlı bir şəkildə daşıyan bir yol deyil!

Niyə C ++-da faydalı əlaqələr tapıram?

C-fonuna əsaslanaraq, C ++ istinadları bir qədər aptal bir konsepsiya kimi görünə bilər, ancaq bunların mümkün olduğu yerlərdə göstəricilərin əvəzinə istifadə etməlisiniz: avtomatik yanaşma rahatdır və RAII ilə işləyərkən əlaqələr xüsusilə faydalıdır - lakin hər hansı bir təhlükəsizliyin faydası deyil, əksinə, məktubun iddiasını daha az narahatlıq doğurmasıdır.

RAII, C ++ əsas anlayışlarından biridir, ancaq semantikləri kopyalamaqla qeyri-şəffaf şəkildə əlaqə saxlayır. Nəzərdən keçən obyektləri bu problemlərdən qaçınmağa imkan verir, çünki kopyalama yerinə yetirilmir. Əlaqələr bu dildə olmadıqda, istifadə etmək daha çətin olan göstəricilərdən istifadə etmək lazımdır, beləliklə daha yaxşı tətbiq olunan bir həll variantının alternativlərdən daha asan olması lazım olan bir dilin inkişaf prinsipini pozur.

324
28 февр. Christoph tərəfindən cavab Feb 28 2009-02-28 00:26 '09 da 0:26 2009-02-28 00:26

Əgər həqiqətən pedantik olmaq istəyirsənsə, bir göstərici ilə əlaqəsi olmayan bir keçid ilə bir şey edə bilərsiniz: müvəqqəti bir obyektin ömrünü uzat. C ++ 'da, müvəqqəti bir obyektə bir const referansı bağladığınız halda, bu obyektin ömrü bağlantının ömrü olur.

 std::string s1 = "123"; std::string s2 = "456"; std::string s3_copy = s1 + s2; const std::string s3_reference = s1 + s2; 

Bu misalda, s3_copy birləşmə nəticəsində olan müvəqqəti bir obyektin surətini çıxarır. S3_reference əsasən müvəqqəti bir obyekt olur. Bu, həqiqətən, keçidlə eyni ömür boyu olan müvəqqəti bir obyektin bir keçididir.

Bunu const olmadan sınamaq, bu tərtib etməməlidir. Müvəqqəti bir obyektə qeyri-daimi keçid bağlaya bilməzsiniz və bu barədə onun ünvanını qəbul edə bilməzsiniz.

167
12 сент. Cavab Matt Qiymət 12 sentyabr. 2008-09-12 00:43 '08 at 0:43 2008-09-12 00:43

Möhtərəm inancın əksinə, bir NULL linkə sahib olmaq mümkündür.

 int * p = NULL; int  r = *p; r = 1; // crash! (if you're lucky) 

Əlbəttə ki, bu link ilə daha çox məşğul olmaq çətindir, ancaq bununla başa düşsəniz, saçını yırğalayaraq, onu tapmaq üçün çalışırsınız. C ++ əlaqələri təbii olaraq təhlükəsiz deyil!

Texniki cəhətdən, bu null istinad deyil, etibarsız istinaddır . C ++ başqa dillərdə tapa bildiyiniz kimi null bağlantıları konsepsiya kimi dəstəkləmir. Digər etibarsız bağlantılar var. Hər hansı bir etibarsız link, qeyri-müəyyən davranışların aralığını və yanlış göstəricinin istifadəsini artırır.

Həqiqi səhv, link təyin etməzdən əvvəl NULL göstəricisini kənarlaşdırmaqdadır. Amma bu şəraitdə hər hansı bir səhv meydana gətirə biləcək hər hansı bir kompilyatordan xəbərim yoxdur - bu səhv kodun bir nöqtəsinə daha da yayılır. Bu, bu problemi çox həssas edir. Çox hallarda, NULL göstəricisini oynarsanız, bu yerdə sağ yalvarsınız və bunu anlamaya çox hata ayıklama tələb etmir.

Yuxarıda mənim nümunəm qısa və qüsursuzdur. Burada daha realist bir nümunədir.

 class MyClass { ... virtual void DoSomething(int,int,int,int,int); }; void Foo(const MyClass  bar) { ... bar.DoSomething(i1,i2,i3,i4,i5); // crash occurs here due to memory access violation - obvious why? } MyClass * GetInstance() { if (somecondition) return NULL; ... } MyClass * p = GetInstance(); Foo(*p); 

Təkrar etmək istəyirəm ki, null link almaq üçün yeganə yol səhv koddur və onu əldə etməzdən sonra müəyyən olmayan davranışlar əldə edirsiniz. Sıfır istinadı yoxlamaq üçün heç bir məna yoxdur; məsələn, if(> cəhd edə bilərsiniz if(> , ancaq derleyici variantı varlığından optimallaşdırır! Qüvvədə olan bir istinad əsla NULL ola bilməz, buna görə müqayisə həmişə kompilyatorun təmsilçisindən yalan deyildir və bəyanatın ölü kodu kimi sərbəst istisna edə bilərsiniz - bu, müəyyən edilməmiş davranışın mahiyyətidir.

Dəhşətdən qaçınmaq üçün doğru yol, bir istinad yaratmaq üçün null göstəricinin uzaqlaşdırılmamasıdır. Bunun üçün avtomatlaşdırılmış bir yoldur.

 template<typename T> T deref(T* p) { if (p == NULL) throw std::invalid_argument(std::string("NULL reference")); return *p; } MyClass * p = GetInstance(); Foo(deref(p)); 

Ən yaxşı yazı qabiliyyətinə malik olan bir kəs üçün bu problemə köhnə bir baxış üçün, Jim Hyslop və Herb Sutter-dən Null istinadları baxın.

Null göstəricinin aradan qaldırılması təhlükəsinin başqa bir nümunəsi üçün kodu başqa Raymond Chen platformasına köçürməyə çalışırken müəyyən edilməyən davranışın göstərilməsi .

114
12 сент. Mark Ransom tərəfindən verilmiş cavab 12 sentyabr 2008-09-12 00:06 '08 saat 00:06 'da 2008-09-12 00:06

Sintaktik şəkərlə yanaşı, link bir const pointer ( const göstərici deyil). Bir istinad dəyişənini bəyan etdiyiniz zaman bunun nə olduğunu təsbit etməli və onu daha sonra dəyişə bilməyəcəksiniz.

Yeniləmə: İndi bu barədə yenidən düşünürəm, əhəmiyyətli bir fərq var.

Məqsədli sabit göstəricinin adını alaraq və bir const sabitini istifadə edərək əvəz edilə bilər.

Hedef link UB-nin altından heç bir şəkildə dəyişdirilə bilməz.

Bu, kompilyatora istinad edərək böyük bir optimallaşdırma imkanı verməlidir.

111
11 сент. Cavab verilir Arkadiy 11 s. 2008-09-11 23:07 '08 at 11:07 pm 2008-09-11 23:07

Ən əhəmiyyətli hissəsini unutdunuz:

İstifadəçi ilə giriş göstəriciləri istifadə edir ->
İstifadəçi əlaqələrdən istifadə edərək istifadə edir .

foo.bar foo->bar daha üstündür ki, vi, foo.bar üstündür: -)

105
12 сент. Cavab verən Orion Edwards 12 Sentyabr. 2008-09-12 01:10 '08 saat 01:10 'da 2008-09-12 01:10

Əslində, link göstərici kimi görünmür.

Kompilyatorlar dəyişənlərə "istinadlar" verir, adını yaddaş ünvanı ilə əlaqələndirir; tərtib edərkən hər hansı bir dəyişən adı bir yaddaş ünvanına tərcümə etməkdir.

Bağlantı yaratdığınızda, kompilyatora fərqli bir pointer dəyişən adı atadığınızı bildirirsiniz; niyə əlaqələr null göstərə bilmir, çünki dəyişən ola bilməz və olmur.

Göstəricilər dəyişkəndir; digər dəyişənlərin ünvanını ehtiva edir və ya sıfır ola bilər. Göstərici bir dəyərə sahib olması vacibdir və linkdə yalnız istinad etdiyi dəyişən var.

İndi real kodun bəzi şərhləri üçün:

 int a = 0; int b = a; 

Burada birinə işarə edən başqa bir dəyişən yaratmırsınız; sadəcə a dəyərini ehtiva yaddaşın məzmununa başqa bir ad əlavə edin. Bu yaddaş indi iki ad var: ab və hər hansı bir ad istifadə edərək həll edilə bilər.

 void increment(int n) { n = n + 1; } int a; increment(a); 

Bir funksiya çağırıldığında, derleyici genellikle kopyalanacak argümanlar üçün yaddaş boşlukları üretir. Funksional imza yaradılacaq boşluqları müəyyənləşdirir və bu boşluqlar üçün istifadə etmək üçün ad verir. Bir parametrenin istinad olaraq bildirilməsi sadəcə, üsul çağırışı zamanı yeni bir yaddaş yerini ayırmaq yerine, kompilyatoru dəyişən giriş dəyişənliyi ilə sahəni istifadə etməyə deyir. Funksiyanız birbaşa zəng sahəsində elan edilmiş bir dəyişənə manipulyasiya edəcəyini söyləmək qəribə görünə bilər, ancaq yığılmış kodun icrası zamanı heç bir miqyas yoxdur; yalnız bir düz yaddaş var və funksiya kodu hər hansı bir dəyişənə manipulyasiya edə bilər.

İndi kompilyator tərtib edərkən məsələn, extern dəyişəndən istifadə edərkən istinad bilmədi ola bilər. Beləliklə, baza əsas kodda göstərici kimi tətbiq oluna və ya olmaya bilər. Ancaq mən sizə verdiyim nümunələrdə, çox güman ki, göstərici ilə həyata keçirilməyəcək.

63
19 сент. Cavab Vincent Robert 19 sentyabrda verilir. 2008-09-19 15:23 '08 saat 15:23 'da 2008-09-19 15:23

Bağlantılar göstəricilərə çox oxşardır, ancaq kompozitorları optimallaşdırmaq üçün xüsusi olaraq yaradılır.

  • Referanslar, kompilyator dəyişkən olan izləmə istinad aliaslarını asanlaşdırır ki, belə bir şəkildə dizayn edilir. İki mühüm xüsusiyyət çox vacibdir: heç bir "istinad istinad" və istinadların yenidən təyin edilməsi yoxdur. Kompilyatorun tərtibat dövründə olan dəyişənlərin hansı əlaqələrin olduğunu müəyyən etməyə imkan verirlər.
  • Bağlantılar, məsələn, kompilyatorun qeydiyyatdan keçməyi seçdiyi yaddaş ünvanlarına malik olmayan dəyişənlərə istinad edə bilər. Yerli bir dəyişənin ünvanını alırsanız, derleyici bir qeydiyyatdan qoymaq çox çətindir.

Məsələn:

 void maybeModify(int x); // may modify x in some way void hurtTheCompilersOptimizer(short size, int array[]) { // This function is designed to do something particularly troublesome // for optimizers. It will constantly call maybeModify on array[0] while // adding array[1] to array[2]..array[size-1]. There no real reason to // do this, other than to demonstrate the power of references. for (int i = 2; i < (int)size; i++) { maybeModify(array[0]); array[i] += array[1]; } } 

Optimallaşdırıcı kompilyator anlaya bilərik ki, [0] və [1] kifayət qədər dəstəyə daxil oluruq. Alqoritmi optimallaşdırmaq məqsədəuyğun olardı ki:

 void hurtTheCompilersOptimizer(short size, int array[]) { // Do the same thing as above, but instead of accessing array[1] // all the time, access it once and store the result in a register, // which is much faster to do arithmetic with. register int a0 = a[0]; register int a1 = a[1]; // access a[1] once for (int i = 2; i < (int)size; i++) { maybeModify(a0); // Give maybeModify a reference to a register array[i] += a1; // Use the saved register value over and over } a[0] = a0; // Store the modified a[0] back into the array } 

Bu optimallaşdırma etmək üçün, zəng zamanı heç bir şey dəyişə bilməyəcəyini sübut etmək lazımdır [1]. Bunu etmək olduqca asandır. Ən azı 2 olduğum üçün array [i] bir sıra [1] istinad edə bilməz. maybeModify () bir a0 olaraq istinad edir (aliasing array [0]). Heç bir "istinad" hesabı olmadığı üçün, kompilyatorun sadəcə mümkün olduğunu sübut etmək lazımdır. X dəyişən ünvanı heç vaxt x alır və array [1] dəyişməyəcəyini sübut etdi.

Bununla yanaşı, gələcək çağırışın [0] oxuması və yaza bilməsinin mümkün olmadığını sübut etmək lazımdır. Bu, sübut etmək üçün çox vaxt əsassızdır, çünki bir çox hallarda bu istinad heç bir sinif nümunəsi kimi daimi strukturda saxlanmır.

İndi göstəricilərlə eyni şeyi et.

 void maybeModify(int* x); // May modify x in some way void hurtTheCompilersOptimizer(short size, int array[]) { // Same operation, only now with pointers, making the // optimization trickier. for (int i = 2; i < (int)size; i++) { maybeModify( array[i] += array[1]; } } 

Davranış eynidır; yalnız indi bir dəyişiklik heç vaxt dəyişməyəcəyini sübut etmək daha çətindir [1], çünki biz artıq göstəriciyə işarə etmişik; pişik torbadan çıxdı. İndi o, çox daha mürəkkəb bir sübuta malik olmalıdır: statik analiz, heç yazmadığını sübut etmək üçün modifiye edə bilər və x + 1. O, həmçinin bir sıra [0] nə qədər çətin.

Müasir derleyiciler statik analiz ilə daha yaxşı və daha yaxşılaşırlar, lakin onlara kömək etmək və əlaqələrdən istifadə etmək həmişə gözəldir.

Əlbəttə ki, belə ağıllı optimallaşdırmalar istisna olmaqla, tərtibatçılar zəruri hallarda göstəricilərə istinad edirlər.

EDIT: Bu cavabın yayımlanmasından beş il sonra, əlaqələr birbaşa əlaqəli anlayışa baxmağın başqa bir yolundan fərqlənən faktiki texniki fərq tapdım. Bağlantılar, müvəqqəti obyektlərin ömrünü göstəricilərin edə bilməyəcəyi şəkildə dəyişə bilər.

 F createF(int argument); void extending() { const F ref = createF(5); std::cout << ref.getArgument() << std::endl; }; 

Ümumiyyətlə, bir ifadənin sonunda, yaradan createF(5) kimi yaradılmışlar kimi, adətən müvəqqəti obyektlər məhv edilir. Lakin, bu obyekti istinad ilə əlaqələndirərək, ref , C ++ ref kənarda keçənə qədər bu müvəqqəti obyektin ömrünü uzadır.

62
01 сент. Cavab Cort Ammon 01 sep verilir. 2013-09-01 06:44 '13 'da 6:44' da 2013-09-01 06:44

Bağlantı heç vaxt NULL ola bilməz.

37
11 сент. Cavab 11 sentyabrda RichS tərəfindən verilir . 2008-09-11 23:12 '08 at 11:12 pm 2008-09-11 23:12

Hər iki əlaqənin və göstəricinin dolayı yolla digər bir dəyərə çatdırılması üçün istifadə olunmasına baxmayaraq, əlaqələr və göstəricilər arasında iki fərqli fərq var. Birincisi, keçid həmişə obyektə aiddir: səhv onu müəyyənləşdirmədən linkin tərifindədir. Tapşırıq davranışı ikinci əhəmiyyətli fərqdir: Bir linkin təyin edilməsi linkə əlavə olunan obyektin əvəzi; başqa bir obyektin linkini yoxlamaz. Başlatma işleminden sonra, bağlantı her zaman eyni temel nesne aiddir.

Proqramın bu iki fraqmentini nəzərdən keçirin. Birincisi, digərinə bir göstərici təyin edirik:

 int ival = 1024, ival2 = 2048; int *pi =  *pi2 =  pi = pi2; // pi now points to ival2 

Tapşırıqdan sonra, ival, pi tərəfindən təyin olunan obyekt dəyişməz qalır. Tapşırma pi-nin dəyərini dəyişir, başqa bir obyektə işarə edir. İndi iki əlaqə verən oxşar proqramı nəzərdən keçirin:

 int  = ival,  = ival2; ri = ri2; // assigns ival2 to ival 

Bu tapşırıq ivalin dəyərini dəyişdirir, buna istinadən deyil, riyə istinad edilir. После назначения две ссылки по-прежнему относятся к их исходным объектам, и значение этих объектов теперь тоже самое.

33
ответ дан Kunal Vyas 20 мая '11 в 22:26 2011-05-20 22:26

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

На самом высоком уровне идея ссылок заключается в том, что они являются прозрачными "псевдонимами". Ваш компьютер может использовать адрес, чтобы заставить их работать, но вы не должны беспокоиться об этом: вы должны думать о них как о "просто другом имени" для существующего объекта, и этот синтаксис отражает это. Они более строгие, чем указатели, поэтому ваш компилятор может более надежно предупредить вас, когда вы собираетесь создать обвисшую ссылку, чем когда вы собираетесь создать свисающий указатель.

Кроме того, есть, конечно, некоторые практические различия между указателями и ссылками. Синтаксис для их использования явно отличается, и вы не можете "пересаживать" ссылки, ссылаться на небытие или ссылаться на ссылки.

27
ответ дан Lightness Races in Orbit 29 окт. '14 в 20:17 2014-10-29 20:17

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

  void fun(int  int  // A common usage of references. int a = 0; int  = a; // b is an alias for a. Not so common to use. 
22
ответ дан fatma.ekici 01 янв. '13 в 20:45 2013-01-01 20:45

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

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

Məsələn:

 class scope_test { public: ~scope_test() { printf("scope_test done!\n"); } }; ... { const scope_test  scope_test(); printf("in scope\n"); } 

напечатает:

 in scope scope_test done! 

Это языковой механизм, который позволяет ScopeGuard работать.

18
ответ дан MSN 13 сент. '08 в 2:27 2008-09-13 02:27

Ссылка не является другим именем, данным некоторой памяти. Это неизменный указатель, который автоматически отключается при использовании. В основном это сводится к:

 int j = i; 

Он внутренне становится

 int* const j = > 
18
ответ дан tanweer alam 26 февр. '13 в 8:20 2013-02-26 08:20

Это основано на tutorial . То, что написано, делает это более ясным:

 >>> The address that locates a variable within memory is what we call a reference to that variable. (5th paragraph at page 63) >>> The variable that stores the reference to another variable is what we call a pointer. (3rd paragraph at page 64) 

Просто помните, что

 >>> reference stands for memory location >>> pointer is a reference container (Maybe because we will use it for several times, it is better to remember that reference.) 

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

Посмотрите на следующее утверждение:

 int Tom(0); int  alias_Tom = Tom; 

alias_Tom можно понимать как alias of a variable (отличное от typedef , которое alias of a type ) Tom . Также можно забыть терминологию такого утверждения, чтобы создать ссылку Tom .

18
ответ дан Life 13 янв. '14 в 16:14 2014-01-13 16:14

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

 #include<iostream> using namespace std; void swap(char *  char *  { char *temp = str1; str1 = str2; str2 = temp; } int main() { char *str1 = "Hi"; char *str2 = "Hello"; swap(str1, str2); cout<<"str1 is "<<str1<<endl; cout<<"str2 is "<<str2<<endl; return 0; } 

И рассмотрим версию C вышеуказанной программы. В C вы должны использовать указатель на указатель (множественная косвенность), и это приводит к путанице, и программа может выглядеть сложной.

 #include<stdio.h>  void swap1(char **str1_ptr, char **str2_ptr) { char *temp = *str1_ptr; *str1_ptr = *str2_ptr; *str2_ptr = temp; } int main() { char *str1 = "Hi"; char *str2 = "Hello"; swap1(  printf("str1 is %s, str2 is %s", str1, str2); return 0; } 

Для получения дополнительной информации о ссылке на указатель посетите следующую страницу:

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

 #include <iostream> using namespace std; int main() { int x = 10; int *ptr =  int  = ptr; } 
15
ответ дан Destructor 09 февр. '15 в 16:17 2015-02-09 16:17

Существует одно фундаментальное различие между указателями и ссылками, которые я не видел никого, о которых упоминалось: ссылки позволяют использовать семантику pass-by-reference в аргументах функции. Указатели, хотя сначала это не видно, нет: они предоставляют только семантику pass-by-value. Это было очень хорошо описано в этой статье .

С уважением, Амп; rzej

14
ответ дан Andrzej 06 февр. '12 в 11:59 2012-02-06 11:59

Я использую ссылки, если мне не нужно ни одного из них:

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

  • Вы можете сделать арифметику на указателе. Например, p += offset;

14
ответ дан Aardvark 12 сент. '08 в 16:41 2008-09-12 16:41

Рискуя добавить в замешательство, я хочу добавить некоторые данные, я уверен, что это в основном зависит от того, как компилятор реализует ссылки, но в случае gcc идея о том, что ссылка может указывать только на переменную в стеке фактически не корректно, возьмите это, например:

 #include <iostream> int main(int argc, char** argv) { // Create a string on the heap std::string *str_ptr = new std::string("THIS IS A STRING"); // Dereference the string on the heap, and assign it to the reference std::string  = *str_ptr; // Not even a compiler warning! At least with gcc // Now lets try to print it value! std::cout << str_ref << std::endl; // It works! Now lets print and compare actual memory addresses std::cout << str_ptr << " : " <<  << std::endl; // Exactly the same, now remember to free the memory on the heap delete str_ptr; } 

Что выводит это:

 THIS IS A STRING 0xbb2070 : 0xbb2070 

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

 int main(int argc, char** argv) { // In the actual new declaration let immediately de-reference and assign it to the reference std::string  = *(new std::string("THIS IS A STRING")); // Once again, it works! (at least in gcc) std::cout << str_ref; // Once again it prints fine, however we have no pointer to the heap allocation, right? So how do we free the space we just ignorantly created? delete   } 

Что выводит это:

 THIS IS A STRING 

Поэтому ссылка является указателем под капотом, они оба просто сохраняют адрес памяти, где адрес указывает на неактуальный, что, по вашему мнению, произойдет, если я вызову std:: cout < < str_ref; ПОСЛЕ вызова delete str_ref? Ну, очевидно, что он компилируется отлично, но вызывает ошибку сегментации во время выполнения, поскольку он больше не указывает на допустимую переменную, мы по существу имеем сломанную ссылку, которая все еще существует (пока она не выпадает из области), но бесполезна.

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

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

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

Помните, что приведенные выше примеры - это примеры, демонстрирующие, что такое ссылка, вы никогда не захотите использовать ссылку в этих целях! Для правильного использования ссылки здесь уже много @, которые поражают гвоздь на голове

13
ответ дан Tory 15 окт. '14 в 0:38 2014-10-15 00:38

Другое отличие состоит в том, что у вас могут быть указатели на тип void (и это означает указатель на что-либо), но ссылки на void запрещены.

 int a; void * p =  // ok void  p = a; // forbidden 

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

12
ответ дан kriss 29 янв. '10 в 18:15 2010-01-29 18:15

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

Ссылки менее мощные, чем указатели

1) После создания ссылки впоследствии нельзя ссылаться на другой объект; он не может быть пересмотрен. Это часто делается с указателями.

2) Ссылки не могут быть NULL. Указатели часто делают NULL, чтобы указать, что они не указывают на какую-либо действительную вещь.

3) При объявлении эта ссылка должна быть инициализирована. Нет такого ограничения с указателями

Из-за вышеуказанных ограничений ссылки на С++ не могут использоваться для реализации структур данных, таких как Linked List, Tree и т.д. В Java ссылки не имеют ограничений и могут использоваться для реализации всех структур данных. Ссылки, более мощные в Java, являются основной причиной, по которой Java не нуждается в указателях.

Ссылки более безопасны и просты в использовании:

1) Безопаснее: поскольку ссылки должны быть инициализированы, дикие ссылки, такие как дикие указатели, вряд ли будут существовать. По-прежнему возможно иметь ссылки, которые не относятся к допустимому местоположению

2) Прост в использовании: для доступа к значению не требуется оператор разыменования. Они могут использоваться как обычные переменные. " Амп; оператор нужен только во время объявления. Кроме того, к элементам ссылки объекта можно обращаться с помощью оператора точки ('.), В отличие от указателей, в которых для доступа к элементам необходим оператор стрелки (- > ).

Вместе с вышеупомянутыми причинами существует несколько мест, таких как аргумент конструктора копирования, где указатель не может использоваться. Ссылка должна использоваться, чтобы передать аргумент в конструкторе копирования. Аналогично ссылки должны использоваться для перегрузки некоторых операторов, таких как ++ .

12
ответ дан Yogeesh HT 02 дек. '15 в 12:41 2015-12-02 12:41

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

 void increment(int *ptrint) { (*ptrint)++; } void increment(int  { refint++; } void incptrtest() { int testptr=0; increment( } void increftest() { int testref=0; increment(testref); } 

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

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

10
ответ дан Adisak 15 окт. '09 в 4:57 2009-10-15 04:57

Эта программа может помочь в понимании ответа на вопрос. Это простая программа ссылки "j" и указатель "ptr", указывающий на переменную "x".

 #include<iostream> using namespace std; int main() { int *ptr=0, x=9; // pointer and variable declaration ptr= // pointer to variable "x" int  j=x; // reference declaration; reference to variable "x" cout << "x=" << x << endl; cout << " <<  << endl; cout << "j=" << j << endl; cout << " <<  << endl; cout << "*ptr=" << *ptr << endl; cout << "ptr=" << ptr << endl; cout << " <<  << endl; getch(); } 

Запустите программу и посмотрите на результат, и вы поймете.

Кроме того, выпейте 10 минут и посмотрите это видео: https://www.youtube.com/watch?v=rlJrrGV0iOg

10
ответ дан Arlene Batada 15 марта '13 в 6:03 2013-03-15 06:03

Я чувствую, что есть еще один момент, который здесь не был рассмотрен.

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

Хотя это может показаться поверхностным, я считаю, что это свойство имеет решающее значение для ряда функций С++, например:

  • Шаблоны. Поскольку параметры шаблона заданы в стиле утка, все синтаксические свойства типа имеют значение, поэтому часто один и тот же шаблон можно использовать как с T , так и с T> .
    (или std::reference_wrapper<T> , который все еще полагается на неявный листинг до T> )
    Шаблоны, охватывающие как T> , так и T> , еще более распространены.

  • Lvalues. Рассмотрим оператор str[0] = 'X'; Без ссылок он будет работать только для c-строк ( char* str ). Возврат символа по ссылке позволяет пользовательским классам иметь одну и ту же нотацию.

  • Скопируйте конструкторы. Синтаксически имеет смысл передавать объекты для копирования конструкторов, а не указатели на объекты. Но конструктор копирования просто не может взять объект по значению - это приведет к рекурсивному вызову того же конструктора копий. Это оставляет ссылки как единственный вариант здесь.

  • Оператор перегружает. С помощью ссылок можно ввести косвенный вызов оператора - скажем, operator+(const T a, const T b) , сохранив при этом одну и ту же инфиксную нотацию. Это также работает для регулярных перегруженных функций.

Эти точки дают значительную часть С++ и стандартной библиотеки, поэтому это довольно важное свойство ссылок.

9
ответ дан Ap31 06 июля '17 в 22:48 2017-07-06 22:48

Еще одно интересное использование ссылок - предоставить аргумент по умолчанию для пользовательского типа:

 class UDT { public: UDT() : val_d(33) {}; UDT(int val) : val_d(val) {}; virtual ~UDT() {}; private: int val_d; }; class UDT_Derived : public UDT { public: UDT_Derived() : UDT() {}; virtual ~UDT_Derived() {}; }; class Behavior { public: Behavior( const UDT  = UDT() ) {}; }; int main() { Behavior b; // take default UDT u(88); Behavior c(u); UDT_Derived ud; Behavior d(ud); return 1; } 

По умолчанию используется ссылка "привязка константы к временному" аспекту ссылок.

9
ответ дан Don Wakefield 12 сент. '08 в 20:59 2008-09-12 20:59

Как указатель, ссылка является псевдонимом для объекта, обычно реализован для хранения машинного адреса объекта и не налагать накладные расходы по сравнению с указателями, но он отличается от указатель в том, что:

• Вы получаете доступ к ссылке с точно таким же синтаксисом, что и имя объект.

• Ссылка всегда ссылается на объект, для которого он был инициализирован.

• Нет нулевой ссылки, и мы можем предположить, что ссылка относится к объекту.

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

Məsələn:

 template<class T> class vector { T∗ elem; // ... public: T operator[](int i) { return elem[i]; } // return reference to element const T operator[](int i) const { return elem[i]; } // return reference to const element void push_back(const T a); // pass element to be added by reference // ... }; void f(const vector<double> v) { double d1 = v[1]; // copy the value of the double referred to by v.operator[](1) into d1 v[2] = 7; // place 7 in the double referred to by the result of v.operator[](2) v.push_back(d1); // give push_back() a reference to d1 to wor k with } 
8
ответ дан MSD561 02 дек. '17 в 20:30 2017-12-02 20:30

Возможно, некоторые метафоры помогут; В контексте экрана рабочего стола -

  • Ссылка требует указания фактического окна.
  • Указатель требует расположения части пространства на экране, которое, как вы уверяете, будет содержать ноль или более экземпляров этого типа окна.
8
ответ дан George R 27 дек. '14 в 16:00 2014-12-27 16:00

Существует очень важное техническое различие non- между указателями и ссылками: аргумент, передаваемый в функцию указателем, гораздо более заметен, чем аргумент, передаваемый в функцию посредством ссылки non- const. Məsələn:

 void fn1(std::string s); void fn2(const std::string s); void fn3(std::string s); void fn4(std::string* s); void bar() { std::string x; fn1(x); // Cannot modify x fn2(x); // Cannot modify x (without const_cast) fn3(x); // CAN modify x! fn4( // Can modify x (but is obvious about it) } 

Назад в C вызов, который выглядит как fn(x) может быть передан только по значению, поэтому он определенно не может изменить x ; чтобы изменить аргумент, вам нужно передать указатель fn(> . Поэтому, если аргументу не предшествует > вы знали, что он не будет изменен. (Обратное, > означает изменение, было неверно, потому что иногда вам приходилось передавать большие структуры только для чтения с помощью const указателя.)

Некоторые утверждают, что это такая полезная функция при чтении кода, что параметры указателя всегда следует использовать для изменяемых параметров, а не non- const ссылки, даже если функция не ожидает nullptr . То есть эти люди утверждают, что подписи функций, такие как fn3() выше, не должны быть разрешены. Руководящие принципы стиля Google C++ являются примером этого.

7
ответ дан Arthur Tacca 02 нояб. '17 в 14:16 2017-11-02 14:16

Разница между указателем и ссылкой

Указатель может быть инициализирован равным 0, а ссылка не указана. Фактически, ссылка должна также ссылаться на объект, но указатель может быть нулевым указателем:

 int* p = 0; 

Но мы не можем иметь int p = 0; , а также int p=5 ; .

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

 Int x = 0; Int y = 5; Int p = x; Int p1 = y; 

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

Еще одно отличие заключается в том, что указатель может указывать на другой объект, однако ссылка всегда ссылается на один и тот же объект, поэтому возьмите этот пример:

 Int a = 6, b = 5; Int rf = a; Cout << rf << endl; // The result we will get is 6, because rf is referencing to the value of a. rf = b; cout << a << endl; // The result will be 5 because the value of b now will be stored into the address of a so the former value of a will be erased 

Другой момент: когда у нас есть шаблон, такой как шаблон STL, такой тип шаблона класса всегда будет возвращать ссылку, а не указатель, чтобы упростить чтение или присвоение нового значения с помощью оператора []:

 Std ::vector<int>v(10); // Initialize a vector with 10 elements V[5] = 5; // Writing the value 5 into the 6 element of our vector, so if the returned type of operator [] was a pointer and not a reference we should write this *v[5]=5, by making a reference we overwrite the element by using the assignment "=" 
6
ответ дан dhokar.w 06 янв. '17 в 17:01 2017-01-06 17:01

Различие заключается в том, что переменная указателя константы не должна быть путаной с указателем на константу, может быть изменена в течение некоторого времени во время выполнения программы, требуется использовать семантику указателя ( *), а ссылки могут устанавливаются только при инициализации (поэтому их можно устанавливать только в списке инициализаторов конструктора, но не как-то иначе) и использовать обычную доступность для семантики. В основном были введены ссылки, позволяющие поддерживать перегрузку операторов, как я читал в какой-то очень старой книге. Как кто-то сказал в этом потоке - указатель может быть установлен в 0 или любое другое значение, которое вы хотите. 0 (NULL, nullptr) означает, что указатель инициализирован ничем. Это ошибка для разыменования нулевого указателя. Но на самом деле указатель может содержать значение, которое не указывает на правильное расположение памяти. В свою очередь, ссылки не позволяют пользователю инициализировать ссылку на то, на что нельзя ссылаться, из-за того, что вы всегда предоставляете ему значение правильного типа. Хотя есть много способов сделать ссылочную переменную инициализированной неправильной ячейкой памяти - вам лучше не углубляться в детали. На уровне машины оба указателя и ссылки работают равномерно - с помощью указателей. Скажем, в основных ссылках есть синтаксический сахар. Ссылки rvalue отличаются от этого - они, естественно, представляют собой объекты stack/heap.

5
ответ дан Zorgiev 24 апр. '16 в 16:22 2016-04-24 16:22
  • 1
  • 2

Другие вопросы по меткам или Задайте вопрос