विषय पर बढ़ें

अंतिम अद्यतन:2026-10-12

C++ पॉइंटर और रेफ़रेंस

लेखक के बारे में:

व्यक्तिगत WeChat सार्वजनिक अकाउंट:

wechat

1.रेफ़रेंस और पॉइंटर

सारांश:

रेफ़रेंस पॉइंटर
initialize करना ज़रूरी बिना initialize के भी चलता है
खाली नहीं हो सकता खाली हो सकता है
target नहीं बदल सकता target बदल सकता है

रेफ़रेंस को initialize करना ज़रूरी है, जबकि पॉइंटर को ज़रूरी नहीं।

हम रेफ़रेंस परिभाषित करते समय उसे एक initial value देनी ही होती है, लेकिन पॉइंटर को ऐसी ज़रूरत नहीं।

int &r;    //不合法,没有初始化引用
int *p;    //合法,但p为野指针,使用需要小心

रेफ़रेंस खाली नहीं हो सकता, जबकि पॉइंटर खाली हो सकता है।

रेफ़रेंस खाली न होने के कारण उसे उपयोग करते समय उसकी वैधता जाँचने की ज़रूरत नहीं, जबकि पॉइंटर उपयोग करते समय पहले यह जाँचना होता है कि पॉइंटर null तो नहीं, वरना प्रोग्राम crash हो सकता है।

void test_p(int* p)
{
    if(p != null_ptr)    //对p所指对象赋值时需先判断p是否为空指针
        *p = 3;
    return;
}
void test_r(int& r)
{
    r = 3;    //由于引用不能为空,所以此处无需判断r的有效性就可以对r直接赋值
    return;
}

रेफ़रेंस target नहीं बदल सकता

पॉइंटर कभी भी अपनी दिशा बदल सकता है, लेकिन रेफ़रेंस केवल initialize होते समय जिस ऑब्जेक्ट की ओर था, उसी की ओर रहता है, बदला नहीं जा सकता।

int a = 1;
int b = 2;

int &r = a;    //初始化引用r指向变量a
int *p = &a;   //初始化指针p指向变量a

p = &b;        //指针p指向了变量b
r = b;         //引用r依然指向a,但a的值变成了b

2.रेफ़रेंस

lvalue रेफ़रेंस

सामान्य रेफ़रेंस, आमतौर पर ऑब्जेक्ट की पहचान दर्शाता है।

rvalue रेफ़रेंस

rvalue रेफ़रेंस वह रेफ़रेंस है जिसे rvalue (एक temporary ऑब्जेक्ट, नष्ट होने वाला ऑब्जेक्ट) से बाइंड करना ही होता है, आमतौर पर ऑब्जेक्ट की वैल्यू दर्शाता है।

rvalue रेफ़रेंस से move semantics और perfect forwarding हासिल होता है, इसके मुख्य मक़सद दो हैं:

  • दो ऑब्जेक्ट के बीच अनावश्यक object copy ख़त्म करके, computation और storage resources बचाकर दक्षता बढ़ाना।
  • generic फ़ंक्शन को ज़्यादा संक्षिप्त और स्पष्ट रूप से परिभाषित कर पाना।

reference collapsing

  • X& &, X& &&, X&& & को X& में collapse किया जा सकता है
  • X&& && को X&& में collapse किया जा सकता है

C++ का रेफ़रेंस प्रोग्रामर की freedom घटाकर मेमोरी ऑपरेशन की सुरक्षा और semantics की सुंदरता बढ़ाता है। जैसे रेफ़रेंस पर initialize करना मजबूर करने से, हम रेफ़रेंस उपयोग करते समय उसे खाली है या नहीं जाँचने की ज़रूरत नहीं, कोड ज़्यादा संक्षिप्त और सुंदर रहता है, हर जगह पॉइंटर बिखरने से बचा जाता है। इस केस के अलावा रेफ़रेंस निम्नलिखित दो केस में भी उपयोग होता है:

रेफ़रेंस टाइप पैरामीटर

आमतौर पर हम read-only पैरामीटर के लिए const reference parameter उपयोग करते हैं, ऐसी स्थिति में parameter copy से बचा जा सकता है और pass-by-value जैसा ही call तरीका मिलता है।

void test(const vector<int> &data)
{
    //...
}
int main()
{
    vector<int> data{1,2,3,4,5,6,7,8};
    test(data);
}

रेफ़रेंस टाइप return value

C++ operator overloading की सुविधा देता है, कुछ ऑपरेटर overload करते समय reference return value उपयोग करने से उस ऑपरेटर के मूल syntax जैसा ही call तरीका मिलता है, ऑपरेटर की semantics की निरंतरता बनी रहती है। एक उदाहरण है operator [], यह ऑपरेटर आमतौर पर reference ऑब्जेक्ट लौटाता है, तभी उसे सही ढंग से बदला जा सकता है।

vector<int> v(10);
v[5] = 10;    //[]操作符返回引用,然后vector对应元素才能被修改
              //如果[]操作符不返回引用而是指针的话,赋值语句则需要这样写
*v[5] = 10;   //这种书写方式,完全不符合我们对[]调用的认知,容易产生误解

3.पॉइंटर और रेफ़रेंस का परफ़ॉर्मेंस अंतर

पॉइंटर और रेफ़रेंस में परफ़ॉर्मेंस अंतर है या नहीं? ऐसे सवाल के लिए assembly स्तर पर देखना होगा। हम पहले एक test1 फ़ंक्शन लिखते हैं, पैरामीटर पास करने में पॉइंटर:

void test1(int* p)
{
    *p = 3;    //此处应该首先判断p是否为空,为了测试的需要,此处我们没加。
    return;
}

इस कोड सेगमेंट के संगत assembly कोड नीचे है:

(gdb) disassemble 
Dump of assembler code for function test1(int*):
   0x0000000000400886 <+0>:  push   %rbp
   0x0000000000400887 <+1>:  mov    %rsp,%rbp
   0x000000000040088a <+4>:  mov    %rdi,-0x8(%rbp)
=> 0x000000000040088e <+8>:  mov    -0x8(%rbp),%rax
   0x0000000000400892 <+12>: movl   $0x3,(%rax)
   0x0000000000400898 <+18>: nop
   0x0000000000400899 <+19>: pop    %rbp
   0x000000000040089a <+20>: retq   
End of assembler dump.

उपरोक्त कोड की 1, 2 line parameter call काon-site संरक्षण ऑपरेशन है; तीसरी line parameter pass है, फ़ंक्शन कॉल का पहला पैरामीटर आमतौर पर rdi register में रहता है, यह line rdi register की वैल्यू (पॉइंटर p की वैल्यू) को stack में लिखती है; चौथी line stack में p की वैल्यू को rax register में लिखती है; पाँचवीं line immediate 3 को rax register की वैल्यू द्वारा इंगित मेमोरी में लिखती है, यहाँ (%rax) के दोनों ओर के कोष्ठकों पर ध्यान दें, ये कोष्ठक फ़ालतू नहीं हैं, (%rax) और %rax बिल्कुल अलग अर्थ रखते हैं, (%rax) rax register की वैल्यू वाले पते के मेमोरी भाग को दर्शाता है, यानी C++ कोड के *p के बराबर, जबकि %rax rax register को दर्शाता है, C++ कोड की p वैल्यू के बराबर, इसलिए assembly में यहाँ %rax नहीं बल्कि (%rax) उपयोग हुआ है।

अब हम parameter pass में रेफ़रेंस उपयोग करने वाला C++ कोड सेगमेंट test2 लिखते हैं:

void test2(int& r)
{
    r = 3;    //赋值前无需判断reference是否为空
    return;
}

इस कोड के संगत assembly कोड नीचे है:

(gdb) disassemble 
Dump of assembler code for function test2(int&):
   0x000000000040089b <+0>:  push   %rbp
   0x000000000040089c <+1>:  mov    %rsp,%rbp
   0x000000000040089f <+4>:  mov    %rdi,-0x8(%rbp)
=> 0x00000000004008a3 <+8>:  mov    -0x8(%rbp),%rax
   0x00000000004008a7 <+12>: movl   $0x3,(%rax)
   0x00000000004008ad <+18>: nop
   0x00000000004008ae <+19>: pop    %rbp
   0x00000000004008af <+20>: retq   
End of assembler dump.

हम पाते हैं कि test2 का assembly कोड और test1 का assembly कोड बिल्कुल एक जैसा है, इससे पता चलता है कि C++ कंपाइलर प्रोग्राम कंपाइल करते समय पॉइंटर और रेफ़रेंस को बिल्कुल एक जैसी machine code बना देता है। इसलिए C++ का रेफ़रेंस सिर्फ़ C++ के पॉइंटर ऑपरेशन का एक "syntactic sugar" है, नीचे के स्तर पर C++ कंपाइलर इन दोनों ऑपरेशन को बिल्कुल एक जैसे तरीके से implement करता है।

3.सारांश

C++ में रेफ़रेंस ऑपरेशन लाया गया, रेफ़रेंस के उपयोग पर ज़्यादा restriction लगाकर, रेफ़रेंस उपयोग की सुरक्षा और सुविधा सुनिश्चित की गई, साथ ही कोड की एलिगेंस भी बनी रही। उपयुक्त स्थिति में उपयुक्त ऑपरेशन का उपयोग करें, रेफ़रेंस का उपयोग कुछ हद तक "हर जगह पॉइंटर बिखरने" से बचा सकता है, प्रोग्राम की stability बढ़ाने में भी इसका सकारात्मक योगदान है। आख़िर में, पॉइंटर और रेफ़रेंस का निचला implementation एक जैसा ही है, दोनों के परफ़ॉर्मेंस अंतर की चिंता न करें।

उपरोक्त हिस्सा यहाँ से रेफ़रेंस:http://irootlee.com/juicer_pointer_reference/#

संबंधित विषय

टिप्पणियाँ