Call by Value vs Call by Reference & మెమరీ పాయింటర్స్
మీ వద్ద మీ విలువైన ఇంటి స్థలానికి సంబంధించిన ఒరిజినల్ రిజిస్ట్రేషన్ డాక్యుమెంట్ ఉందనుకోండి. మీ స్నేహితుడు ఆ డాక్యుమెంట్లోని కొలతలను పరిశీలించాలనుకుంటున్నారు. మీరు ఏం చేస్తారు? ఒరిజినల్ పత్రాన్ని చేతిలో పెట్టకుండా, దానిని ఒక జిరాక్స్ మిషన్ లో పెట్టి ఫోటోకాపీ (Photocopy) తీసి ఇస్తారు. ఇప్పుడు మీ స్నేహితుడు ఆ జిరాక్స్ కాగితంపై పెన్నుతో రాతలు రాసినా, సిరా ఒలికించినా, లేదా ఆ కాగితాన్ని కోపంతో చింపేసినా — మీ బీరువాలో భద్రంగా దాచిన ఒరిజినల్ డాక్యుమెంట్కు ఏదైనా నష్టం జరుగుతుందా? బొత్తిగా జరగదు! ఇది కంప్యూటర్ ప్రోగ్రామింగ్లోని Call by Value పద్ధతి.
ఇప్పుడు రెండవ పరిస్థితిని ఊహించండి: మీ స్నేహితుడికి జిరాక్స్ కాపీ ఇచ్చే బదులు, మీ బ్యాంక్ లాకర్ ఉన్న చిరునామా మరియు లాకర్ తాళం చెవి ఇచ్చారనుకుందాం (Address / Reference / Pointer). అతను నేరుగా ఆ చిరునామాకు వెళ్లి, లాకర్ తెరిచి, లోపల ఉన్న ఒరిజినల్ పత్రంపై సంతకం చేస్తే — మీ అసలైన డాక్యుమెంట్ మారిపోతుంది కదా! ఇది C లోని Call by Reference (పాయింటర్ల ద్వారా పాస్ చేయడం) పద్ధతి!
ఈ పదవ అధ్యాయంలో, ప్రపంచవ్యాప్తంగా ప్రతీ C ఇంటర్వ్యూలో అడిగే swap() ఫంక్షన్ వైఫల్యం, & (అడ్రస్-ఆఫ్) మరియు * (డిరిఫరెన్స్) ఆపరేటర్ల అంతర్గత మ్యాజిక్, ఒకే ఫంక్షన్ నుండి బహుళ విలువలను రిటర్న్ (Multiple Return Values) చేసే ప్రొఫెషనల్ టెక్నిక్స్, మరియు సురక్షితమైన మల్టీ-అకౌంట్ బ్యాంకింగ్ ఫండ్ ట్రాన్స్ఫర్ ప్రాజెక్ట్ను లోతుగా నిర్మిద్దాం!
మెమరీ మేనేజ్మెంట్లో పాయింటర్లు మరియు పారామీటర్ పాసింగ్ అనేవి C ప్రోగ్రామింగ్కు కిరీటం లాంటివి. ఈ అధ్యాయంలో కవర్ చేయబడే కీలక అంశాలు:
- C లో డీఫాల్ట్ కాల్ పద్ధతి: C భాషలో ప్రతి ఫంక్షన్ కాల్ స్వాభావికంగా కేవలం Call by Value మాత్రమే ఎందుకుంటుంది?
- ది క్లాసిక్ స్వాప్ ఫెయిల్యూర్ (The Classic Swap Bug): విలువలను మార్చడానికి రాసిన సాధారణ ఫంక్షన్ ఎందుకు విఫలమవుతుంది?
- స్టాక్ ఫ్రేమ్ మెమరీ ఐసొలేషన్:
main()యొక్క మెమరీని వేరే ఫంక్షన్ నేరుగా తాకలేకపోవడానికి కారణం. - పాయింటర్ల ప్రాథమిక అవసరం: మెమరీ చిరునామాను సంగ్రహించే
&మరియు ఆ చిరునామాలోని విలువను చదివే/మార్చే*ఆపరేటర్లు. - C లో Call by Reference అనుకరణ (Pass by Pointer):
swap(&x, &y)అసెంబ్లీ లెవల్ పనితీరు. - సమగ్ర తులనాత్మక పట్టిక: Call by Value vs Call by Reference పక్కపక్కనే విశ్లేషణ.
- మల్టిపుల్ రిటర్న్ వాల్యూస్ (Multiple Return Values): C లో ఒకే ఫంక్షన్ నుండి ఏకకాలంలో రెండు లేదా మూడు ఫలితాలను ఎలా పొందాలి?
- లార్జ్ డేటా స్ట్రక్చర్స్ & పెర్ఫార్మెన్స్ బూస్ట్: మెగాబైట్ల డేటాను కాపీ చేయకుండా కేవలం 8-బైట్ల పాయింటర్తో నానోసెకన్లలో పాస్ చేయడం.
- const పాయింటర్ సేఫ్టీ (Read-Only Parameters): రెఫరెన్స్ పాస్ చేసినప్పుడు పొరపాటున డేటా మారకుండా లాక్ చేయడం.
- NULL పాయింటర్ చెకింగ్ & సెగ్మెంటేషన్ ఫాల్ట్ నివారణ:
SIGSEGVక్రాష్లను అరికట్టే గార్డ్ క్లాజ్లు. - సమగ్ర రియల్ వరల్డ్ ప్రాజెక్ట్: బ్యాంకింగ్ మల్టీ-అకౌంట్ ఫండ్ ట్రాన్స్ఫర్ & కరెన్సీ ఎక్స్ఛేంజ్ ఇంజిన్.
- బిగినర్స్ చేసే టాప్ 5 పాయింటర్ తప్పులు: అన్ఇనిషియలైజ్డ్ పాయింటర్స్, ఆపరేటర్ ప్రిసిడెన్స్ కన్ఫ్యూజన్.
- స్వీయ మూల్యాంకన క్విజ్: 5 లోతైన విశ్లేషణాత్మక ప్రశ్నలు & గేట్/ఇంటర్వ్యూ కాన్సెప్ట్స్.
C లాంగ్వేజ్లో కఠినమైన నియమం ఉంది: C లో సాంకేతికంగా కేవలం Call by Value మాత్రమే ఉంటుంది! మీరు ఒక వేరియబుల్ను ఫంక్షన్కు పంపినప్పుడు, ఆ వేరియబుల్ యొక్క ప్రస్తుత విలువ (Value) మాత్రమే కాపీ అయి ఫంక్షన్ పారామీటర్లోకి వెళ్తుంది.
మెమరీ కాపీ ప్రక్రియ (Under the Hood):
మీరు modifyValue(x) అని పిలిచినప్పుడు, main() ఫంక్షన్ స్టాక్ ఫ్రేమ్లో ఉన్న x యొక్క ఒరిజినల్ మెమరీ లొకేషన్ అలాగే సురక్షితంగా ఉంటుంది. CPU కాల్ స్టాక్ పై modifyValue కోసం ఒక కొత్త స్టాక్ ఫ్రేమ్ ఏర్పడుతుంది. అందులోని పారామీటర్ x కి ఒక సరికొత్త మెమరీ అడ్రస్ కేటాయించబడి, అందులోకి ఒరిజినల్ విలువ నకలు (Duplicate Copy) చేయబడుతుంది.
#include <stdio.h>
void modifyValue(int n) {
printf(" [ఫంక్షన్ లోపల - ప్రారంభం] n = %d, మెమరీ అడ్రస్ = %p\n", n, (void*)&n);
n = n + 100; // కేవలం లోకల్ కాపీ మాత్రమే పెరుగుతుంది!
printf(" [ఫంక్షన్ లోపల - ముగింపు] n = %d, మెమరీ అడ్రస్ = %p\n", n, (void*)&n);
}
int main() {
int score = 50;
printf("[main లోపల - కాల్ చేయడానికి ముందు] score = %d, మెమరీ అడ్రస్ = %p\n", score, (void*)&score);
modifyValue(score); // Call by Value!
printf("[main లోపల - కాల్ చేసిన తర్వాత] score = %d, మెమరీ అడ్రస్ = %p\n", score, (void*)&score);
return 0;
}
main() లోని score వేరియబుల్ మెమరీ అడ్రస్ మరియు ఫంక్షన్ లోని n వేరియబుల్ మెమరీ అడ్రస్ పూర్తిగా వేర్వేరుగా ఉంటాయి! అందుకే ఫంక్షన్ లోపల n విలువను 150 చేసినప్పటికీ, main() లోని score అలాగే 50 గానే ఉండిపోయింది!
ప్రపంచవ్యాప్తంగా ప్రతి ప్రోగ్రామింగ్ విద్యార్థి తమ కెరీర్ ప్రారంభంలో ఎదుర్కొనే అత్యంత ప్రసిద్ధమైన బగ్: రెండు సంఖ్యలను మార్చడానికి (Swap) సాధారణ ఫంక్షన్ రాసి విఫలమవ్వడం!
#include <stdio.h>
// ❌ పనిచేయని బగ్గీ స్వాప్ ఫంక్షన్
void wrongSwap(int a, int b) {
int temp = a;
a = b;
b = temp;
printf(" [wrongSwap లోపల] a = %d, b = %d (ఇక్కడ మారాయి!)\n", a, b);
}
int main() {
int x = 10, y = 20;
printf("స్వాప్ చేయడానికి ముందు: x = %d, y = %d\n", x, y);
wrongSwap(x, y); // ❌ ఫెయిల్ అవుతుంది!
printf("స్వాప్ చేసిన తర్వాత: x = %d, y = %d (మారలేదు!)\n", x, y);
return 0;
}
wrongSwap() ఫంక్షన్ లోపల ఉన్న a మరియు b లు కేవలం నకలు కాపీలు మాత్రమే. ఆ ఫంక్షన్ లోపల వాటి విలువలు మారాయి నిజమే! కానీ ఆ ఫంక్షన్ ఎగ్జిక్యూషన్ ముగియగానే దాని స్టాక్ ఫ్రేమ్ మెమరీ నుండి నాశనమైపోయింది (Popped from stack). main() లోని అసలైన x మరియు y వేరియబుల్స్ అలాగే చెక్కుచెదరకుండా ఉండిపోయాయి!
పై సమస్యను పరిష్కరించాలంటే, మనం విలువల కాపీలను పంపకూడదు. అసలైన వేరియబుల్స్ ఉన్న మెమరీ అడ్రస్లను (Memory Addresses) ఫంక్షన్కు పంపాలి. దీనికోసం C లో రెండు మేజికల్ ఆపరేటర్లు ఉన్నాయి:
1. & ఆపరేటర్ (Address-Of Operator)
ఒక వేరియబుల్ RAM లో ఏ మెమరీ గదిలో (Hexadecimal Address) దాగి ఉందో ఆ చిరునామాను అందిస్తుంది.
ఉదాహరణ: &x ➔ 0x7ffee1b2c458
2. * ఆపరేటర్ (Dereference Operator)
ఒక పాయింటర్ పట్టుకున్న మెమరీ అడ్రస్ లోపలికి నేరుగా వెళ్లి, అక్కడ ఉన్న అసలైన విలువను చదవడానికి లేదా మార్చడానికి ఉపయోగిస్తారు.
ఉదాహరణ: *ptr = 99;
ఇప్పుడు మనం పాయింటర్లను ఉపయోగించి నిజమైన Call by Reference (Pass by Address) ను నిర్మిద్దాం. ఇక్కడ ఫంక్షన్ కాల్ చేసేటప్పుడు వేరియబుల్స్ ముందు & ఉంచి వాటి అడ్రస్లను పంపుతాం. ఫంక్షన్ హెడర్లో వాటిని అందుకోవడానికి * పాయింటర్లను పెడతాం:
#include <stdio.h>
// ✅ విజయవంతంగా పనిచేసే సరైన స్వాప్ ఫంక్షన్
void trueSwap(int *ptrA, int *ptrB) {
// 1. ptrA ఉన్న చిరునామాలోని విలువను తాత్కాలికంగా దాచు
int temp = *ptrA;
// 2. ptrB ఉన్న చిరునామాలోని విలువను ptrA చిరునామాలోకి కాపీ చేయి
*ptrA = *ptrB;
// 3. temp లో ఉన్న విలువను ptrB చిరునామాలోకి రాయి
*ptrB = temp;
printf(" [trueSwap లోపల] అడ్రస్ ఆధారంగా ఒరిజినల్ మెమరీ మార్చబడింది!\n");
}
int main() {
int x = 10, y = 20;
printf("స్వాప్ చేయడానికి ముందు: x = %d, y = %d\n", x, y);
// వేరియబుల్స్ అడ్రస్లను (&x, &y) ఫంక్షన్కు పాస్ చేస్తున్నాం
trueSwap(&x, &y);
printf("స్వాప్ చేసిన తర్వాత: x = %d, y = %d (విజయవంతంగా మారాయి!)\n", x, y);
return 0;
}
trueSwap ఫంక్షన్ తన స్వంత కొత్త వేరియబుల్స్ను మార్చలేదు. అది main() లోని x మరియు y ల యొక్క మెమరీ చిరునామాలను పట్టుకుని, * ఆపరేటర్ ద్వారా నేరుగా main() యొక్క గదుల్లోకి వెళ్లి అక్కడే విలువలను తారుమారు చేసింది! అందుకే ఫంక్షన్ ముగిసిన తర్వాత కూడా మార్పులు శాశ్వతంగా ఉండిపోయాయి!
ఈ రెండు పద్ధతుల మధ్య గల సమగ్ర సాంకేతిక వ్యత్యాసాలను ఒకే పట్టికలో పరిశీలిద్దాం:
| అంశం / ఫీచర్ | Call by Value | Call by Reference (Pass by Pointer) |
|---|---|---|
| ఫంక్షన్కు ఏమి పాస్ చేస్తాం? | వేరియబుల్ విలువ యొక్క నకలు కాపీ (Copy of value). | వేరియబుల్ యొక్క అసలైన మెమరీ అడ్రస్ (&variable). |
| ఫార్మల్ పారామీటర్ల రకం | సాధారణ వేరియబుల్స్ (ఉదా: int a). |
పాయింటర్ వేరియబుల్స్ (ఉదా: int *ptr). |
| ఒరిజినల్ డేటా భద్రత | చాలా ఎక్కువ (ఫంక్షన్ ఒరిజినల్ విలువను మార్చలేదు). | తక్కువ (ఫంక్షన్ ఒరిజినల్ డేటాను నేరుగా మార్చగలదు). |
| మెమరీ వినియోగం | పెద్ద డేటా ఉన్నప్పుడు ప్రతిసారీ కాపీ చేయడానికి ఎక్కువ మెమరీ ఖర్చవుతుంది. | కేవలం 8 బైట్ల చిరునామా మాత్రమే వెళ్తుంది కాబట్టి మెమరీ ఆదా అవుతుంది. |
| స్వాపింగ్ లేదా సవరణలు | సాధ్యం కాదు (ఫంక్షన్ బయట ప్రభావం ఉండదు). | ఖచ్చితంగా సాధ్యం (కాలర్ వేరియబుల్స్ నేరుగా మారతాయి). |
| ఉత్తమ వినియోగ సందర్భం | చిన్న డేటా టైప్స్ (int, char, float) గణనలకు. | పెద్ద అర్రేలు, స్ట్రక్చర్స్ మరియు మల్టిపుల్ రిటర్న్స్ కోసం. |
C లాంగ్వేజ్లో ఒక ప్రాథమిక పరిమితి ఉంది: ఒక ఫంక్షన్ యొక్క return స్టేట్మెంట్ ద్వారా కేవలం ఒకే ఒక్క విలువను మాత్రమే వెనక్కి పంపగలం!
కానీ నిజ జీవితంలో ఒక గణిత ఫంక్షన్ నుండి భాగహారంలో భాగఫలం (Quotient) మరియు శేషం (Remainder) రెండూ ఒకేసారి కావాల్సి వస్తే ఎలా? పాయింటర్ల ద్వారా Call by Reference వాడి మనం ఎన్ని విలువలనైనా ఒకేసారి రిటర్న్ చేయవచ్చు!
#include <stdio.h>
// ఒకే ఫంక్షన్ నుండి Quotient మరియు Remainder రెండింటినీ రిటర్న్ చేద్దాం
int divide(int dividend, int divisor, int *quotient, int *remainder) {
if (divisor == 0) {
return 0; // సున్నా చేత భాగహారం నిషిద్ధం (విఫలం)
}
*quotient = dividend / divisor; // మొదటి ఫలితం
*remainder = dividend % divisor; // రెండవ ఫలితం
return 1; // విజయం (Success flag)
}
int main() {
int a = 29, b = 6;
int q, r;
printf("29 ని 6 చేత భాగిస్తున్నాం...\n");
if (divide(a, b, &q, &r)) {
printf("✅ భాగహారం సక్సెస్!\n");
printf(" భాగఫలం (Quotient) = %d\n", q);
printf(" శేషం (Remainder) = %d\n", r);
} else {
printf("❌ ఎర్రర్: సున్నాతో భాగించలేము!\n");
}
return 0;
}
భవిష్యత్తులో మనం స్ట్రక్చర్స్ (Structures) మరియు అర్రేల గురించి నేర్చుకోబోతున్నాం. ఒక ఉద్యోగి రికార్డులో 50 రకాల వివరాలు (పేరు, ఆధార్, వేతనం, అడ్రస్...) ఉండవచ్చు. ఆ స్ట్రక్చర్ సైజు సుమారు 2,048 బైట్లు (2 KB) అనుకుందాం.
ఫంక్షన్ పిలిచిన ప్రతిసారీ మొత్తం 2,048 బైట్ల డేటాను స్టాక్ పైకి కొత్తగా కాపీ చేయాలి. లూప్లో వేలసార్లు రన్ అయితే మెమరీ మరియు సమయం విపరీతంగా వృధా అవుతాయి.
డేటా ఎంత పెద్దదైనా సరే, కేవలం దాని మెమరీ అడ్రస్ మాత్రమే ఫంక్షన్కు వెళ్తుంది. 64-బిట్ CPU లో ఒక పాయింటర్ సైజ్ కేవలం 8 బైట్లు మాత్రమే! పర్ఫార్మెన్స్ 100 రెట్లు పెరుగుతుంది!
కాల్ బై రిఫరెన్స్లో ఒక పెద్ద రిస్క్ ఉంది: పాయింటర్ చేతికి ఇస్తున్నాం కాబట్టి, ఫంక్షన్ లోపల పొరపాటున ఆ డేటా మారిపోయే ప్రమాదం ఉంది. డేటాను వేగంగా పాస్ చేయాలి, కానీ లోపల ఎవరూ దాన్ని మార్చకూడదు అనుకుంటే C లో const క్వాలిఫైయర్ ఉపయోగిస్తారు!
#include <stdio.h>
// const int *data అంటే: ఈ పాయింటర్ ద్వారా డేటాను చదవవచ్చే కానీ మార్చకూడదు!
void printConfiguration(const int *configValue) {
printf("సిస్టమ్ కాన్ఫిగరేషన్ విలువ: %d\n", *configValue);
// ఒకవేళ డెవలపర్ పొరపాటున ఇలా రాయడానికి ప్రయత్నిస్తే:
// *configValue = 999;
// కంపైలర్ వెంటనే: 'assignment of read-only location' అని ఎర్రర్ ఇచ్చి ఆపేస్తుంది!
}
int main() {
int secretKey = 7788;
printConfiguration(&secretKey);
return 0;
}
పాయింటర్లను ఫంక్షన్లలో వాడేటప్పుడు సీనియర్ ఇంజనీర్లు పాటించే మొట్టమొదటి భద్రతా సూత్రం: డిఫెన్సివ్ ప్రోగ్రామింగ్ (Defensive Programming). ఎవరైనా పొరపాటున శూన్య చిరునామాను (NULL pointer) ఫంక్షన్కు పాస్ చేస్తే, దానిని డిరిఫరెన్స్ చేయబోతే ఆపరేటింగ్ సిస్టమ్ ప్రోగ్రామ్ను వెంటనే క్రాష్ చేస్తుంది (Segmentation Fault)!
#include <stdio.h>
int safelyIncrement(int *ptr) {
// 🛡️ గార్డ్ క్లాజ్: పాయింటర్ NULL కాదా అని తనిఖీ చేయి!
if (ptr == NULL) {
printf("🚨 హెచ్చరిక: అక్రమ శూన్య పాయింటర్ (NULL Pointer) గుర్తించబడింది! సెగ్ఫాల్ట్ నివారించబడింది.\n");
return -1; // విఫలమైనట్లు సంకేతం
}
(*ptr)++; // సురక్షితమైన ఇంక్రిమెంట్
return 0; // విజయం
}
int main() {
int counter = 10;
int *validPtr = &counter;
int *nullPtr = NULL; // ఎక్కడికీ పాయింట్ చేయని పాయింటర్
safelyIncrement(validPtr);
printf("చెల్లుబాటు అయ్యే పాయింటర్ ఇంక్రిమెంట్ అయింది: %d\n", counter);
safelyIncrement(nullPtr); // ప్రోగ్రామ్ క్రాష్ అవ్వకుండా సురక్షితంగా రక్షించబడుతుంది!
return 0;
}
ఇప్పటివరకు నేర్చుకున్న Call by Reference, మల్టిపుల్ పాయింటర్స్, మరియు ఎర్రర్ హ్యాండ్లింగ్ను కలిపి ఒక నిజమైన బ్యాంకింగ్ ఫండ్ ట్రాన్స్ఫర్ ఇంజిన్ను (Bank Fund Transfer System) నిర్మిద్దాం. ఒక ఖాతా నుండి మరొక ఖాతాకు నగదు బదిలీ జరగాలంటే రెండు ఖాతాల బ్యాలెన్స్లు ఒకే ట్రాన్సాక్షన్లో సరిగ్గా మారాలి:
#include <stdio.h>
// బ్యాంక్ ఫండ్ ట్రాన్స్ఫర్ ఫంక్షన్ (Call by Reference)
int transferFunds(double *fromAccount, double *toAccount, double amount) {
// 1. సేఫ్టీ చెక్: పాయింటర్లు చెల్లుబాటు అయ్యేవేనా?
if (fromAccount == NULL || toAccount == NULL) {
printf("❌ ట్రాన్సాక్షన్ విఫలమైంది: ఖాతా పాయింటర్లు చెల్లవు!\n");
return 0;
}
// 2. అమౌంట్ చెక్: బదిలీ మొత్తం పాజిటివ్గా ఉండాలి
if (amount <= 0) {
printf("❌ ట్రాన్సాక్షన్ విఫలమైంది: బదిలీ మొత్తం సున్నా లేదా నెగెటివ్ ఉండకూడదు!\n");
return 0;
}
// 3. సరిపడా నిధులు ఉన్నాయా? (Insufficient Funds Check)
if (*fromAccount < amount) {
printf("❌ ట్రాన్సాక్షన్ విఫలమైంది: ఖాతాలో సరిపడా బ్యాలెన్స్ లేదు! (లభ్యత: రూ. %.2f)\n", *fromAccount);
return 0;
}
// 4. అసలైన మెమరీ లొకేషన్లలో బ్యాలెన్స్లను అప్డేట్ చేయి
*fromAccount -= amount; // పంపే వారి ఖాతా నుండి మినహాయింపు
*toAccount += amount; // అందుకునే వారి ఖాతాకు జమ
printf("✅ ట్రాన్సాక్షన్ విజయవంతమైంది: రూ. %.2f బదిలీ చేయబడింది!\n", amount);
return 1;
}
int main() {
double rahulBalance = 25000.00; // రాహుల్ ఖాతా
double snehaBalance = 8500.00; // స్నేహ ఖాతా
printf("==================================================\n");
printf(" మనా బ్యాంకింగ్ కోర్ సిస్టమ్ (Fund Transfer Engine)\n");
printf("==================================================\n");
printf("బదిలీకి ముందు:\n");
printf(" రాహుల్ బ్యాలెన్స్: రూ. %.2f\n", rahulBalance);
printf(" స్నేహ బ్యాలెన్స్ : రూ. %.2f\n", snehaBalance);
printf("--------------------------------------------------\n");
// రాహుల్ ఖాతా నుండి స్నేహ ఖాతాకు రూ. 7,000 బదిలీ చేద్దాం
printf("-> రాహుల్ నుండి స్నేహకు రూ. 7,000 బదిలీ చేస్తున్నాం...\n");
transferFunds(&rahulBalance, &snehaBalance, 7000.00);
printf("--------------------------------------------------\n");
printf("బదిలీ తర్వాత:\n");
printf(" రాహుల్ బ్యాలెన్స్: రూ. %.2f\n", rahulBalance);
printf(" స్నేహ బ్యాలెన్స్ : రూ. %.2f\n", snehaBalance);
printf("==================================================\n");
return 0;
}
swap(a, b) అని రాస్తే సాధారణ విలువ వెళ్తుంది. పాయింటర్ పారామీటర్కు అడ్రస్ పాస్ చేయడానికి కచ్చితంగా swap(&a, &b) అని రాయాలి.
ptr = 50; అని రాస్తే పాయింటర్ పట్టుకున్న చిరునామానే మారిపోతుంది! లోపల ఉన్న విలువను మార్చడానికి *ptr = 50; అని రాయాలి.
ఎటువంటి వేరియబుల్ అడ్రస్ ఇవ్వకుండా ఖాళీ పాయింటర్ను డిరిఫరెన్స్ చేస్తే ఆపరేటింగ్ సిస్టమ్ మెమరీ క్రాష్ అవుతుంది.
పాయింటర్ విలువను పెంచడానికి
(*ptr)++ అని బ్రాకెట్స్ వాడాలి. *ptr++ అని రాస్తే పాయింటర్ అడ్రస్ పెరిగిపోతుంది!
ఫంక్షన్ ముగియగానే లోకల్ మెమరీ నాశనమవుతుంది కాబట్టి లోకల్ వేరియబుల్ పాయింటర్ను బయటకు రిటర్న్ చేయకూడదు.
ఈ పాఠంలోని లోతైన అంశాలను క్రింది 5 ప్రాక్టికల్ ప్రశ్నల ద్వారా పరీక్షించుకోండి:
❓ ప్రశ్న 1: C లో Call by Reference ను సాంకేతికంగా ఎలా అమలు చేస్తారు?
&var) పాయింటర్ల ద్వారా పాస్ చేయడం ద్వారా (Pass by Address / Pointer) Call by Reference ను అమలు చేస్తారు.
❓ ప్రశ్న 2: swap(int a, int b) ఫంక్షన్ ఒరిజినల్ వేరియబుల్స్ను ఎందుకు మార్చలేదు?
main() లోని ఒరిజినల్ వేరియబుల్స్ మారవు.
❓ ప్రశ్న 3: ఒక ఫంక్షన్ పారామీటర్ను 'const int *ptr' అని డిక్లేర్ చేస్తే ఏం జరుగుతుంది?
❓ ప్రశ్న 4: C ఫంక్షన్ నుండి ఒకే సమయంలో రెండు లేదా ఎక్కువ విలువలను ఎలా రిటర్న్ చేయవచ్చు?
*ptr1 = val1; *ptr2 = val2; అని అసలైన వేరియబుల్స్లోకి నేరుగా రాతలు రాయడం ద్వారా మల్టిపుల్ రిటర్న్స్ సాధించవచ్చు.
❓ ప్రశ్న 5: NULL పాయింటర్ను డిరిఫరెన్స్ చేయడానికి (*ptr) ప్రయత్నిస్తే ఏం జరుగుతుంది?
if (ptr != NULL) చెక్ చేయాలి.
పైన వివరించిన బ్యాంకింగ్ ఫండ్ ట్రాన్స్ఫర్ మరియు పాయింటర్ స్వాపింగ్ ప్రోగ్రామ్లను మన ఆన్లైన్ C కంపైలర్లో ఓపెన్ చేసి స్వయంగా రన్ చేయండి. ఖాతా బ్యాలెన్స్లను మార్చి వివిధ లావాదేవీలను టెస్ట్ చేయండి!
📌 అధ్యాయ సారాంశం (Quick Summary):
- Call by Value: విలువల నకలు కాపీని పంపుతుంది; కాలర్ వేరియబుల్స్ సురక్షితంగా ఉంటాయి.
- Call by Reference: వేరియబుల్స్ యొక్క మెమరీ అడ్రస్ను (
&) పంపుతుంది; నేరుగా మార్పులు జరుగుతాయి. - పాయింటర్లు: మెమరీ చిరునామాలను పట్టుకునే ప్రత్యేక వేరియబుల్స్.
- మల్టిపుల్ రిటర్న్స్: పాయింటర్ల ద్వారా ఒకే ఫంక్షన్ నుండి బహుళ ఫలితాలను పొందవచ్చు.
- const భద్రత: పాయింటర్ ద్వారా డేటా మార్చబడకుండా రీడ్-ఓన్లీ రక్షణ కల్పిస్తుంది.