对指针的学习总结

2018-06-18 04:20:41来源:未知 阅读 ()

新老客户大回馈,云服务器低至5折

  1   1 /*----------------------------------------
  2   2                 指针练习(精华)
  3   3 
  4   4     1)首先,要理解变量或数组的首地址,指的就是存放数据的RAM或ROM中地址号最小的那个字节地址。
  5   5 
  6   6     2)指针前面的类型说明符,具有2重意义(既决定了对一级指针进行解引用时,将会操作的字节数,以及对一级指针进行算术运算时,会跳转的地址个数)。    
  7   7       ①决定了指针加一时,指针会跳转多少个地址,
  8   8       例如:
  9   9               如果指针是
 10  10               char类型的,(指针+n) 指针会跳转n*1个地址。
 11  11               int 类型的,(指针+n) 指针会跳转n*2个地址。
 12  12               long类型的,(指针+n) 指针会跳转n*4个地址。
 13  13               
 14  14       ②并且还决定了通过指针操作地址值时,实际上会返回多少个字节的值,且地址号大的字节先返回。 
 15  15       例如:    
 16  16               假设要操作指针的一次地址返回值,那么如果指针是
 17  17               char类型的,返回1个字节。
 18  18               int 类型的,返回2个字节。
 19  19               long类型的, 返回4个字节。
 20  20               
 21  21       数组前面的类型说明符,同样具有2重意义,且跟上面的很相似。
 22  22       例如:
 23  23               #include"stdio.h"
 24  24               int c[]={0x1234,0x5678};
 25  25               void main()
 26  26               {
 27  27                 printf("%p %d\n",c,*c);            //数组是int类型意味着返回2个字节
 28  28                 printf("%p %d\n",(c+1),*(c+1));  //实际上(c+1)与c是夹着一个地址,因为数组类型符号是int,如果数组类型是long,则夹着3地址
 29  29               }
 30  30       
 31  31       也就是要注意类型所占的字节数,还有就是什么时候该看数组类型符号或者指针类型符号。
 32  32     3)&叫取首地址符号,*叫解引用符号。
 33  33 
 34  34     4)数组名是指一个首地址,所以,point=a(point是一个指针,a是一个数组名), a的前面不需要加&符号。
 35  35       变量名指的是一个值,a[1]指的也是一个值,这些值包含着一个或多个字节,在想要让指针指向这些值的字节的地址时,
 36  36       需要在变量名以及a的前面加上&符号,即意思是要让指针赋值符号(=)右边的东西是地址。
 37  37 
 38  38     5)数组或变量的数据是一个一个字节的存放的,而且字节的地址是呈现连续的,赋值的时候,从左到右看
 39  39       越往右,字节的地址号越大。因此,对于多字节数据类型的数组而言,看起来有种“首尾相连”的效果,
 40  40       因为一个元素的最低位字节其地址的加一地址对应的字节,就是下一个元素的最高位字节。
 41  41       
 42  42       简单点来说就是低地址存放高字节,这种现象称为大端排列(常用单片机)。注意:有些时候则是低地址存放低字节,这种现象称为小端排列(ARM)。
 43  43       
 44  44     6)指针可分为:函数指针,数组指针(多维指针),变量指针,结构体指针。  又可分为:多级指针,多维指针。  地址可分为:多级地址,多维地址。
 45  45 
 46  46     7)只有字符数组的最后一个元素会紧接一个隐藏元素,该元素值为0,映射的字符为“\0”。
 47  47 
 48  48     8)数据指针型函数,也叫指针函数(即返回值是一个地址)。   
 49  49     
 50  50     9)char (*p)[2];   //声明一个1维指针(也叫1维数组指针)
 51  51       分析方括号([])对多维指针的操作时,要遵循一个原则:先明确指针的维数,再分析有多少组方括号,方括号里面的数字是多少,由此得到地址是如何跳转的;
 52  52                                                         然后根据方括号的组数得知地址最终会发生多少次的解引用,如果解引用的次数少于地址的维数,
 53  53                                                         那么最终得到的还是一个地址,也如果解引用的次数等于地址的维数+1,那么得到是一个数据值。
 54  54                                                         每次对多维地址进行一次解引用后,地址的维数将会变小。
 55  55       一维数组名就是一个一级0维地址,二维数组名就是一个一级1维地址,多维数组名就是一个一级多维地址。每一个数组名都是一个地址。这些地址是固定的。
 56  56       一级多维指针的特点是:解引用的写法很特殊;运算时地址的跳转很特殊。
 57  57       探究代码如下:
 58  58       int Array[2][3][2]={{{1,2},{3,4},{5,6}},{{7,8},{9,10},{11,12}}};  //Array是一个一级2维地址
 59  59 
 60  60       printf("%d\n", Array); 
 61  61       printf("%d\n", Array[1]);   //与上一行代码相比,发生了 3*2*4=24个地址 的跳转
 62  62       printf("%d\n", Array[1][1]);    //与上一行代码相比,发生了 2*4=8个地址 的跳转
 63  63 
 64  64       printf("%d\n",*(Array[1][1]));//对0维地址进行1次解引用,得到一个数据值,为9
 65  65 
 66  66       printf("%d\n",*(*(Array[1])));    //对1维地址进行2次解引用,得到一个数据值,为7
 67  67       printf("%d\n",*(Array[1]));        //对1维地址进行1次解引用,得到的是一个0维地址,且与Array[1]值一样,但Array[1]是一个1维地址
 68  68 
 69  69     10) #include<stdio.h>  
 70  70         #include<stdlib.h>  
 71  71         int main()  
 72  72         {  
 73  73 
 74  74             //下面是存在着非法读写的演示,虽然非法读写是可以实现,但是这只能存在于同一个进程里面,而且这种情况没有什么有利的实际意义
 75  75 
 76  76             int *p;    //int类型指针,操作4个字节
 77  77             p=(int *)malloc(2);  //向堆申请了2个字节,但不进行初始化,calloc方式的申请会进行初始化
 78  78             if(p)  
 79  79                 printf("Memory Allocated at: %p\n",p);  
 80  80             else  
 81  81                 printf("Not Enough Memory!\n");
 82  82             printf("%x\n",*p);     //由于只申请了2个字节,所以非法读取了2个字节
 83  83             *p=0x12345678;        //由于只申请了2个字节,所以非法写入了2个字节
 84  84             printf("%x\n",*p);    //由于只申请了2个字节,所以非法读取了2个字节     
 85  85             free(p);   //释放堆中申请过的2个字节,并且有可能把内存中的值也清0,这要取决于运行的内核
 86  86             //下面是非法读写了4个字节
 87  87             printf("%x\n",*p);    
 88  88             *p=0x87654321;
 89  89             printf("%x\n",*p);    
 90  90             return 0;  
 91  91         } 
 92  92 
 93  93     11)经探究发现,不同类型的指针指向的地址跟指针类型不一致时,有可能会报错,也有可能只是警告而已
 94  94 
 95  95     12)unsigned char (*q)();  //声明一个函数指针
 96  96        指针形式调用函数时不给定传递参数值的话,默认是传递-1   ,  指针调用函数的格式为:"(*指针名)(形参列表)" 或 "指针名(形参列表)"
 97  97 
 98  98     13)一级和多级指针的使用:
 99  99     int val=0x1122;
100 100     char *p3=&val;
101 101     char **p2=&p3;
102 102 
103 103     printf("%x\n", p3); 
104 104     printf("%x\n", p3+1); //跳转1个地址(因为p3是个一级指针而且类型修饰符为char)
105 105 
106 106     printf("%x\n", *(p3)); //操作1个字节(因为p3是个一级指针而且类型修饰符为char)
107 107     printf("%x\n", *(p3+1));//操作1个字节(因为p3是个一级指针而且类型修饰符为char)
108 108 
109 109     printf("%x\n", (p2)); 
110 110     printf("%x\n", (p2+1)); //跳转4个地址(因为内存中字节所使用的地址长度为32位且指针p2是一个二级指针)
111 111 
112 112     printf("%x\n", *(p2)); //操作4个字节(因为内存中字节所使用的地址长度为32位且指针p2是一个二级指针)
113 113 
114 114     14)对多级多维指针的探究:
115 115     //假设已经掌握对多级0维指针,和一级多维指针的使用
116 116 
117 117     unsigned char (**p)[3];  //这是一个二级2维指针
118 118     int Array3[10]={0x804a0e0,0x55667788,2,3,4,5,6,7,8,9};  //之所以第一个元素设为0x804a0e0,是因为如果该值取的不当,下面对(*(*p))进行解引用的时候,有可能在程序执行时导致内核塌陷,看起来好像是程序语法错误,也就是说要保证解引用的指针是在正确范围内的
119 119     p=Array3;  //Array3是一个一级1维地址,先让Array3转变为二级2维地址,再让二级2维指针p所指向
120 120 
121 121     printf("%x\n", p);  //原本p是一个二级2维指针,在这里p表示为一个二级2维地址
122 122     printf("%x\n", p+1);  //发生4个地址的跳转(因为地址长度为4个字节),因为(p+1)是一个二级2维地址,也就是此行语句输出值比上一行语句大4
123 123 
124 124     printf("%x\n", *p);   //解引用得到4个字节的值,因为p是一个二级2维地址。*p 得到的是一个一级2维地址
125 125     printf("%x\n", (*p+1));  //跳转3个字节,因为 *p 属于一级2维地址,也就是此行语句输出值比上一行语句大3。因为一级指针地址的跳转,是取决于维数,*p是一个一级2维地址,那么跳转数为:sizeof(unsigned char)*[3]=3
126 126     
127 127     printf("%x\n", *(*p));  //对一级2维地址(*p)进行解引用,得到一级1维地址*(*p)
128 128 
129 129     printf("%d\n", *(*(*p))); //对一级1维地址*(*p)进行解引用,得到1个字节的值,值为0xe0也就是224,因为指针 p 的类型修饰符为char,而sizeof(unsigned char)=1
130 130 
131 131     printf("%d\n", *(*(*p))+1); //该行打印值比上一行大1,为225
132 132 
133 133     //总结:对多级多维指针进行解引用的时,每次解引用都会遵循先降级再降维,当级数没降到1,那么维数不起作用。
134 134     //      当已经是一级指针了后,维数起作用,当做一级多维指针或一级1维指针处理  ,一级1维地址再解引用就得到值了
135 135     
136 136     15)指针常量和常量指针
137 137     int a =3;  
138 138     int b = 1;  
139 139     int c = 2;  
140 140     int const *p1 = &b;//const 在前,定义为常量指针  
141 141     int *const p2 = &c;//*在前,定义为指针常量
142 142     //常量指针p1:指向的地址可以变,但内容不可以重新赋值,内容的改变只能通过修改地址指向后变换。   
143 143     //p1 = &a是正确的,但 *p1 = a是错误的。
144 144     //指针常量p2:指向的地址不可以重新赋值,但内容可以改变,必须初始化,地址跟随一生。
145 145     //p2= &a是错误的,而*p2 = a 是正确的。
146 146 
147 147     16)假设a是数组名,p是指针名,那么p=&a等效于p=a
148 148     int abc[4]={1,2,3,4};
149 149     int (*p)[4]; 
150 150     p=abc;  //等效于p=&abc;
151 151     for(i=0;i<4;++i)
152 152     {
153 153         printf("%d\n",p[0][i]);
154 154     }
155 155 
156 156     17)在sizeof()函数参数列表里,解引用符*的作用稍微发生了变化。
157 157     //下面证明了,解引用符号“ * ”在不同的场合应用,其作用发生了改变,如在sizeof()函数的参数中时,对数组名进行解引用,将会影响打印出来的数组“维积”
158 158     printf("%d\n", sizeof((**pp)));    //打印出数组“维积”,为4
159 159     printf("%d\n", sizeof(*p));  //打印数组的“维积”,为4*sizeof(int)=16
160 160     printf("%d\n", sizeof(p));  //打印出指针“维积”,为3*4*4=48
161 161     printf("%d\n", sizeof(*(**p[3][4])));  //打印出指针“维积”,为5*4=20
162 162 
163 163     18)关于指针和数组的一种特殊数据结构
164 164     int (**p[3][4])[2][5];  //定义一个二级3维指针2维数组
165 165     int pp[5][6];
166 166     int test;
167 167     //p=&test;   //因为p是数组名,是一个常量,所以p=&test;这句有错误
168 168 
169 169     18)int array[10];
170 170        double *a=&array[5];
171 171        double *b=array;
172 172        printf("a-b=%d\n",a-b);  //打印结果为:2。   4*5/8=2    此行代码是这样理解的: a-b得到一个double类型的地址值,然后转换成int类型,那么有20/8=2
173 173     
174 174 ------------------------------------------*/
175 175 #include"stdio.h"
176 176 #include"stdlib.h"
177 177 
178 178 
179 179 void main()
180 180 {
181 181 /*
182 182     int i;
183 183     int a[10]={[4]=4,5,6,7,[1]=1,2,3,110};    //数组的初始化技巧
184 184     for (i=0;i<10 ;++i )
185 185     {
186 186         printf("%d\n",a[i]);
187 187     }
188 188 */
189 189 /*
190 190     int abc[4]={1,2,3,4};
191 191     int i=0;
192 192     int (*p)[4];
193 193     p=&abc;  //p=abc;等效于p=&abc;
194 194     printf("%x,%x\n",&abc,abc);
195 195     for(i=0;i<4;++i)
196 196     {
197 197         printf("%d\n",p[0][i]);
198 198     }
199 199 */
200 200 /*
201 201     int Array[2][3][2]={{{1,2},{3,4},{5,6}},{{7,8},{9,10},{11,12}}};  
202 202     printf("%d\n",*(*(*(Array+1)))); 
203 203     printf("%d\n",*(*(Array+1))); 
204 204     printf("%d\n",*(Array+1)); 
205 205     printf("%d\n",(Array+1)); 
206 206     //printf("%d\n",*(*(Array+1)));
207 207 */
208 208 /*
209 209     int *p; 
210 210     int c[]={0x1234,0x5678};
211 211     int a=4;
212 212     int *b=&a;
213 213     p=(int *)malloc(4);
214 214     //printf("%p %d\n",b,*b);
215 215     //printf("%p %d\n",c,*c);
216 216     //printf("%p %d\n",(c+1),*(c+1));
217 217     //printf("%x",*p);
218 218     *p=0x12345678;
219 219     printf("%p\n",p);
220 220     printf("%x\n",*p);
221 221     free(p);
222 222     printf("%p\n",p);
223 223     printf("%x\n",*p);
224 224     b++;
225 225     printf("%p %d\n",b,*b);
226 226     b++;
227 227     printf("%p %d\n",b,*b);
228 228     b++;
229 229     printf("%p %d\n",b,*b);
230 230     b++;
231 231 */          
232 232 /*
233 233     char d[6] = {0x01,0x02,0x03,0x04,0x05,0x06};
234 234     char (*p)[2];   
235 235     p = d;  
236 236     printf("%p\n", (p));  
237 237     printf("%x\n", *(p)); 
238 238     printf("%p\n", *(*(p))); 
239 239     printf("%p\n", (p+1));
240 240     printf("%x\n", *(p+1)); 
241 241     printf("%p\n", *(*(p+1)));
242 242 */
243 243 /*
244 244     //-------------一级和多级指针的使用----------//
245 245     int val=0x1122;
246 246     char *p3=&val;
247 247     char **p2=&p3;
248 248 
249 249     printf("%x\n", p3); 
250 250     printf("%x\n", p3+1); //跳转1个地址(因为p3是个一级指针而且类型修饰符为char)
251 251 
252 252     printf("%x\n", *(p3)); //操作1个字节(因为p3是个一级指针而且类型修饰符为char)
253 253     printf("%x\n", *(p3+1));//操作1个字节(因为p3是个一级指针而且类型修饰符为char)
254 254 
255 255     printf("%x\n", (p2)); 
256 256     printf("%x\n", (p2+1)); //跳转4个地址(因为内存中字节所使用的地址长度为32位且指针p2是一个二级指针)
257 257 
258 258     printf("%x\n", *(p2)); //操作4个字节(因为内存中字节所使用的地址长度为32位且指针p2是一个二级指针)
259 259     //------------------------------------------//
260 260 */
261 261 /*
262 262     //-------------对多维指针的方括号运算以及多次解引用----------//
263 263     printf("%d\n", Array); 
264 264     printf("%d\n", Array[1]);   
265 265     printf("%d\n", Array[1][1]);    
266 266 
267 267     printf("%d\n",*(Array[1][1]));
268 268 
269 269     printf("%d\n",*(*(Array[1])));    
270 270     printf("%d\n",*(Array[1]));        
271 271     //-----------------------------------------------------------//
272 272 */
273 273 /*
274 274     //----------------------对多级多维指针的探究-----------------//
275 275     //假设已经掌握对多级0维指针,和一级多维指针的使用
276 116 
277 117     unsigned char (**p)[3];  //这是一个二级2维指针
278 118     int Array3[10]={0x804a0e0,0x55667788,2,3,4,5,6,7,8,9};  //之所以第一个元素设为0x804a0e0,是因为如果该值取的不当,下面对(*(*p))进行解引用的时候,有可能在程序执行时导致内核塌陷,看起来好像是程序语法错误,也就是说要保证解引用的指针是在正确范围内的
279 119     p=Array3;  //Array3是一个一级1维地址,先让Array3转变为二级2维地址,再让二级2维指针p所指向
280 120 
281 121     printf("%x\n", p);  //原本p是一个二级2维指针,在这里p表示为一个二级2维地址
282 122     printf("%x\n", p+1);  //发生4个地址的跳转(因为地址长度为4个字节),因为(p+1)是一个二级2维地址,也就是此行语句输出值比上一行语句大4
283 123 
284 124     printf("%x\n", *p);   //解引用得到4个字节的值,因为p是一个二级2维地址。*p 得到的是一个一级2维地址
285 125     printf("%x\n", (*p+1));  //跳转3个字节,因为 *p 属于一级2维地址,也就是此行语句输出值比上一行语句大3。因为一级指针地址的跳转,是取决于维数,*p是一个一级2维地址,那么跳转数为:sizeof(unsigned char)*[3]=3
286 126     
287 127     printf("%x\n", *(*p));  //对一级2维地址(*p)进行解引用,得到一级1维地址*(*p)
288 128 
289 129     printf("%d\n", *(*(*p))); //对一级1维地址*(*p)进行解引用,得到1个字节的值,值为0xe0也就是224,因为指针 p 的类型修饰符为char,而sizeof(unsigned char)=1
290 130 
291 131     printf("%d\n", *(*(*p))+1); //该行打印值比上一行大1,为225
292 132 
293 133     //总结:对多级多维指针进行解引用的时,每次解引用都会遵循先降级再降维,当级数没降到1,那么维数不起作用。
294 134     //      当已经是一级指针了后,维数起作用,当做一级多维指针或一级1维指针处理  ,一级1维地址再解引用就得到值了
295 295 */
296 296 /*
297 297     int (**p[3][4])[2][5];  //定义一个二级2维指针2维数组
298 298     int pp[5][6];
299 299     int test;
300 300     //p=&test;   //因为p是数组名,是一个常量,所以p=&test;这句有错误
301 301 
302 302     //下面证明了,解引用符号“ * ”在不同的场合应用,其作用发生了改变,如在sizeof()函数的参数中时,对数组名进行解引用,将会影打印出来的响数组“维积”
303 303     printf("%d\n", sizeof((**pp)));    //打印出数组“维积”,为4
304 304     printf("%d\n", sizeof(*p));  //打印数组的“维积”,为4*sizeof(int)=16
305 305     printf("%d\n", sizeof(p));  //打印出指针“维积”,为3*4*4=48
306 306     printf("%d\n", sizeof(*(**p[3][4])));  //打印出指针“维积”,为5*4=20
307 307 */
308 308 /*
309 309     int a =3;  
310 310     int b = 1;  
311 311     int c = 2;  
312 312     int const *p1 = &b;//const 在前,定义为常量指针  
313 313     int *const p2 = &c;//*在前,定义为指针常量
314 314     //常量指针p1:指向的地址可以变,但内容不可以重新赋值,内容的改变只能通过修改地址指向后变换。   
315 315     //p1 = &a是正确的,但 *p1 = a是错误的。
316 316     //指针常量p2:指向的地址不可以重新赋值,但内容可以改变,必须初始化,地址跟随一生。
317 317     //p2= &a是错误的,而*p2 = a 是正确的。
318 318 */
319 319 
320 320     int array[10];
321 321     double *a=&array[5];
322 322     double *b=array;
323 323     printf("a-b=%d\n",a-b);  //打印结果为:2。   4*5/8=2    此行代码是这样理解的: a-b得到一个double类型的地址值,然后转换成int类型,那么有20/8=2
324 324 
325 325 }

 

这是我对指针学习的归纳记载,如有疑问欢迎联系作者本人一起探讨~

尊重作者的劳动,转载请记得注明来源:http://www.cnblogs.com/weifeng727/p/5584151.html

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:计蒜客_计数和数数(C语言实现)

下一篇:贪吃蛇—C—基于easyx图形库(下):从画图程序到贪吃蛇【自带穿墙