A TUTORIAL ON POINTERS AND ARRAYS IN C
by Ted Jensen
मराठी अनुवाद
केवळ डॉ. विजय गोखले सरांमुळेच ...
सुगत मानकर
सरांबद्दल अधिक माहिती
मुळ PDF खालील site वर उपलब्ध
http://pweb.netcom.com/~tjensen/ptr/cpoint.htm
हा मराठी अनुवाद आवडला असल्यास अभिप्राय नक्की कळवा .....!!!
काही चुका असल्यास त्या आधी कळवा..!!!
msugad@gmail.com
+917875500863
int *ptr;
बर , या सगळ्या technical गोष्टी पहिल्याच प्रयत्नात समजेल अस मी गृहीत धरत
नाही, काळ आणि वेळे सोबत तुम्ही हे २ प्रकरण परत परत वाचाल. चला आता pointers character arrays आणि
strings कडे वळू.
हा मराठी अनुवाद आवडला असल्यास अभिप्राय नक्की कळवा .....!!!
काही चुका असल्यास त्या आधी कळवा..!!!
msugad@gmail.com
+917875500863
TABLE OF CONTENTS
CHAPTER 1: What is a pointer?
CHAPTER 2: Pointer types and Arrays
CHAPTER 3: Pointers and Strings
CHAPTER 4: More on Strings
CHAPTER 5: Pointers and Structures
CHAPTER 6: Some more on Strings, and Arrays of Strings
CHAPTER 7: More on Multi-Dimensional Arrays
CHAPTER 8: Pointers to Arrays
CHAPTER 9: Pointers and Dynamic Allocation of Memory
CHAPTER 10: Pointers to Functions
प्रकरण १: Pointer काय आहे?
C मधील अशी एक गोष्ट आहे जी C शिकायला सुरवात करणाऱ्यांना अवघड वाटते ती म्हणजे Pointer. ह्या धड्याचा
उद्देश Pointer
ची ओळख आणि
त्याचा वापर कसा करावा हा आहे . मला असं एकमेव कारण सापडलं कि C शिकायला सुरवात
करणाऱ्यांची Pointer सोबत असलेली अडचण म्हणजे ते Variable ला फारच कमी
लेखतात किंवा त्यांना Variable च्या अस्तित्वाची जाणीव होत नाही. म्हणून आपण थोडं Variable बद्दल जाणून घेऊया.
Program मधला एखादा Variable म्हणजे एखाद दिलेलं नाव, आणि त्याची किंमत (Value) कमी जास्त होत
राहते. त्या Variable ला कॉम्पुटर च्या मेमरी मध्ये काही जागा राखीव कशी, कुठे, किती मिळेल आणि
त्या जागेवर त्याची Value कशी टाकल्या जाईल
याची सोय Compiler आणि Linker
करून देतात. Variable किती मर्यादेपर्यंत वाढणार आहे त्यावर त्या ब्लॉक ची size ठरलेली असते.
उदा. PC's वर Integer Variable ची size हि २ byte असते आणि जर long integer असेल तर ४ byte असते. C मधे variable च्या प्रकारचा म्हणजेच Integer किंवा Char किंवा float किंवा double इत्यादींचा size हा प्रत्येक मशीन वर
सारखाच असेल असं काही नाही, तो मशीन वर
अवलंबून असतो.
जेव्हा आपण एकदा variable जाहीर(Declare) करतो तेव्हा आपण compile ला दोन गोष्टी सांगतो, त्यातली एक म्हणजे त्या variable च नाव आणि दुसरी म्हणजे तो variable कोणत्या प्रकारचा आहे.
उदा.
int k;
असं लिहून आपण इथे integer प्रकारचा k नावाचा variable declare केला. int हा शब्द बघून compiler मेमरी मध्ये integer ची value ठेवण्यासाठी २ byte (PC असेल तर) जागा होईल याची
सोय करतो. तसेच Compiler एक symbol table पण तयार करतो त्यात तो K नावाच्या symbol ची आणि मेमरी मध्ये मिळालेला address
जिथे २ byte ची जागा काढून ठेवण्यात आली त्याची नोंद
करतो.
जेव्हा आपण k = 5; असं लिहू, तेव्हा मेमरी मध्ये
मिळालेल्या address वर २ byte जागेमध्ये ५ हि value टाकलेली असेल आणि C मध्ये अशा प्रकारच्या variable ला Object म्हणतात.
एका अर्थाने object k संबंधित दोन "Value" आहेत.
इथे lvalue म्हणजे माणसाना समजणाऱ्या भाषेत K पण compiler च्या भाषेत Address. तर rvalue म्हणजे उजव्या बाजूला असलेली किंमत ५.
c/c++ मधील सर्व variables हि l-value type ची आहेत , म्हणजे काय सर ??
सर्व variables हि l-value type ची आहेत म्हणजे l-value हि ती value असते जिला address असतो/ जागा असते (मेमरी मध्ये). l-value हे नावच ते assignment statement च्या डाव्या बाजूला असल्यामुळेच पडलेलं आहे. जेव्हा जेव्हा आपण काही assignment करू तेव्हा तेव्हा assignment operator च्या डावीकडे l-valueच असली पाहिजे म्हणजेच त्याला मेमरी मध्ये जागा मिळालेली असलीच पाहिजे.
समजा एखाद statement ५ = ६ अस लिहिल तर ते compile error देईल, कारण ५ हि l-value नाही आहे, त्याला मेमरी नाही आहे, त्यामुळे ५ ची value ६ नाही करू शकत ,५ हा ५च आहे एक नम्बर आहे.
जेव्हा l-value = "काही तरी value" असं आपण assignment करतो तेव्हा l-value ज्या मेमरी ऍड्रेस वर आहे ती value overwrite केली जाते .
l-values च्या उलट r-values आहे. r-value हि कोणतीही value आहे जी आपण l-value ला assigned करू शकतो. r-values ह्या नेहमी evaluate केल्या जातात त्यातून एखादी value काढण्यासाठी.
r-values ची उदाहरण
C च्या standardization च्या नियमा प्रमाणे rvalue हि assignment statement च्या डाव्या बाजूला वापरता येत नाही म्हणून ५ = k; हे समीकरण चुकीच आहे. तस बघायला गेल तर वर केलेली "lvalue" ची व्याख्या थोड्या प्रमाणात C साठी बदलते, K&R II (पान नंबर १९७):[१]
- एक म्हणजे त्या जागेवर ठेवलेला integer (वरच्या उदाहरणामध्ये ५ ) काहीजण यालाच rvalue (right value, उच्चार "are value")
- दुसरी म्हणजे मेमोरी मधल्या जागेची “value” म्हणजेच k चा Address. काहीजण यालाच lvalue (left value, उच्चार "elvalue") संबोधतात.
इथे lvalue म्हणजे माणसाना समजणाऱ्या भाषेत K पण compiler च्या भाषेत Address. तर rvalue म्हणजे उजव्या बाजूला असलेली किंमत ५.
c/c++ मधील सर्व variables हि l-value type ची आहेत , म्हणजे काय सर ??
सर्व variables हि l-value type ची आहेत म्हणजे l-value हि ती value असते जिला address असतो/ जागा असते (मेमरी मध्ये). l-value हे नावच ते assignment statement च्या डाव्या बाजूला असल्यामुळेच पडलेलं आहे. जेव्हा जेव्हा आपण काही assignment करू तेव्हा तेव्हा assignment operator च्या डावीकडे l-valueच असली पाहिजे म्हणजेच त्याला मेमरी मध्ये जागा मिळालेली असलीच पाहिजे.
समजा एखाद statement ५ = ६ अस लिहिल तर ते compile error देईल, कारण ५ हि l-value नाही आहे, त्याला मेमरी नाही आहे, त्यामुळे ५ ची value ६ नाही करू शकत ,५ हा ५च आहे एक नम्बर आहे.
जेव्हा l-value = "काही तरी value" असं आपण assignment करतो तेव्हा l-value ज्या मेमरी ऍड्रेस वर आहे ती value overwrite केली जाते .
l-values च्या उलट r-values आहे. r-value हि कोणतीही value आहे जी आपण l-value ला assigned करू शकतो. r-values ह्या नेहमी evaluate केल्या जातात त्यातून एखादी value काढण्यासाठी.
r-values ची उदाहरण
- r-values म्हणजे single numbers (उदा ५ , हे ५ म्हणजे ५च evaluate होते )
- variables (उदा x , मागची जी कोणती value assigned केलेली होती , ती evaluate होईल )
- expressions ( उदा ३ + x )
C च्या standardization च्या नियमा प्रमाणे rvalue हि assignment statement च्या डाव्या बाजूला वापरता येत नाही म्हणून ५ = k; हे समीकरण चुकीच आहे. तस बघायला गेल तर वर केलेली "lvalue" ची व्याख्या थोड्या प्रमाणात C साठी बदलते, K&R II (पान नंबर १९७):[१]
"An object is a named region of
storage; an lvalue is
an expression referring to an
object."
“एखादा object म्हणजेच मेमोरी मधल्या जागेला दिलेलं नाव आणि lvalue हे त्या Object ला
संबोधित करणार समीकरण (expression).”
प्रोग्रामर लोक l-values आणि r-values बद्दल कधीही भर भरून बोलत नाहीत आणि ह्या व्याख्या पाठ केल्याच पाहिजे असेही काही नाही फक्त सांगण्याचा मुद्दा हाच आहे कि "assignment statement च्या डाव्या बाजूला काही तरी असल पाहिजे जे मेमरी मधल्या जागेला संबोधित करेल उदा variable. उजव्या बाजूला असलेल कितीही मोठं statement हे evaluate होऊन एकच value तयार होते "
मात्र या
मुद्द्याला धरून वर केलेली l-values आणि r-values व्याख्या पुरेशी आहे. पुढे जस जस आपण pointers सोबत मिसळू तस तस pointers बद्दल अधिक माहिती मिळेल.
बर आता असे समजू
int j, k;
k = 2;
j = 7; <-- line 1
k = j; <-- line 2
वरील
उदाहरणात compiler पहिल्या ओळीचा अर्थ असा लावतो कि j म्हणजे त्याचा address
(त्याची lvalue) आणि ७ हि value त्या address वर टाकण्यासाठी चा code तयार करतो.
पण दुसऱ्या ओळीत k चा address हा lvalue तर j हा rvalue होतो, इथे j हा त्याच्या
पत्त्यावर असलेल्या value ला संबोधित करतो. या अवस्थेत compiler j चा address k मध्ये value म्हणून न
टाकता j च्या address वर असलेली value म्हणजेच ७ टाकण्यासाठीचा code तयार करतो.
म्हणून Run-time ला k ची किंमत ७ होते.
वरील सर्व उदाहरणामध्ये आपण २ byte चा integer वापरला
आहे म्हणजेच २ byte च्या एका जागेवर असलेल्या rvalues दुसऱ्या २ byte जागेवर copy
केल्या. जर आपण long integer वापरला असता तर आपण ४ byte जागा व्यापली असती. आता अस समजू कि काही जरुरी कारणात्सव आपल्याला lvalue
म्हणजेच address ठेवणारा variable पाहिजे. अशी value ठेवणाऱ्या variable ची size
हि system वर अवलंबून असते. जुन्या desktop computer वर तर ६४ k एकूण मेमरी
असायची, अशा अवस्थेत address हा २ byte मध्ये बसेल अशी सोय होती, जर कॉम्पुटर
मध्ये जास्त मेमरी असेल तर address ला जास्त जागा देऊ शकतो. काही कॉम्पुटर जस IBM PC यावर segement आणि offset
सांभाळायला विशिष्ट handling ची गरज लागते.
प्रत्यक्ष बघितलं तर अशा variable ला size किती
लागणार हे तितक महत्वाच नाही कारण आपल्याला variable मध्ये address टाकायचा आहे अस
compiler ला सांगायला आपल्याकडे मार्ग आहे , अशा variables ना pointer variable म्हणतात. C मध्ये आपण असा variable त्याच्या नावाच्या आधी * चिन्ह लाऊन जाहीर/declare करतो.
तसेच आपण pointer कोणत्या type चा आहे हे सुद्धा declare करतो, सहसा pointer चा
type तो दर्शवित असलेया जागेवर कोणत्या प्रकारचा data आहे यावर ठरतो.
उदा:
खाली declare केलेला variable बघा
int *ptr;
इथे
ptr हे variable च नाव आहे (जस वर आपण k बघितलं होत). इथला “*” compiler ला सांगतो
कि याला pointer variable पाहिजे म्हणजेच याला मेमरी मध्ये काही जागा काढून ठेव
त्या जागेवर तो address टाकणार आहे. तर int सांगतो कि जो address ठेवणार आहे ना तो integer type च्या data चा आहे बर का
..! आणि अशाच प्रकारच्या pointers ना "point
to" an integer अस म्हणतात.
जेव्हा
आपण int k; अस लिहील होत तेव्हा k ला कोणती value दिली नव्हती पण आजकालचा कोणताही
ANSI चे नियम पाळणारा compiler असेल आणि declaration हे method च्या बाहेर असेल तर
compiler k ला 0 value देतो. तसच जर
pointer variable सुद्धा method च्या
बाहेर declare असेल (ग्लोबल) तर compiler त्याला अशी value देतो जी कोणत्याही c object किवा
method ला दर्शवित नाही. अशा प्रकारे initialize होणाऱ्या pointers
ना “null” pointers म्हणतात.
प्रत्यक्ष null
pointers साठी वापरण्यात येणारा bit pattern हा
कदाचित 0 असूही शकतो नसूही शकतो
कारण हे प्रत्यक्ष system वर अवलंबून असतो ज्या system वर तो code तयार झाला. म्हणून
तयार होणारा code हा मागच्या जुन्या किवा नवीन compilers ला सुसंगत (compatible)
राहायला हवा त्यासाठी null pointer असल्यास null macro वापरतात त्याला “NULL”
macro म्हणतात. जेव्हा आपण ptr = NULL; अस म्हणतो तेव्हा तो pointer null pointer
होतो. तसच जर आपण if(k==0) पडताळतो तसच if(ptr == NULL) पडताळता येत. आता समजा
आपल्याला ptr मध्ये integer variable k चा address ठेवायचा असेल तर आपल्याला &
unary operator वापराव लागेल.
ptr = &k;
जरी
k हा उजव्या बाजूला असला तरी त्याची lvalue म्हणजेच address काढण हे &
operator च काम आहे, आणि तो address आपल्या ptr variable ला देन. आता कुठे आपण
म्हणू शकतो ptr हा k ला दर्शवतो (point) करतो. आपण अजून एका operator ची ओळख करून
घेऊ. तो म्हणजे “*” यालाच "dereferencing
operator" म्हणतात. यालाच “value at” अस सुद्धा ओळखतात.
*ptr = 7; / *(&k)=7; / value at address k is 7
जर
ptr “points to” k (ज्या मध्ये address आहे) तर compiler ७ हि value ptr नि
दर्शविलेल्या address वर टाकतो/copy करतो. म्हणून वरील समीकरणामध्ये k ची value ७
बनते.
अशाप्रकारे
जेव्हा जेव्हा आपण * वापरू तेव्हा तेव्हा pointer नि point केलेल्या value ला आपण
बघत असतो खुद pointer च्या address ला नाही. जेव्हा आपल्याला ptr नि point
केलेल्या address वरची integer value print करायची तेव्हा
printf("%d\n",*ptr); अस लिहाव.
हे सगळ समजण्यासाठी खाली दिलेला प्रोग्राम एकदा run
करून बघा आणि त्याच output बारकाईने बघा.
------------ Program 1.1 --------------------------------- /* Program 1.1 from PTRTUT10.TXT 6/10/97 */ #include <stdio.h> int j, k; int *ptr; int main(void) { j = 1; k = 2; ptr = &k; printf("\n"); printf("j has the value %d and is stored at %p\n", j, (void *)&j); printf("k has the value %d and is stored at %p\n", k, (void *)&k); printf("ptr has the value %p and is stored at %p\n", ptr, (void*)&ptr); printf("The value of the integer pointed to by ptr is %d\n", *ptr); return 0; }
टीप:(void *) बद्दल पुढील प्रकरणामध्ये बघू आता फक्त वापरून बघा.
पुनरावलोकन:
- एखादा variable हे त्याचा type आणि नाव देऊन declare करतात (उदा int k;)
- एखादा pointer variable हे त्याचा type आणि नाव देऊन declare करतात (उदा int *ptr;) इथे * हा compiler ला सांगतो कि ptr नावाच variable हे pointer variable आहे आणि type हा compiler ला सांगतो कि कुठल्या type च्या data ला हा point करतोय.
- एकदा variable declare केला कि त्याच्या नावापुढे & operator वापरून त्याचा address शोधता येतो. उदा &k
- unary * operator वापरून आपण pointer point करत असलेल्या ठिकाणची value मिळवू शकतो उदा *ptr.
- variable ची lvalue म्हणजेच मेमरी मधला address आणि rvalue म्हणजेच त्या address वर असलेली value.
पहिल्या
प्रकरणासाठी संदर्भ:
"The C Programming Language" 2nd Edition
B. Kernighan and D. Ritchie
Prentice Hall
ISBN 0-13-110362-8
प्रकरण २: Pointer types आणि Arrays
चला
मंडळी मग पुढे जाऊ. अस समजा कि आपल्याला pointer ज्याला point करतोय त्या variable
चा type माहिती असन का गरजेच आहे ? जस
int *ptr;
जेव्हा
एखादा pointer variable कुणाला तरी point करतो आणि जेव्हा आपण *ptr = 2; अस लिहितो तेव्हा compiler ला कळत कि किती byte point केलेल्या मेमरी location वर copy करायचे आहे. जर ptr हा pointing to an integer असेल तर २ byte copy
होतील. जर long असेल तर ४ byte copy होतील. अशाच प्रकारे float double साठी सुद्धा
योग्य तेव्हढे byte copy होतील.
pointer point करत असलेल्याचा type define करण्यामागे code interpret करताना
compiler साठी अजून एक वेगळा फायदा आहे. उदा. समजा मेमरी मधल्या एखाद्या block
मध्ये 10 integer एका row मधे आहेत म्हणजेच २० byte मेमरी compiler नि 10 integer
ठेवन्यासाठी राखीव ठेवायची आहे आणि आता अस समजा आपला ptr त्या 10 मधल्या पहिल्या
integer ला point करतोय, अजून अस समजूया कि सध्या तो पहिला integer मेमरी मध्ये
१००व्या जागेवर आहे , अशा वेळी जर आपण अस लिहील तर काय होईल ?
ptr + 1;
इथे
compiler ला आधीच माहिती झाल कि ptr हा pointer आहे (म्हणजेच त्याची value हि
address आहे) आणि हा तर integer type च्या data ला point करतोय ज्याचा address आपण
१०० पकडला. जेव्हा आपण ptr + 1; करतो आता compiler ptr मध्ये १ add न करता integer ची
size add करतो म्हणजेच २. म्हणून आता pointer आता १०२ या जागेला point करतोय. अशाच
प्रकारे जर pointer long चा असता तर १ add न होता १०० + ४ = १०४ add झाले असते.
हाच नियम बाकी data type ला लागू होतो उदा float , double , user defined data
type सुद्धा जस structure. हि बेरीज साध्या बेरजे प्रमाणे न करता याला "pointer arithmetic" या शब्दांनी ओळखली
जाते.
अशाच
प्रकारे ++ptr आणि ptr++ दोन्ही ptr + 1; याच्या बरोबरीचे आहेत (जरी अगोदर कि नंतर वाढणार याचा क्रम कसाही असला तरी ), pointer
चा address हा जर unary ++ operator वापरून , pre किंवा post वापरून वाढवणार असेल
तर तो sizeof(type) च्या size नुसार
वाढेलच. इथे "type" हा
कुठल्या type च्या object ला pointed केला
आहे यावर ठरतो (जस 2 एका integer
साठी , 4 long साठी ..).
Array
च्या व्याखेप्रमाणे बघितल तर 10 integer चा block हा एका मागोमाग एक
असा सलग जागा व्यापतो, हेच एक मनोरंजक pointers आणि array मधल नात उघड करत.
खालील उदा बघा:
int my_array[] = {1,23,17,4,-5,100};
इथे
६ integer असलेला array आहे. त्यातला प्रत्येक integer आपण my_array या subscript
न my_array[0] ते my_array[5] वापरून घेऊ शकतो. पण आपण हेच काम pointer वापरून
सुद्धा करता येईल ते कस बघूया.
int *ptr; ptr = &my_array[0]; /* array मधल्या पहिल्या integer ला ptr point करतोय*/
आणि
आता array मधला प्रत्येक integer print करू.
----------- Program 2.1 -----------------------------------
/* Program 2.1 from PTRTUT10.HTM 6/13/97 */
#include<stdio.h>
int my_array[] = {1,17,23,4,-6,35};
int *ptr;
int main(void)
{
int i;
ptr = &my_array[0]; /*pointer array मधल्या पहिल्या element ला point करतोय */
printf(“n\n”);
for(i=0; i<6 ;i++){
printf(“my_array[%d] = %d”,i,my_array[i]); /*<-- A */
printf(“ptr + %d= %d \n”,i,*(ptr+i)); /*<-- B */
}
return 0;
}
वरील
प्रोग्राम Compile आणि run करा काळजीपूर्वक lines
A आणि B बघा ,प्रोग्राम तर दोन्ही ओळींची सारखीच output देतोय. तसच
हे पण बघा दुसऱ्या ओळीत आपण ptr variable कसा dereference
केला , प्रथम आपण १ add केला
आणि मग dereference केला. आता line B खालील
प्रमाणे बदला:
printf("ptr + %d = %d\n",i, *ptr++);
आणि
परत compile आणि run करा.. परत खालील प्रमाणे बदला:
printf("ptr + %d = %d\n",i, *(++ptr));
printf("ptr + %d = %d\n",i, *(++ptr));
आणि
परत एकदा compile आणि run करा. प्रत्येक वेळी आलेली output काळजीपूर्वक बघा. c
मध्ये standardization प्रमाणे
जेव्हा जेव्हा आपण &var_name[0]
अस वापरू हेच फक्त variable च नाव दिल तरी सारखच आहे . उदा.
ptr = &my_array[0];
यालाच
खालीलप्रमाणे सुद्धा लिहिता येईल
ptr = my_array;
येणार
output सारखच असेल. बघा करून काय येते ते.
याच
उदाहरणावरून बरेच जण म्हणतात arrayच नाव हे pointer आहे. pointerला सुरवात
करणाऱ्याची (त्यात मीही जेव्हा सुरवात केली होती) हीच गोची होते कि array च नाव
म्हणजे pointer. पण तस न म्हणता array च नाव म्हणजे array मधल्या पहिल्या element
चा address अस म्हणायला काही हरकत नाही.
उदा आपण
ptr = my_array; अस लिहू शकतो
पण my_array = ptr; अस नाही लिहू शकत.
कारण
ptr हा variable आहे आणि my_array हा constant आहे, म्हणजेच my_array[] एकदा
declare केला कि array मधल्या पहिल्या element चा address कधी बदलू शकत नाही.
अगोदर
आपण “lvalue” बघताना K&R-2 मधल
वाक्य बघितलं होत:
"An object is a named region
of storage; an lvalue is an expression referring to an
object."
“एखादा object म्हणजेच memory मधल्या
जागेला दिलेलं नाव आणि lvalue
हे त्या Object ला संबोधित करणार समीकरण (expression).”
तर
इथे एक प्रश्न पडतो, जस आधी म्हटल्याप्रमाणे my_array हे मेमरी मधल्या जागेला
दिलेलं नाव आहे, तर मग my_array वरच्या
समीकरणात lvalue का नाही ठरत?? हा
प्रश्न सोडविण्यासाठी काहीजण my_array ला
"unmodifiable lvalue"
संबोधतात.
वरचा
प्रोग्राम खालच्या ओळी ऐवजी
ptr = &my_array[0];
अशी
ओळ वापरा
ptr = my_array;
परत
एकदा compile आणि run करून बघा, उत्तर तेच येइल.
चला
ptr आणि my_array
या नावामध्ये असलेला फरक अजून खोलात जाऊन तपासुया. काही लेखक arrayच नाव म्हणजे constant pointer अस संबोधतात. हो पण नेमक काय? बर प्रथम इथे constant म्हणजे काय ते समजून घेऊ. चला एकदा परत variable ची व्याख्या बघू. जेव्हा आपण
एखादा variable declare करतो तेव्हा आपण मेमरी मध्ये काही जागा पाहिजे त्या type
च्या size प्रमाणे राखीव ठेवतो. हे एकदा झाल कि मग आपण variable ला २ प्रकारे interpret करतो. जेव्हा variable जर assignment operator च्या डाव्या बाजूस असेल तेव्हा compiler त्याला “डावी बाजू म्हणजे मेमरी location ज्यावर
उजवीकडच्या समीकरणातून येणारी value ठेवली जाईल” अस interpret करतो, पण variable जर assignment operator च्या उजव्या बाजूस असेल तेव्हा compiler variable च्या नावाला “variable
च्या राखीव address वर असलेली value” अस interpret
करतो.
हेच डोक्यात ठेवून खालील constants बघा
int i, k;
i = 5;
k = i;
इथे
i हा variable आहे आणि data segment मध्ये २ byte मेमरी घेईल. 5 हा constant आहे,
अस २ byte ची मेमरी बाजूला काढण्यापेक्षा जर code segment मधेच embed केल आणि आता k= i; अस लिहील तर हे compiler ला सांगत कि असा code तयार कर कि जो run time ला i
च्या address वर (&i) असलेली ५ हि value k
ला देईल. अस केल तर data segment चा कचरा कमी होईल. म्हणजेच k आणि i दोन्ही object
आहेत तर ५ हा constant.
आता
हेच उदाहरण घेऊ my_array हा
constant आहे, जस compiler नि एकदा array कुठे ठेवायचं ती जागा ठरवली रे ठरवली तो
लगेच my_array[0] ला address देतो आणि जस ptr = my_array; बघीतल कि लगेच तो address हा constant म्हणून code segment मधल्या मेमरी मध्ये टाकतो आणि त्यापुढच्या array मधल्या कोणत्याही element
ला data segment वर reference देत नाही.
आपण
मागील प्रकरणात (void *) expression वापरल
होत ते काय आहे हे जाणून घेण्यासाठी आता वेळ आली आहे, तर बघूया काय आहे (void *).
जस pointers चे प्रकार बघितले pointers to integers आणि pointers to characters. पुढील प्रकरणात आपण pointers to structures आणि pointer to
pointers
सुद्धा बघणार आहोत. तसेच
आपण हे सुद्धा बघितलं कि वेगवेगळ्या system वर pointer चा size बदलत जातो. हे
सुद्धा शक्य आहे कि pointer चा size हा pointer point करत असलेला data चा data
type कोणता आहे त्यावर सुद्धा अवलंबून आहे. म्हणून integers सोबत अडचणी येऊ शकतात.
उदा
जर एखादा long integer value एखाद्या short integer type असलेल्या variable ला
assign केला तर अडचण येते. अशाचप्रकारे जर एका type च्या pointer ची value जर
दुसऱ्या type च्या pointer variable ला assign केली तर अडचण येतेच. अशा प्रकारच्या
अडचणी सोडवण्यासाठी c एक void type असलेला pointer देतो. आपण त्याला खालील प्रमाणे
लिहू शकतो.
void *vptr;
void pointer हा generic pointer म्हणून ओळखला जातो. उदा
तर c मध्ये integer type चा pointer सोबत character type चा pointer यांचा फरक
काढण्यास परवानगी देत नाही. अस करायचं असेल तर कुठल्याही type चाpointer हा फक्त
void pointer सोबतच compare करता येतो. साहजिकच जर कोणत्याही pointer ला generic
बनवायचं असेल तर त्याला void pointer सोबत type cast कराव लागेल. पहिल्या
प्रकरणामध्ये पहिल्या प्रोग्राम मध्ये आपण integer type चा pointer void type च्या
pointer ला type cast केला कारण %p हा “print as a pointer” असतो.
प्रकरण ३: Pointer आणि Strings
pointers
आणि arrays अजून चांगल समजून घ्यायच असेल तर Strings चा अभ्यास करन गरजेच आहे. तसच
c मधील काही standard string functions आतमध्ये कशी बनवली असतील याची स्पष्ट कल्पना येईल. आणि शेवटी pointers ला functionचा parameter बनवून कधी आणि कस
पाठवायचं हे पण बघूया.
c
मध्ये characters चा array म्हणजेच string. हेच दुसऱ्या भाषांमध्ये खर असेल अस
नाही.
BASIC, Pascal, FORTRAN आणि बाकीच्या काही
languages मध्ये, string ला स्वतःचा data type आहे. पण
c मध्ये नाही. C मध्ये string म्हणजे characters
चा
array आणि त्याचा शेवट binary
zero character नि होतो ('\0'). चला
काही code बघूया,समजण्यासाठी
आपण खालील code लिहिणार आहो कदाचित तुम्ही अस कधी लिहीत बसणार नाही
उदाहरण
बघा:
char my_string[40];
my_string[0] = 'T';
my_string[1] = 'e';
my_string[2] = 'd':
my_string[3] = '\0';
जरी
कुणी अशाप्रकारे string बनवत बसणार नसला तरी याच उत्तर “nul character नि शेवट
झालेल्या characters चा array” असच येईल. हे लक्षात असुद्या “nul” हा “NULL”
सारखा नाही. इथे nul म्हणजे ‘\0’, हा एक character आहे म्हणजेच याला १ byte ची
मेमरी लागते. तर NULL हा macro आहे आणि तो c च्या header file मध्ये #defined आहे.
nul हा #defined असू शकतो किंवा नसू पण शकतो.
वरील
उदाहरणाप्रमाणे string तयार करायला भरपूर वेळ लागतो, c कडे string तयार करण्याचे अजून
मार्ग आहे.
एखादा
असा पण लिहील:
char my_string[40] = {'T', 'e', 'd', '\0'};
पण
इथे परत typing भरपूर करावं लागेल. तर, C खालील प्रमाणे लिहिण्यास परवानगी देते:
char my_string[40] = "Ted";
single
quotes च्या ऐवजी जर double quotes वापरले तर compiler
आपणहून string च्या शेवटी nul character ( '\0' ) टाकतो.
वरील
सर्व उदाहरणात string एकच तयार होणार, ४० size दिल्यामुळे compiler सलग ४० byte ची
मेमरी देतो आणि त्यातले पहिले ४ characters मध्ये
Ted\0 टाकतो.
आता
खालील प्रोग्राम बघा:
------------------program 3.1-------------------------------------
/* Program 3.1 from PTRTUT10.HTM 6/13/97 */
#include<stdio.h>
char strA[80] = “A string to be used for demonstration purpose”;
char strB[80] ;
int main(void){
char *pA; /* a pointer to type character */
char *pB; /* another pointer to type character */
puts(strA); /* show string A */
pA = strA; /* point pA at string A */
puts(pA); /* show what pA is pointing to */
pB = strB; /* point pB at string B */
putchar('\n'); /* move down one line on the screen */
while(*pA != '\0') /* line A (see text) */
{
*pB++ = *pA++; /* line B (see text) */
}
*pB = '\0'; /* line C (see text) */
puts(strB); /* show strB on screen */
return 0;
}
--------------------------- end program 3.1 -------------------------------------
इथे
आपण २ character array प्रत्येकी ८० byte चे declare केले, हे global असल्यामुळे
initialize नसेल तर compiler
त्यातले सर्व ‘\0’ नि initialize करतो.
strA मध्ये पहिले ४२ double quote नि
व्यापलेले आहेत.
आता
आपण २ character pointer घेतले आहे आणि त्याला print केल. तसच आपण pointer pA नि strA ला
"point" केल. म्हणजेच strA[0]
चा
address variable pA मध्ये copy केला. puts()वापरून pA ला
print केल. खालील function चा prototype
बघा:
int puts(const char *s);
काही
क्षणासाठी const कडे दुर्लक्ष करा. puts() ला पाठवलेला parameter
हा
एक pointer आहे, म्हणजेच pointer
ची
value (c मधील सर्व parameters हे passed
by value आहेत), आणि
इथे pointer ची value म्हणजे ज्याला point
करतो त्याचा address. सोप्या शब्दात सांगायचं तर सरळ सरळ puts() ला आपण address
पाठवला. म्हणून जेव्हा आपण puts(strA); अस लिहितो तेव्हा strA[0] चा address पाठवतो.
अशाचप्रकारे
जेव्हा आपण puts(pA); लिहू तेव्हा आपण puts ला strA[0] चा address
पाठवतोय कारण आपण pA = strA; अस केल आहे.
आता
while loop बघा.
Line
A: जोपर्यंत pA (म्हणजेच *pA) ने point केलेला character हा nul म्हणजेच ‘\0’ येत
नाही तोपर्यंत खालील code चालव.
*pB++ = *pA++;
Line B: pA न point केलेला character pB च्या point केलेल्या जागेवर copy कर, आता pA
आणि pB दोघानाही वाढवा
जेणेकरून pA आणि pB दोघंही आपल्या पुढील character ला point करतील. अस करत करत जेव्हा आपण शेवटचा character copy करून
झालेला असेल तेव्हा pA nul character ला point करेल त्यामुळे loop मधून आपण बाहेर
येऊ. तरीसुद्धा आपण nul character
copy केल नाही. आणि c च्या definition प्रमाणे “string
in C must be nul terminated”. c मधली string nul
character नि शेवट झालीच पाहिजे. म्हणून आपण Line ३ मध्ये pB ला शेवटी nul
character लावला.
जर
तुम्ही debugger लाऊन strA, strB, pA and pB आणि प्रत्येक single
step
बघितली तर तुम्हाला खूप लवकर कळेल. तसच
आपण strB[] वर initialize केली नव्हती ती आता
खालील प्रमाणे रिकामी न ठेवता भरून प्रोग्राम चालवून बघा.
strB[80] = "12345678901234567890123456789012345678901234567890"
इथे
वापरलेले आकडे strA च्या string पेक्षा जास्त आहेत, debug करून प्रत्येक step बघा
एकदा !!
परत
एकदा काही क्षणासाठी puts()
चा prototype बघूया,
int puts(const char *s);
आपण
parameter ला "const" लावल, जेव्हा
आपण अस पाठवू तेव्हा आपण user ला सांगत असतो कि s नि point केलेली string function
बदलू शकणार नाही. तो पूर्ण string constant म्हणून वागवली जाईल.
वरच्या
प्रोग्राम मध्ये फक्त साधी एक string दुसऱ्या string मध्ये कशी copy करायची एवढंच
केल.
जर
आपण काय करत आहे हे समजल असेल तर आपण आपल
standard strcpy() सारख function लिहू
char *my_strcpy(char *destination, char *source)
{
char *p = destination;
while (*source != '\0')
{
*p++ = *source++;
}
*p = '\0';
return destination;
}
या
प्रोग्राम मध्ये आपण destination return करण्यासाठी standard practice वापरली.
पुन्हा
हे function आता दोन character
pointers
च्या value म्हणजेच address
घेईल आणि वरचा main प्रोग्राम आपण असा लिहू
int main(void)
{
my_strcpy(strB, strA);
puts(strB);
}
आपल्या
वरील method चा standard C प्रमाणे prototype
खालील प्रमाणे असायला पाहिजे:
char *my_strcpy(char *destination, const char *source);
इथे
वापरलेला "const" modifier user ला खात्रीपूर्वक सांगतो कि source pointer नि point
केलेला source data हा function बदलणार नाही. जेव्हा जेव्हा आपल्याला अशी खात्री
पाहिजे तेव्हा parameter च्या आधी "const"
modifier
वरील दर्शविल्याप्रमाणे वापरावा .
आता जर
*source = 'X';
अस
केल तर const नसताना source ची string “X” झाली असती पण आता const असल्यामुळे
करायला गेल तर compiler error
देईल. करा आणि बघा.
आता
वरील उदाहरणातल्या काही गोष्टी बघू, पहिली गोष्ट *ptr++ हा
प्रथम ptr नि point केलेली value return करतो आणि मग वाढवतो. जो काही राडा करायचा
तो operators च्या precedence सोबत.
जर आपण (*ptr)++ अस लिहील असत तर इथे
pointer increment नाही होणार,समजा आता ptr ‘T’ ला point करतोय आणि आपण (*ptr)++
केल तर इथे ‘T’ चा ‘U’ होईल.
अशाप्रकारे
आपण copy प्रोग्राम characters
च्या array साठी केला तसच integers
च्या array साठी, Doubles च्या array
साठी सुद्धा करता येईल पण integer, double , float असल्यास शेवट '\0' नि करायची
गरज नाही. array च शेवट ओळखणार आपल function लिहू शकतो , जस आपल्याला फक्त positive integers copy करायचे असतील तर शेवटच character negative integer ठेऊन करता येईल.
तसच आपल्याला किती character copy करायचे हे अजून एक parameter टाकून आपण लिहू
शकू.
void int_copy(int *ptrA, int *ptrB, int nbr);
इथे nbr हा किती
integers copy झाले पाहिजे तो आकडा घेतो. तुम्हाला
हि idea आवडली असेल तर करून बघायला काही हरकत नाही.
अशाप्रकारे
functions ला pointer parameter वापरून आपण
मोठे मोठे arrays सहज manipulate करू
शकतो. उदा जर आपला array 5000 integers चा आहे आणि त्याला function मध्ये manipulate करायचं आहे,
तर संपूर्ण array न जाता फक्त array चा address जाईल. जर
पूर्ण array गेला असता तर सगळा array manipulate करायच्या आधीच stack वर जाऊन बसला
असता. जर function ला value पाठवली तर original integer ला काही करू शकणार नाही . पण जर arrays
and pointers असतील तर आपण address पाठवू शकतो आणि original
variables च्या value manipulate करू शकतो.
चला मंडळी, आपण कमी वेळात बरच काही शिकलो अहो, परत एकदा मागे जाऊन बघूया, string copy करण्यासाठी आपण काय केल होत, आता खालील function बघा:
परत एकदा strings म्हणजेच characters चे arrays. इथे आपण copy करताना pointer न वापरता array वापरला आहे. दोन्ही प्रकारात उत्तर एकच येणार आहे. यातील काही interesting points आपण बघूया.
C मध्ये parameters हे passed by value आहेत, आपण पहिल्या प्रोग्राम मध्ये character pointer पाठवला आणि दुसऱ्या array च नाव. पण बघितल तर array च्या पहिल्या element चा address pass झाला.म्हणजेच source[i] हे *(p+i) याच्या बरोबर आहे अस म्हणता येत. आणि हे खर आहे जेव्हा जेव्हा आपण a[i] अस लिहू तेव्हा कुठलीही शंका न आणता त्याच्या ठिकाणी आपण *(a + i) टाकू शकतो. कारण compiler आत मध्ये दोन्ही cases मध्ये एकच code तयार करतो. जरी syntax दोन्ही साठी एकच उत्तर देतो, म्हणून आपण सरळ म्हणू शकतो pointer arithmetic is the same thing as array indexing.
याचा अर्थ असा नाही होत कि pointers आणि arrays दोन्ही एकच आहेत. आणि ते नाहीच आहेत. आपण फक्त अस म्हणतोय कि array चे element काढायला दोन मार्ग आहे एक म्हणजेarray indexing वापरून आणि दुसरा pointer arithmetic वापरून, कोणताही वापरलं तरी एकच उत्तर येणार.
एकदा शेवटच्या expression मधला (a + i) हा भाग बघितला तर यात + वापरून केलेली साधी बेरीज आहे, आणि अशा प्रकारच्या expression ला C च्या नियमानुसार commutative expression म्हणतात. म्हणजेच (a + i) हे (i + a) असाही लिहू शकतो. तसच आपण *(i + a) च्या जागी *(a + i) अस लिहू शकतो.
जस a[i] ला *(a + i) अस करता येत असेल तर *(i + a) ला आपण i[a] लिहू शकतो चला करून बघूया खरच i[a] चालेल कि नाही ते:
Array indexing आणि pointer arithmetic दोन्ही एकच उत्तर देतात त्यामुळे वर बघितलं तस आपल्या function मध्ये आपण:
dest[i] = source[i]; च्या ऐवजी
जर pointer version अजून speed up करायचं असेल तर खालील प्रमाणे वापरता येईल:
आता तुम्ही pointer वापरून काही प्रयोग experiment करण्यास उत्सुक असाल, strings ला Manipulate करन खूप चांगल आहे. तुम्हाला खालील standard functions आपल्या मताने लिहायला आवडेल:
strlen();
strcat();
strchr();
आपण pointers वापरून परत strings आणि त्याचे manipulation पुढील प्रकरणात घेऊ. त्या आधी थोड structure बद्दल जाणून घेऊया.
जस तुम्हाला माहित असेल, आपण वेगवेगळा संचय असलेला data एकाच block मध्ये structure वापरून लिहू शकतो.
उदा. एखाद्या व्यक्तीचं file structure खालील प्रमाणे असू शकते
आता अस समजू कि अशाप्रकारचे भरपूर structures एका disk file मध्ये आहे आणि आपल्याला त्यातले सर्व वाचायचे आहेत आणि नाव व आडनाव print करायचं म्हणजे आपल्याला कल्पना येईल कि किती लोकांची माहिती त्या file मध्ये आहे. बाकीची माहिती print करायची नाही. आपण printing चं काम करण्यासाठी एक function call करू, त्या function ला structure चा pointer pass करू.
उदा:
structure चे member dot operator वापरून घेता येतात:
वरील उदा घेतलेलं structure लहान आहे आपण त्यात अजून data member टाकू शकतो जस
date_of_hire; (data types not shown)
date_of_last_raise;
last_percent_increase;
emergency_phone;
medical_plan;
Social_S_Nbr;
etc.....
समजा आपल्याकडे खूप सारे employees आहेत आणि functions वापरून आपल्याला या structure मध्ये असलेला data manipulate करायचा आहे. उदा: आपल्याला अस function लिहायला आवडेल जे कोणताही structure parameter म्हणून pass केल तर त्यातला नाव आडनाव वगैरे print करेल. पण जर original C (Kernighan & Ritchie, 1st Edition) बघितलं तर त्यात आपण पूर्ण structure pass करूच शकत नाही. फक्त त्या structure ला point करत असलेला pointer pass करू शकतो. पण आता ANSI C मध्ये पूर्ण structure pass करता येत. आपला उद्देश pointer वापरून structure manipulation करायचा आहे त्यामुळे आपण pointer वर लक्ष देऊ.
असो, जर आपण पूर्ण structure pass केल तर आपल्याला structure चे पूर्ण content calling function मधून called function मध्ये टाकावे लागतील. यासाठी system stack चा वापर करते, म्हणून इथे structure चे सर्व content stack वर टाकल्या जातील. पण जर structure खूप मोठ असेल तर मग अडचणी येतील. पण जर structure चा pointer pass केला तर stack वरची बरीच मेमरी वाचेल. चला तर मग बघूया कस structure चा pointer पाठवायचा आणि कसा वापरायचा.
वर म्हटल्याप्रमाणे आपल्याला एक अस function लिहायचं आहे जे structure चा pointer हा parameter म्हणून स्वीकारेल आणि त्या function मध्ये आपण structure चे members access करू. समजा आपल्याला employee च नाव print करायचं आहे.
चला मग structure ला point करणारा एक pointer declare करू, असे pointer struct tag वापरून करता येतात.
आणि त्याला आता वरच्या structure ला point करू :
आता आपण de-referencing pointer वापरून सगळे member घेऊ शकतो. पण structure च्या pointer ला dereference करायचं कस??
समजा आपल्याला employee च वय टाकायचं (set करायचं) आहे तर आपण खालील प्रमाणे लिहू:
वरची ओळ काळजीपूर्वक बघा, (*st_ptr) इथे bracket मध्ये असलेला pointer ज्याला point करतोय त्याला replace कर असा अर्थ होतो. म्हणजेच structure my_struct.
हा प्रोग्राम debug करून प्रत्येक step बघा my_struct आणि p कसा बदलत जातो ते प्रत्येक step ला बघा इथे खूप माहिती मिळेल.
चला काही क्षणासाठी strings परत बघूया. खालील सर्व उदाहरणामध्ये global scope वापरू.म्हणजेच कोणत्याही function च्या बाहेर, main() सुद्धा. आपण मागील प्रकरणात खालील प्रमाणे string वापरली होती
प्रत्यक्ष, जर आपल्याला "Ted" हे नाव store करायच आहे तर आपण असही लिहू शकतो:
array वापरताना my_name वापरा किंवा &my_name[0] वापरा दोन्ही वेळेस array च्या पहिल्या element चा address मिळणार. जस आपण बघितलं array च location हे run time ला fix होत. array हा constant आहे (variable नाही). Pointer notation वापरताना my_name हे variable आहे.
समजा आपण हेच काम global न करता function च्या आत केल तर काय होईल?
इथे my_function_A च्या वेळेस , array a[] च्या value(s) हा data बनतात, म्हणजेच array आता ABCDE value नि initialize झाला तर my_function_B च्या वेळेस pointer cp ची value data होते. इथे pointer initialized केला आणि string FGHIJ ला point करत आहे. दोन्ही my_function_A आणि my_function_B मध्ये local variables आहेत म्हणून string ABCDE stack वर ठेवल्या जाईल तर pointer cp ची value, string FGHIJ कुठेही ठेवता येईल.माझ्या system वर data segment मध्ये गेली.
जुन्या K&R C प्रमाणे बघितलं तर my_function_A मध्ये array च केलेल initialization हे चुकीच आहे पण नवीन ANSI C मध्ये चालेल.पण portability and backwards compatibility चा विचार करायला पाहिजे.
चला आता multi-dimensional arrays बघूया.
उदा:
char multi[5][10];
याचा अर्थ काय? char multi[5][10]; इथे underlined part हे array नाव आहे अस समजू. नावाच्या अगोदर char आणि नंतर [10]. म्हणजेच आपल्याकडे 10 characters चा array आहे. पण इथे नाव multi[5] हा स्वतः 5 elements असलेला array आहे आणि प्रत्येक array मधे 10 characters आहेत. म्हणून आपल्याकडे ५ element असलेला array आहे आणि प्रत्येक array मध्ये 10 characters आहेत.
ह्याच माहिती नुसार जर data भरला तर खालील प्रमाणे दिसेल:
Arrays हे मेमरी मध्ये contiguous असल्यामुळे, वरील उदाहरणाचा actual memory block
हा असा दिसेल:
0123456789abcdefghijABCDEFGHIJ9876543210JIHGFEDCBA
^
|_____ starting at the address &multi[0][0]
टीप मी multi[0] = "0123456789" अस लिहिलेल नाही. या आधी मी अस म्हटल होत double quotes string असेल तर शेवटी terminating '\0' असायला पाहिजे. म्हणून इथे मला प्रत्येक row मध्ये ११ character ची जागा करा लागेल. 2 dimensional arrays च मेमरी mapping कस होते हा माझा सांगण्याचा उद्देश आहे, इथे 2 dimensional characters चा array आहे, "strings" चा नाही.
आता array मध्ये किती columns आहेत हे compiler ला माहिती आहे. multi + 1 यालाच compiler दुसऱ्या row मध्ये असलेल्या character a चा address अस interpret करतो. character a चा address location शोधण्यासाठी तो row मध्ये एकूण किती columns आहेत त्याची बेरीज करतो उदा इथे 10.
जर आपण int multi[5][10] चा array बघितला असता तर compiler नि दुसऱ्या row चा पहिला element शोधायला 10*sizeof(int) इतकी बेरीज केली असती, माझ्या system वर int ची size २ byte आहे तर इथे compiler 20 ची बेरिज करेल. वरील उदाहरणात चौथ्या row मध्ये असलेल्या पहिल्या element (9) चा address आपण &multi[3][0] किंवा सरळ 9 value *(multi + 3) pointer notation वापरून मिळवला असता. अशाच प्रकारे दुसऱ्या element चा address आपण खालील प्रमाणे 1 add करून मिळवू शकू.
दोघे पण एकच उत्तर देतील. चला एक integer array चा प्रोग्राम करून बघूया
Pointer version मध्ये double de-referencing वापराव लागल, त्यामुळे 2 dimensional array ला pointer to a pointer अस हि म्हणता येईल. Three dimensional array ला array of arrays of arrays म्हणता येईल आणि काहीजण त्यालाच a pointer to a pointer to a pointer म्हणतील.
compiler array च notation म्हणजेच int multi[][] बघून मेमरी मध्ये जागा राखीव ठेवतो आणि array हा constant आहे variable नाही. आपण fixed address बद्दल बोलत होतो variable pointer बद्दल नाही. वरील उदाहरणात बघितल्याप्रमाणे dereferencing function वापरून, address न बदलता कोणताही element आपण काढू शकतो ( multi[0][0] चा address म्हणजेच symbol multi नि दिलेला address).
आपण मागील प्रकरणात खालील प्रमाणे array initialize केला होता
यावरून आपण x हा pointer असल्या सारखा आहे कारण expression हे de-referenced आहे तसच त्यातला col हा integer आहे ते आपल्याला माहिती आहे. याच बेरजेला "pointer arithmetic" अस म्हणतात हे आपण अगोदर बघून झाल आहे. X + col + 1 नि point केलेला address हा X + col च्या address पेक्षा sizeof(int) नि जास्त असला पाहिजे.
प्रत्यक्ष आपल्याला 2 dimensional arrays चा memory layout माहिती आहे, expression multi + row वर वापरल्याप्रमाणे, multi + row + 1 तेवढ्या किमतीने वाढलीच पाहिजे जी किमत पुढील row मध्ये point करते. म्हणजेच ती किंमत COLS * sizeof(int) .
जर expression *(*(multi + row) + col) run time ला बरोबर evaluate व्हायला पाहिजे असेल तर compiler नि असा code तयार केला पाहिजे जो COLS ची value काढेल, म्हणजेच 2nd dimension.
जरी आपण दोन्ही प्रकारचे expression एकच असले तरी आपण pointer expression वापराव कि array expression multi[row][col] ?
कोणताही एक expression वापरायच असेल तर एकूण 5 values माहिती असायलाच पाहिजे:
1. Array मधल्या पहिल्या element चा address, तो आपण multi, म्हणजेच, array च नाव लिहून मिळवतो.
2. Elements च्या type ची size, जस sizeof(int).
3. Array चा 2nd dimension.
4. पहिल्या dimension ची index value, म्हणजेच row.
5. दुसऱ्या dimension ची index value, म्हणजेच col.
वरील सर्व माहिती आहे अस समजून एक function लिहू जे declared array च्या element ची value manipulate करत. उदा. समजा array multi मधील सर्व element ची value 1 आहे हे गृहीत धरू.
आणि हे function आपण अस वापरू:
set_value(multi);
आपण loop limit करण्यासाठी #defined केलेल्या ROWS and COLS value वापरल्या पण compiler च्या दृष्टिकोनातून बघितल तर #defines फक्त constants आहे, याचं array च्या size शी काही घेण देन नाही. row आणि col हे local variables. आपल्याला पहिला dimension ची गरज नाही, का नाही ? हे आपण पुढे बघू.
अशा काही योग्य वेळी आहेत जिथे पहिला dimension ची value parameter definition मध्ये नसायला पाहिजे, परंतु second dimension असायलाच पाहिजे. कारण आपल्याला m_array[row][col] evaluate करायचं आहे जस वरती केल. आता parameter define करताना data type (इथे int ) आणि for loops मध्ये row आणि column साठी लागणारी automatic variables म्हणजेच ROWS किंवा COLS यापैकी कोणतीही एकच value single parameter आपण पाठवू शकतो. अशा परिस्थिती मध्ये जेव्हा आपण call करताना set_value(multi); अस pass केल multi म्हणजेच first element चा address (pointer to the array) pass केला, दुसऱ्या element च काय? हे टाळण्यासाठी आपण compiler ला 2nd dimension हे parameter definition मध्ये आहे हे स्वतःहून सांगाव लागत. जस void set_value(int m_array[][COLS]);
हेच तत्व सर्व dimensions ना लागू होत. समजा 3 dimensional arrays चा array असता तर, parameter definition मध्ये 2nd आणि 3rd dimension असायलाच पाहिजे.
उदा. void set_value(int m_array[][Y][Z]);
जस आपण अगोदर program 3.1 मध्ये बघितलं pointers एका character array सोबत कसा वापरायचा, जर multi-dimensional arrays असेल तर तो कसा वापरायचा हे पण तितकच महत्वाच आहे. प्रकरण २ मध्ये आपण बघितल होत int type चा pointer array मधल्या पहिल्या int element ला point कसा करतो:
int *ptr;
इथे बघितल तर pointer variable चा type array मधल्या पहिल्या element शी जुळलाच पाहिजे.
तसच आपण pointer चा वापर function चा formal parameter म्हणून पण करू शकतो
उदा:
काही मंडळी function prototype असा पण लिहितील:
आता आपण 2 dimensional array बद्दल बघू. जस मागच्या प्रकरणात बघितल कि, C मध्ये 2 dimensional array ला array of one dimensional arrays अस interpret करते. 2 dimensional integers array मधला first element हा one dimensional integer चा array आहे. आणि त्या element ला point करत असलेला pointer त्याच data type चा असायला पाहिजे. तर हे आपण "typedef" keyword वापरून साध्य करू शकतो. typedef च काम specified data type ला नवीन नाव देण, नवीन ओळख देण उदा:
byte b[10]; जेव्हा अस लिहू तेव्हा याचाच अर्थ “b हा unsigned char type चा array आहे”.
वरील typedef च्या declaration मध्ये byte हा शब्द unsigned char म्हणून वापरला आहे.
अजून उदाहरण बघूया
typedef int Array[10];
इथे Array हा 10 integers असलेल्या array चा data type झाला. म्हणजेच जेव्हा आपण अस लिहू.
Array my_arr;
इथे my_arr हा array of 10 integers आणि
Array arr2d[5];
इथे arr2d हा 5 arrays चा array आहे आणि प्रत्येक array मध्ये 10 integers आहेत. तसच
Array *p1d;
इथे p1d हा pointer to an array of 10 integers आहे. आता *p1d हा same type ला point करतोय जस arr2d करतोय, म्हणजेच आपण two dimensional array म्हणजेच arr2d चा address p1d ला assign करू शकतो.
p1d = &arr2d[0];
or
p1d = arr2d;
वरील दोन्ही एकच आहे.
आपण आपल्या pointer साठी वापरलेला data type Array हा 10 integers चा array असल्यामुळे जेव्हा आपण pointer arithmetic करू म्हणजेच p1d ला १ नि increment करू तेव्हा तो 10*sizeof(int) नि वाढेल. इथे sizeof(*p1d) 20 आहे . तुम्ही सुद्धा हे एक छोटा प्रोग्राम लिहून run करून सिद्ध करू शकता.
typedef चा वापर केला तर वाचताना आणि प्रोग्राम मध्ये वापरताना सोप जात,परंतु वापरायलाच पाहिजे याच काही बंधन नाही. आपल्याला p1d pointer typedef keyword न वापरता declare करायचा आहे .
पण खालील उदा हे वरच्या पेक्षा पूर्ण वेगळ आहे.
int *p1d[10];
याचा अर्थ इथे p1d हे 10 integer pointers असलेल्या array च नाव आहे.
प्रकरण ४: Strings बद्दल अजून काही
चला मंडळी, आपण कमी वेळात बरच काही शिकलो अहो, परत एकदा मागे जाऊन बघूया, string copy करण्यासाठी आपण काय केल होत, आता खालील function बघा:
char *my_strcpy(char dest[],char source[]){
int i = 0;
while (source[i] !='\0') {
dest[i] = source[i];
i++;
}
dest[i] ='\0';
return dest;
}
परत एकदा strings म्हणजेच characters चे arrays. इथे आपण copy करताना pointer न वापरता array वापरला आहे. दोन्ही प्रकारात उत्तर एकच येणार आहे. यातील काही interesting points आपण बघूया.
C मध्ये parameters हे passed by value आहेत, आपण पहिल्या प्रोग्राम मध्ये character pointer पाठवला आणि दुसऱ्या array च नाव. पण बघितल तर array च्या पहिल्या element चा address pass झाला.म्हणजेच source[i] हे *(p+i) याच्या बरोबर आहे अस म्हणता येत. आणि हे खर आहे जेव्हा जेव्हा आपण a[i] अस लिहू तेव्हा कुठलीही शंका न आणता त्याच्या ठिकाणी आपण *(a + i) टाकू शकतो. कारण compiler आत मध्ये दोन्ही cases मध्ये एकच code तयार करतो. जरी syntax दोन्ही साठी एकच उत्तर देतो, म्हणून आपण सरळ म्हणू शकतो pointer arithmetic is the same thing as array indexing.
याचा अर्थ असा नाही होत कि pointers आणि arrays दोन्ही एकच आहेत. आणि ते नाहीच आहेत. आपण फक्त अस म्हणतोय कि array चे element काढायला दोन मार्ग आहे एक म्हणजेarray indexing वापरून आणि दुसरा pointer arithmetic वापरून, कोणताही वापरलं तरी एकच उत्तर येणार.
एकदा शेवटच्या expression मधला (a + i) हा भाग बघितला तर यात + वापरून केलेली साधी बेरीज आहे, आणि अशा प्रकारच्या expression ला C च्या नियमानुसार commutative expression म्हणतात. म्हणजेच (a + i) हे (i + a) असाही लिहू शकतो. तसच आपण *(i + a) च्या जागी *(a + i) अस लिहू शकतो.
जस a[i] ला *(a + i) अस करता येत असेल तर *(i + a) ला आपण i[a] लिहू शकतो चला करून बघूया खरच i[a] चालेल कि नाही ते:
char a[20];
int i;
a[3] = 'x'; अस लिहा किंवा 3[a] = 'x';
बघा करून ! एखादा characters, integers or longs, इत्यादी चा array घ्या त्यात 3rd किंवा 4th element ला value द्या वर सांगितल्या प्रमाणे आणि print करून बघा काय येत ते चांगला compiler असेल तर 3[a] च्या वेळेस भूकणार नाही आणि उत्तर एकच येईल. एक कुतूहल ... अधिक काही नाही !Array indexing आणि pointer arithmetic दोन्ही एकच उत्तर देतात त्यामुळे वर बघितलं तस आपल्या function मध्ये आपण:
dest[i] = source[i]; च्या ऐवजी
*(dest + i) = *(source + i); अस लिहू शकू.
पण इथे प्रत्येक i च्या value ला दोनदा बेरीज करावी लागते. तस बघायला गेल तर Additions करायला incrimination (जे ++ operator जस i++ करताना वापरल) पेक्षा जास्त वेळ लागतो. हे आजच्या modern optimizing compilers च्या बाबतीत खर असेल अस सागंता येत नाही. पण pointer version हे array version पेक्षा लवकर होते.जर pointer version अजून speed up करायचं असेल तर खालील प्रमाणे वापरता येईल:
while (*source != '\0')
ऐवजी
while (*source)
अस वापरावं लागेल. दोन्ही case मध्ये शेवटी zero (FALSE) येईल.आता तुम्ही pointer वापरून काही प्रयोग experiment करण्यास उत्सुक असाल, strings ला Manipulate करन खूप चांगल आहे. तुम्हाला खालील standard functions आपल्या मताने लिहायला आवडेल:
strlen();
strcat();
strchr();
आपण pointers वापरून परत strings आणि त्याचे manipulation पुढील प्रकरणात घेऊ. त्या आधी थोड structure बद्दल जाणून घेऊया.
प्रकरण ५: Pointers आणि Structures
जस तुम्हाला माहित असेल, आपण वेगवेगळा संचय असलेला data एकाच block मध्ये structure वापरून लिहू शकतो.
उदा. एखाद्या व्यक्तीचं file structure खालील प्रमाणे असू शकते
struct tag {
char lname[20]; /* last name */
char fname[20]; /* first name */
int age; /* age */
float rate; /* e.g. 12.75 per hour */
};
आता अस समजू कि अशाप्रकारचे भरपूर structures एका disk file मध्ये आहे आणि आपल्याला त्यातले सर्व वाचायचे आहेत आणि नाव व आडनाव print करायचं म्हणजे आपल्याला कल्पना येईल कि किती लोकांची माहिती त्या file मध्ये आहे. बाकीची माहिती print करायची नाही. आपण printing चं काम करण्यासाठी एक function call करू, त्या function ला structure चा pointer pass करू.
उदा:
structure चे member dot operator वापरून घेता येतात:
--------------- program 5.1 ------------------
/* Program 5.1 from PTRTUT10.HTM 6/13/97 */
#include <stdio.h>
#include <string.h>
struct tag {
char lname[20]; /* last name */
char fname[20]; /* first name */
int age; /* age */
float rate; /* e.g. 12.75 per hour */
};
struct tag my_struct; /* declare the structure my_struct */
int main(void)
{
strcpy(my_struct.lname,"Jensen");
strcpy(my_struct.fname,"Ted");
printf("\n%s ",my_struct.fname);
printf("%s\n",my_struct.lname);
return 0;
}
-------------- end of program 5.1 --------------
वरील उदा घेतलेलं structure लहान आहे आपण त्यात अजून data member टाकू शकतो जस
date_of_hire; (data types not shown)
date_of_last_raise;
last_percent_increase;
emergency_phone;
medical_plan;
Social_S_Nbr;
etc.....
समजा आपल्याकडे खूप सारे employees आहेत आणि functions वापरून आपल्याला या structure मध्ये असलेला data manipulate करायचा आहे. उदा: आपल्याला अस function लिहायला आवडेल जे कोणताही structure parameter म्हणून pass केल तर त्यातला नाव आडनाव वगैरे print करेल. पण जर original C (Kernighan & Ritchie, 1st Edition) बघितलं तर त्यात आपण पूर्ण structure pass करूच शकत नाही. फक्त त्या structure ला point करत असलेला pointer pass करू शकतो. पण आता ANSI C मध्ये पूर्ण structure pass करता येत. आपला उद्देश pointer वापरून structure manipulation करायचा आहे त्यामुळे आपण pointer वर लक्ष देऊ.
असो, जर आपण पूर्ण structure pass केल तर आपल्याला structure चे पूर्ण content calling function मधून called function मध्ये टाकावे लागतील. यासाठी system stack चा वापर करते, म्हणून इथे structure चे सर्व content stack वर टाकल्या जातील. पण जर structure खूप मोठ असेल तर मग अडचणी येतील. पण जर structure चा pointer pass केला तर stack वरची बरीच मेमरी वाचेल. चला तर मग बघूया कस structure चा pointer पाठवायचा आणि कसा वापरायचा.
वर म्हटल्याप्रमाणे आपल्याला एक अस function लिहायचं आहे जे structure चा pointer हा parameter म्हणून स्वीकारेल आणि त्या function मध्ये आपण structure चे members access करू. समजा आपल्याला employee च नाव print करायचं आहे.
चला मग structure ला point करणारा एक pointer declare करू, असे pointer struct tag वापरून करता येतात.
struct tag *st_ptr;
आणि त्याला आता वरच्या structure ला point करू :
st_ptr = &my_struct;
आता आपण de-referencing pointer वापरून सगळे member घेऊ शकतो. पण structure च्या pointer ला dereference करायचं कस??
समजा आपल्याला employee च वय टाकायचं (set करायचं) आहे तर आपण खालील प्रमाणे लिहू:
(*st_ptr).age = 63;
वरची ओळ काळजीपूर्वक बघा, (*st_ptr) इथे bracket मध्ये असलेला pointer ज्याला point करतोय त्याला replace कर असा अर्थ होतो. म्हणजेच structure my_struct.
my_struct.age =63;
पण अस c मध्ये वापरल्या जात नाही, designers नि दुसरा syntax काढला त्याला अस लिहितातst_ptr->age = 63;
हेच डोक्यात ठेवून खालील प्रोग्राम बघा.------------ program 5.2 ---------------------
/* Program 5.2 from PTRTUT10.HTM 6/13/97 */
#include <stdio.h>
#include <string.h>
struct tag{ /* the structure type */
char lname[20]; /* last name */
char fname[20]; /* first name */
int age; /* age */
float rate; /* e.g. 12.75 per hour */
};
struct tag my_struct; /* define the structure */
void show_name(struct tag *p); /* function forward declaration or prototype */
int main(void)
{
struct tag *st_ptr; /* a pointer to a structure */
st_ptr = &my_struct; /* point the pointer to my_struct */
strcpy(my_struct.lname,"Jensen");
strcpy(my_struct.fname,"Ted");
printf("\n%s ",my_struct.fname);
printf("%s\n",my_struct.lname);
my_struct.age = 63;
show_name(st_ptr); /* pass the pointer */
return 0;
}
void show_name(struct tag *p)
{
printf("\n%s ", p->fname); /* p points to a structure */
printf("%s ", p->lname);
printf("%d\n", p->age);
}
-------------------- end of program 5.2 ----------------
हा प्रोग्राम debug करून प्रत्येक step बघा my_struct आणि p कसा बदलत जातो ते प्रत्येक step ला बघा इथे खूप माहिती मिळेल.
प्रकरण ६: Strings आणि Strings च्या Arrays बद्दल अजून
चला काही क्षणासाठी strings परत बघूया. खालील सर्व उदाहरणामध्ये global scope वापरू.म्हणजेच कोणत्याही function च्या बाहेर, main() सुद्धा. आपण मागील प्रकरणात खालील प्रमाणे string वापरली होती
char my_string[40] = "Ted";
इथे मेमरी मध्ये ४० byte जागा allocate होईल आणि पहिल्या ४ byte मध्ये string टाकल्या जातील. ( तीन double quotes मधील characters आणि चौथ nul character '\0').प्रत्यक्ष, जर आपल्याला "Ted" हे नाव store करायच आहे तर आपण असही लिहू शकतो:
char my_name[] = "Ted";
आणि इथे compiler characters मोजतो, nul character साठी एक byte अजून ठेवतो आणि एकूण चार characters मेमरी location वर टाकतो. तो address array च्या नावा सोबत return येतो , या case मध्ये my_name. काही code मध्ये तुम्ही असेही बघाल:char *my_name = "Ted";
हा पण एक alternate मार्ग आहे. तर या दोन्ही मध्ये काही फरक आहे का? उत्तर आहे .. हो. array वापरून केल तर 4 bytes ची जागा static memory block मध्ये व्यापलेली असेल आणि pointer वापरून केल तरी ४ byte लागणारच आहे पण अजून N bytes pointer variable म्हणजेच my_name ठेवायला लागतील (इथे N system वर अवलंबून आहे काही वर कमीतकमी 2 bytes लागतील किवा 4 किवा अजून जास्त ).array वापरताना my_name वापरा किंवा &my_name[0] वापरा दोन्ही वेळेस array च्या पहिल्या element चा address मिळणार. जस आपण बघितलं array च location हे run time ला fix होत. array हा constant आहे (variable नाही). Pointer notation वापरताना my_name हे variable आहे.
समजा आपण हेच काम global न करता function च्या आत केल तर काय होईल?
void my_function_A(char *ptr)
{
char a[] = "ABCDE"
.
.
}
void my_function_B(char *ptr)
{
char *cp = "FGHIJ"
.
.
}
इथे my_function_A च्या वेळेस , array a[] च्या value(s) हा data बनतात, म्हणजेच array आता ABCDE value नि initialize झाला तर my_function_B च्या वेळेस pointer cp ची value data होते. इथे pointer initialized केला आणि string FGHIJ ला point करत आहे. दोन्ही my_function_A आणि my_function_B मध्ये local variables आहेत म्हणून string ABCDE stack वर ठेवल्या जाईल तर pointer cp ची value, string FGHIJ कुठेही ठेवता येईल.माझ्या system वर data segment मध्ये गेली.
जुन्या K&R C प्रमाणे बघितलं तर my_function_A मध्ये array च केलेल initialization हे चुकीच आहे पण नवीन ANSI C मध्ये चालेल.पण portability and backwards compatibility चा विचार करायला पाहिजे.
चला आता multi-dimensional arrays बघूया.
उदा:
char multi[5][10];
याचा अर्थ काय? char multi[5][10]; इथे underlined part हे array नाव आहे अस समजू. नावाच्या अगोदर char आणि नंतर [10]. म्हणजेच आपल्याकडे 10 characters चा array आहे. पण इथे नाव multi[5] हा स्वतः 5 elements असलेला array आहे आणि प्रत्येक array मधे 10 characters आहेत. म्हणून आपल्याकडे ५ element असलेला array आहे आणि प्रत्येक array मध्ये 10 characters आहेत.
ह्याच माहिती नुसार जर data भरला तर खालील प्रमाणे दिसेल:
multi[0] = {'0','1','2','3','4','5','6','7','8','9'}
multi[1] = {'a','b','c','d','e','f','g','h','i','j'}
multi[2] = {'A','B','C','D','E','F','G','H','I','J'}
multi[3] = {'9','8','7','6','5','4','3','2','1','0'}
multi[4] = {'J','I','H','G','F','E','D','C','B','A'}
पाहिजे असल्यास आपण एक एक characters सुद्धा काढू शकतो उदा:multi[0][3] = '3'
multi[1][7] = 'h'
multi[4][0] = 'J'
Arrays हे मेमरी मध्ये contiguous असल्यामुळे, वरील उदाहरणाचा actual memory block
हा असा दिसेल:
0123456789abcdefghijABCDEFGHIJ9876543210JIHGFEDCBA
^
|_____ starting at the address &multi[0][0]
टीप मी multi[0] = "0123456789" अस लिहिलेल नाही. या आधी मी अस म्हटल होत double quotes string असेल तर शेवटी terminating '\0' असायला पाहिजे. म्हणून इथे मला प्रत्येक row मध्ये ११ character ची जागा करा लागेल. 2 dimensional arrays च मेमरी mapping कस होते हा माझा सांगण्याचा उद्देश आहे, इथे 2 dimensional characters चा array आहे, "strings" चा नाही.
आता array मध्ये किती columns आहेत हे compiler ला माहिती आहे. multi + 1 यालाच compiler दुसऱ्या row मध्ये असलेल्या character a चा address अस interpret करतो. character a चा address location शोधण्यासाठी तो row मध्ये एकूण किती columns आहेत त्याची बेरीज करतो उदा इथे 10.
जर आपण int multi[5][10] चा array बघितला असता तर compiler नि दुसऱ्या row चा पहिला element शोधायला 10*sizeof(int) इतकी बेरीज केली असती, माझ्या system वर int ची size २ byte आहे तर इथे compiler 20 ची बेरिज करेल. वरील उदाहरणात चौथ्या row मध्ये असलेल्या पहिल्या element (9) चा address आपण &multi[3][0] किंवा सरळ 9 value *(multi + 3) pointer notation वापरून मिळवला असता. अशाच प्रकारे दुसऱ्या element चा address आपण खालील प्रमाणे 1 add करून मिळवू शकू.
*(*(multi + 3) + 1)
बघितल तर *(*(multi + row) + col) आणि
multi[row][col]
दोघे पण एकच उत्तर देतील. चला एक integer array चा प्रोग्राम करून बघूया
------------------- program 6.1 ----------------------
/* Program 6.1 from PTRTUT10.HTM 6/13/97*/
#include <stdio.h>
#define ROWS 5
#define COLS 10
int multi[ROWS][COLS];
int main(void)
{
int row, col;
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
multi[row][col] = row*col;
}
}
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
printf("\n%d ",multi[row][col]);
printf("%d ",*(*(multi + row) + col));
}
}
return 0;
}
----------------- end of program 6.1 ---------------------
Pointer version मध्ये double de-referencing वापराव लागल, त्यामुळे 2 dimensional array ला pointer to a pointer अस हि म्हणता येईल. Three dimensional array ला array of arrays of arrays म्हणता येईल आणि काहीजण त्यालाच a pointer to a pointer to a pointer म्हणतील.
compiler array च notation म्हणजेच int multi[][] बघून मेमरी मध्ये जागा राखीव ठेवतो आणि array हा constant आहे variable नाही. आपण fixed address बद्दल बोलत होतो variable pointer बद्दल नाही. वरील उदाहरणात बघितल्याप्रमाणे dereferencing function वापरून, address न बदलता कोणताही element आपण काढू शकतो ( multi[0][0] चा address म्हणजेच symbol multi नि दिलेला address).
प्रकरण ७: Multi-Dimensional Arrays बद्दल अजून
आपण मागील प्रकरणात खालील प्रमाणे array initialize केला होता
#define ROWS 5 #define COLS 10 int multi[ROWS][COLS]; आणि वरील array मधला कोणताही element आपण खालील प्रमाणे काढला होता: multi[row][col] किंवा *(*(multi + row) + col) पूर्णपणे समजण्यासाठी *(multi + row) च्या जागी X टाकू: *(X + col)
यावरून आपण x हा pointer असल्या सारखा आहे कारण expression हे de-referenced आहे तसच त्यातला col हा integer आहे ते आपल्याला माहिती आहे. याच बेरजेला "pointer arithmetic" अस म्हणतात हे आपण अगोदर बघून झाल आहे. X + col + 1 नि point केलेला address हा X + col च्या address पेक्षा sizeof(int) नि जास्त असला पाहिजे.
प्रत्यक्ष आपल्याला 2 dimensional arrays चा memory layout माहिती आहे, expression multi + row वर वापरल्याप्रमाणे, multi + row + 1 तेवढ्या किमतीने वाढलीच पाहिजे जी किमत पुढील row मध्ये point करते. म्हणजेच ती किंमत COLS * sizeof(int) .
जर expression *(*(multi + row) + col) run time ला बरोबर evaluate व्हायला पाहिजे असेल तर compiler नि असा code तयार केला पाहिजे जो COLS ची value काढेल, म्हणजेच 2nd dimension.
जरी आपण दोन्ही प्रकारचे expression एकच असले तरी आपण pointer expression वापराव कि array expression multi[row][col] ?
कोणताही एक expression वापरायच असेल तर एकूण 5 values माहिती असायलाच पाहिजे:
1. Array मधल्या पहिल्या element चा address, तो आपण multi, म्हणजेच, array च नाव लिहून मिळवतो.
2. Elements च्या type ची size, जस sizeof(int).
3. Array चा 2nd dimension.
4. पहिल्या dimension ची index value, म्हणजेच row.
5. दुसऱ्या dimension ची index value, म्हणजेच col.
वरील सर्व माहिती आहे अस समजून एक function लिहू जे declared array च्या element ची value manipulate करत. उदा. समजा array multi मधील सर्व element ची value 1 आहे हे गृहीत धरू.
void set_value(int m_array[][COLS])
{
int row, col;
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
m_array[row][col] = 1;
}
}
}
आणि हे function आपण अस वापरू:
set_value(multi);
आपण loop limit करण्यासाठी #defined केलेल्या ROWS and COLS value वापरल्या पण compiler च्या दृष्टिकोनातून बघितल तर #defines फक्त constants आहे, याचं array च्या size शी काही घेण देन नाही. row आणि col हे local variables. आपल्याला पहिला dimension ची गरज नाही, का नाही ? हे आपण पुढे बघू.
अशा काही योग्य वेळी आहेत जिथे पहिला dimension ची value parameter definition मध्ये नसायला पाहिजे, परंतु second dimension असायलाच पाहिजे. कारण आपल्याला m_array[row][col] evaluate करायचं आहे जस वरती केल. आता parameter define करताना data type (इथे int ) आणि for loops मध्ये row आणि column साठी लागणारी automatic variables म्हणजेच ROWS किंवा COLS यापैकी कोणतीही एकच value single parameter आपण पाठवू शकतो. अशा परिस्थिती मध्ये जेव्हा आपण call करताना set_value(multi); अस pass केल multi म्हणजेच first element चा address (pointer to the array) pass केला, दुसऱ्या element च काय? हे टाळण्यासाठी आपण compiler ला 2nd dimension हे parameter definition मध्ये आहे हे स्वतःहून सांगाव लागत. जस void set_value(int m_array[][COLS]);
हेच तत्व सर्व dimensions ना लागू होत. समजा 3 dimensional arrays चा array असता तर, parameter definition मध्ये 2nd आणि 3rd dimension असायलाच पाहिजे.
उदा. void set_value(int m_array[][Y][Z]);
प्रकरण ८: Pointers to Arrays
जस आपण अगोदर program 3.1 मध्ये बघितलं pointers एका character array सोबत कसा वापरायचा, जर multi-dimensional arrays असेल तर तो कसा वापरायचा हे पण तितकच महत्वाच आहे. प्रकरण २ मध्ये आपण बघितल होत int type चा pointer array मधल्या पहिल्या int element ला point कसा करतो:
int *ptr;
ptr = &my_array[0]; /* point our pointer at the first integer in our array */
इथे बघितल तर pointer variable चा type array मधल्या पहिल्या element शी जुळलाच पाहिजे.
तसच आपण pointer चा वापर function चा formal parameter म्हणून पण करू शकतो
उदा:
int p[3] = {'1', '5', '7'};
void a_func(int *p);
काही मंडळी function prototype असा पण लिहितील:
void a_func(int p[]);
आता वरील दोन्ही मध्ये काय pass झाल असेल तर pointer ची value जिथे pointer आता array मधल्या पहिल्या element ला point करतोय, हे लक्षात असू द्या जर array notation वापरायच असेल तर array चे dimension pass करू नका कारण आपण पूर्ण array pass नाही करत आहो, आपण फक्त first element चा address pass करतो.आता आपण 2 dimensional array बद्दल बघू. जस मागच्या प्रकरणात बघितल कि, C मध्ये 2 dimensional array ला array of one dimensional arrays अस interpret करते. 2 dimensional integers array मधला first element हा one dimensional integer चा array आहे. आणि त्या element ला point करत असलेला pointer त्याच data type चा असायला पाहिजे. तर हे आपण "typedef" keyword वापरून साध्य करू शकतो. typedef च काम specified data type ला नवीन नाव देण, नवीन ओळख देण उदा:
typedef unsigned char byte;
compiler इथे unsigned char लाच byte या नावानी ओळखेल. म्हणूनbyte b[10]; जेव्हा अस लिहू तेव्हा याचाच अर्थ “b हा unsigned char type चा array आहे”.
वरील typedef च्या declaration मध्ये byte हा शब्द unsigned char म्हणून वापरला आहे.
अजून उदाहरण बघूया
typedef int Array[10];
इथे Array हा 10 integers असलेल्या array चा data type झाला. म्हणजेच जेव्हा आपण अस लिहू.
Array my_arr;
इथे my_arr हा array of 10 integers आणि
Array arr2d[5];
इथे arr2d हा 5 arrays चा array आहे आणि प्रत्येक array मध्ये 10 integers आहेत. तसच
Array *p1d;
इथे p1d हा pointer to an array of 10 integers आहे. आता *p1d हा same type ला point करतोय जस arr2d करतोय, म्हणजेच आपण two dimensional array म्हणजेच arr2d चा address p1d ला assign करू शकतो.
p1d = &arr2d[0];
or
p1d = arr2d;
वरील दोन्ही एकच आहे.
आपण आपल्या pointer साठी वापरलेला data type Array हा 10 integers चा array असल्यामुळे जेव्हा आपण pointer arithmetic करू म्हणजेच p1d ला १ नि increment करू तेव्हा तो 10*sizeof(int) नि वाढेल. इथे sizeof(*p1d) 20 आहे . तुम्ही सुद्धा हे एक छोटा प्रोग्राम लिहून run करून सिद्ध करू शकता.
typedef चा वापर केला तर वाचताना आणि प्रोग्राम मध्ये वापरताना सोप जात,परंतु वापरायलाच पाहिजे याच काही बंधन नाही. आपल्याला p1d pointer typedef keyword न वापरता declare करायचा आहे .
int (*p1d)[10];
हे proper declaration आहे, इथे p1d हा एक pointer जो 10 integers च्या array ला point करतोय. जस आपण Array type declare करताना केला होत. पण खालील उदा हे वरच्या पेक्षा पूर्ण वेगळ आहे.
int *p1d[10];
याचा अर्थ इथे p1d हे 10 integer pointers असलेल्या array च नाव आहे.
********************************************************************************************************
हा मराठी अनुवाद आवडला असल्यास अभिप्राय नक्की कळवा ..... !!!
काही चुका असल्यास त्या आधी कळवा ..!!!
काही चुका असल्यास त्या आधी कळवा ..!!!
msugad@gmail.com
+917875500863