指针详解(C语言)

一、指针指向基本类型变量

  在C语言中,基本类型由char, short, int, long, float, double这六个基本类型组成,注意数组类型不是基本类型。基本类型指针的用法,比如 char *p = 'A'; ,这在编译过程中,不会报错。而在运行之后会出现异常。原因是指针类型只能指向地址,而不是一个常量,字符'A'是一个常量。修改以上代码如下 char ch = 'A', *p = &ch; 。先定义一个字符类型变量ch,赋值为'A',然后用指针指向ch的地址,这样就可以直接用*p打印字符'A'。

复制代码
1 char ch = 'A', *p = &ch;
2 char *p2 = "大哥哥";    // 字符串可以直接赋值,因为字符串在常量区申请了一块内存空间
3  
4 printf("%c", *p);    // A
5 printf("%s", p2);    // 大哥哥

 我们也可以通过对指针p来对ch进行修改

复制代码
1 *p = 'B';
2 printf("%c\n", ch);    // B

二、字符指针和字符数组的区别

  字符指针和字符数组大致上操作相同。字符数组可以通过下标来取值,字符指针可以通过自增或者地址+n来跳到指定的位置,然后通过*号运算符取这个位置上的某个字符。字符指针和字符数组都可以通过指向一个字符串来完成初始化,但是字符数组只能在初始化的时候有机会指向字符串,在字符数组已经被定义的情况下就不能再次直接用=来完成赋值了,要用strcpy()函数来完成赋值操作。
复制代码
 1 #include <stdio.h>
 2 #include <string.h>
 3 
 4 int main()
 5 {
 6     char *p = "字符指针";
 7     char arr[] = "字符数组";    // 字符指针和字符数组初始化
 8     p = "字符指针 -- 2";    // 字符指针可以在定义之后重新指向另一个字符串
 9     
10     // arr = "大哥哥";    // 修改失败
11     strcpy(arr, "字符数组 -- 2");    // 修改成功,但是却导致了字符指针p值为空了
12 
13     printf("%d\n", *p == NULL);    // 1
14 
15     printf("%s\n", p);    // ""
16     printf("%s\n", arr);    // "字符数组 -- 2"
17 
18     return 0;
19 }
注意,字符指针p并不是修改上一个字符串的值,而是重新指向了另一个字符串,因为字符串是常量,无法修改。
在上面代码中,strcpy()函数成功修改了字符数组arr的值,但是却莫名其妙的导致了指针p指向了NULL。如果有小伙伴知道这是什么情况请留言告诉我,谢谢。
 
— — — — — — — — — — — — — — — — 修复了以上代码:11行的问题 — — — — — — — — — — — — — — — — 
 
因为我定义数组arr时,没有指定其长度。数组的长度默认为当前字符串的长度。当下次为这个数组重新赋值时,要赋值的字符串大于这个数组所能承受的长度之后,导致了这个异常。。
在第7行代码处,修改arr的定义 char arr[255] = "字符数组"; ,为其指定长度为255。

三、指针作为函数参数 

   指针作为参数,可以通过传入变量的地址或者指针所指向的地址来在形参上获取到实际传入的引用,对形参的操作均等价于对实际传入变量的操作。
复制代码
 1 #include <stdio.h>
 2 
 3 // 交换两个变量的值
 4 void swap(int *x, int *y)
 5 {
 6     int temp;
 7     temp = *x;
 8     *x = *y;
 9     *y = temp;
10 }
11 
12 int main()
13 {
14     int a = 10, b = 5;
15     int *pa = &a, *pb = &b;
16 
17     // swap(&a, &b);    // 第一种方式
18     swap(pa, pb);    // 第二种方式
19     printf("a=%d\tb=%d\n", a, b);  // a=5    b=10
20 }    

swap()函数需要传入两个变量的地址,并交换两个变量的值。在main函数中,定义了两个变量,a和b,它们值分别为10和5。通过第一种方式,我们需要将a和b的地址作为实参传入swap()方法中,来实现a和b的值交换。第二种方法,通过指针pa和pb指向了a和b,并将pa和pb传入swap函数中。

指针pa相当于&a,pa是指向变量a的地址。*pa是a的值(10),&pa表示指针自己本身的地址,可以通过二级指针来指向一级指针的地址来对一级指针所指向的变量进行操作。

四、一级指针和二级指针 

  一级指针可以用来指向变量的地址来对变量进行任意操作,那么二级指针就是来指向一级指针并对一级指针进行任意操作的。以此类推,三级指针,四级..。以下代码和上面的差不多,只是新增了二级指针对一级指针的操作 
复制代码
 1 #include <stdio.h>
 2 
 3 // 交换两个变量的值
 4 void swap(int *x, int *y)
 5 {
 6     int temp;
 7     temp = *x;
 8     *x = *y;
 9     *y = temp;
10 }
11 
12 int main()
13 {
14     int a = 10, b = 5;
15     int *pa = &a, *pb = &b;
16 
17     int **ppa = &pa, **ppb = &pb;    // 加了两个二级指针来指向pa和pb自己的地址
18     swap(*ppa, *ppb);    // *ppa和*ppb指向了pa和pb指向的变量a和b的地址
19 
20     printf("a=%d\tb=%d\n", a, b);
21     printf("%d\n", **ppa);    // 5
22 }

17行定义了两个二级指针ppa和ppb,分别指向了一级指针pa和pb的自身地址。注意pa和pb自身的地址和他们指向的地址不一样,指针在内存中也是有空间的,32位操作系统默认占4字节,64位8字节。如指针pa的地址用&pa表示,它所指向的地址直接用pa表示,它所指向地址的值用*pa表示。

二级指针的定义就是比一级指针多了个 * 号罢了。如ppa指向pa的地址,*ppa指向pa所指向的地址,**ppa表示pa指向地址的值,和*pa等价。多级指针用法不外乎和这一个道理。

我们也可以通过只传入二级指针指向的地址,而不是二级指针指向的值来完成变量的交换。修改swap函数形参为二级指针。
1 void swap(int **x, int **y)
2 {
3     int temp;
4     temp = **x;
5     **x = **y;
6     **y = temp;
7 }

在调用swap函数时,直接传入ppa和ppb就行了。如 swap(ppa, ppb); 。来完成对变量a和b值的交换。

五、数组指针和指针数组

数组指针是一个指向数组的指针,可以通过单个指针来完成对整个数组进行遍历。而指针数组则是一些指针组成的数组。
数组指针的用法如下:
 1 #include <stdio.h>
 2 
 3 int main()
 4 {
 5     int arr[5] = {8, 6, 4, 1, 4};    // 定义一个长度为5的int类型数组
 6     int *pArr = arr;
 7     int i;
 8 
 9     *pArr = 10;    // 将数组中第一个元素修改成10
10     *(pArr+2) = 13;    // 将数组中第3个元素修改成13
11 
12     for (i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
13     {
14         printf("%d\t", *(pArr+i));    // 10    6    13    1    4    
15     }
16         
17     return 0;
18 }
 
如果指向的是一个二维数组或者三维数组,在定义时则在指针后面跟上数组除了维度外的其它长度。例如:
复制代码
1 int arr[2][5] = {...};    // 赋值简写
2 int (*pArr)[5] = arr;    // 指向二维数组,这里必须用()将指针包起来,来定义数组指针。否则就变成指针数组了
3 
4 int arrArr[4][2][5] = {...};
5 int (*pArr)[2][5] = arrArr;    // 指向三维数组,除了第一个[]不用写

在定义数组指针时,有一个小坑。如果不将指针部分用 () 起来,那么定义的就不是数组指针了,而是指针数组。由于 [] 优先级比 * 高,则p会先和 [] 组成一队,然后与 * 相连,那么这将定义一个全是由指针组成的数组。

指针数组的用法:

 1 #include <stdio.h>
 2 
 3 int main()
 4 {
 5     int arr[5] = {8, 6, 4, 1, 4};    // 定义一个长度为5的int类型数组
 6     int *pArr[5]; 
 7     int i;
 8 
 9     for (i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
10     {
11         pArr[i] = &arr[i];    // 将数组中每个元素依次对应指针数组中的每个指针
12     }
13 
14     *pArr[0] = 10;    // 将数组中第一个元素修改成10
15     *pArr[2] = 13;    // 将数组中第3个元素修改成13
16 
17     for (i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
18     {
19     printf("%d\t", *pArr[i]);    // 10      6       13      1       4
20     }
21 
22     return 0;  
23 }
指针数组是一组指针的集合,数组中的每一个指针都可以指向一个变量。 

六、常量指针和指针常量


指针常量无法修改自己已经指向的地址,但可以修改所指向地址的值。

复制代码
1 int a,b;
2 int * const p=&a //指针常量
3 //那么分为一下两种操作
4 *p=9;//操作成功
5 p=&b;//操作错误
而常量指针无法修改自己所指向的地址的值,但可以修改自己所指向的地址。
复制代码
1 int a,b;
2  const int *p=&a //常量指针
3 //那么分为一下两种操作
4 *p=9;//操作错误
5 p=&b;//操作成功

指向常量的指针常量既无法修改自己指向的地址,也无法修改自己所指向地址的值。

复制代码
1 const int * const b = &a;//指向常量的指针常量

 

 
 
 
 
posted @ 2019-03-23 22:12  dagger9527  阅读(353)  评论(0编辑  收藏  举报