目录
1.本文主要内容
2.基础知识讲解
3.拓展延伸
4.实际应用
5.总结
0.前言
上一篇文章《部分经典排序算法详解(有图解)》中,
我们通过图解的方式,详解了三种经典的排序方法本文主要讲解C语言的最后一个内容,
也是被称为“C语言的灵魂”的内容——指针
1.本文主要内容
-
指针的基础知识
-
指针的一些注意事项
-
指针的实际应用
2.基础知识讲解
2.1 指针的基础概念
- 能记录其他变量的地址的一种工具,就叫做指针
(1)指针能解决哪些问题?
指针能解决跨区域问题
(即不同作用域、不同代码块之间的数据交互)(2)指针是如何解决这些问题的?
指针能够通过自身指向的地址,来操作该地址所存储的值
以此达到跨区域的数据交互问题
2.2指针的语法定义
2.2.1基本定义方法:
int *p;
其中,变量p即为一个整型指针变量
Int *的意思就是该指针只能指向一个整型变量,
即 该p指针变量的内存空间只能存储一个整型变量的地址
2.2.2基本使用方法:
int a =10;
int *p = &a;//p指向a的地址
printf("%d\n",*p);//输出p指向的地址的值
这里从第二行开始对一些重要的细节进行解释:
-
第二行的 “&” 表示取变量的地址,(&a表示变量a的地址)
由于指针变量存放的是其他变量的地址,因此需要在该变量前加& -
第二行与第三行均有一个星号 * ,但这两行的星号* 意思并不相同
第二行的星号表示变量p是指针
第三行的星号表示取变量p所指向地址中存储的值(即a的值
)
总结:
“&” 表示取变量的地址
指针变量中使用星号* 表示变量p是指针
其他情况下指针变量前加星号* 表示取变量p所指向地址中存储的值
2.2.3指针与数组:
指针与数组有着不少联系
可以使用一个指针指向数组的首地址,然后通过指针来控制、访问数组:
int *p;
int a[20] = {};
p = a;
printf("%d",p[1]);//输出数组a的第二个元素
printf("%d",p+1);//输出数组a的第二个元素
我们来分析一下细节部分:
因为数组a的变量名为a,该变量名所代表的就是该数组的首地址,因此可以把a赋给p,意思就是将数组a的首地址赋给指针p
当p指向数组a之后,p[i]则可以用来表示a[i](效果相同)
由于是int型指针变量,设数组a的首地址为0x100
则p+1的含义是0x100 +1*4 = 0x104,实际上 p+1与p[1]同效果
3.拓展延伸
3.1关于指针的一些研究
3.1.1语法相关注意事项
看看是否能区分一下的语句的含义:
printf("%p",score);输出的是第一个元素的地址
printf("%p",score+1);输出的是第二个元素的地址
printf("%d",*(score+1) );输出的是第二个元素的值
printf("%d",*score +1 );输出的是第一个元素的值+1
printf("%d",*score++);输出的是?
注意:
- *score++相当于( *score)++,星号优先级更高
*(score++)则是先地址++,再与星号结合
因此printf("%d", *score++);输出的是 *score,
语句结束后 *score会+1
再看看一下语句:
char * const p
char const *p
const char *p
总结:
char * const p
p是一个char型指针,const修饰的是p,p不允许改变
即该指针p是常量,不允许修改char const *p 与 const char *p
p是同一种char型指针,const修饰的是char
即 指针指向的字符被看作是常量,不允许修改
3.1.2其他注意事项
(1) 指针必须要指向具体的存储空间,才能赋具体的(非地址)值
int *p;
*p = 10; //会报错
由于p并未指向一个具体的内存空间,使用*p并对其赋值肯定会报错
解决办法:
①将其他值的地址赋给p,就可以使用*p了
②使用malloc()与realloc()自行申请内存空间
(2)使用malloc()与realloc()申请内存空间方法
语法:
int* p;
p = (int*)malloc(10 * sizeof(int));
p = (int*)realloc(p,20*sizeof(int));
第二句语法可以分配10个连续的大小为4个字节的空间
(64位中,整型空间为4个字节)第三句可以为p重新分配一个大小为20个4个字节的空间
其中realloc中的p就是malloc的接收对象使用realloc的前提是,必须先用malloc开辟一段空间
4.实际应用
4.1让函数改变多个参数
一般情况下,我们都会通过返回值来把函数中修改后的值返回出来
由于返回值只能返回一个值
如果需要同时修改多个参数,返回值就无法满足我们
void add(int a,int b){
a++;
b++
}
void main(){
int m=0,n=0;
add(m,n);
}
我们都知道,上面这种情况下,m与n的值最终是不会变的
- 原因是:
调用add()函数时,m与n将它们的值传给形参a、b,
而形参的操作并不会影响实参m、n,故最后m、n的值并不会改变
想要解决这个问题,就需要使用指针:
void add(int *a,int *b){
*a++;
*b++
}
void main(){
int m=0,n=0;
int *p1 = &m;
int *p2 = &n;
add(p1,p2);
}
- 使用指针可以解决问题的原理是:
实参传给形参的是需要操作的变量的地址,
而形参作为指针,会通过地址去操作变量,
以此来操控我们的目标变量的值
4.2定义链表的结构
链表相当于是通过指针来连接的
typedef struct Node{
int data;
struct Node* next;
}Node;
链表中存在一个个的节点,这些节点都包括一个存放数据的数据域,
以及存放下一个节点的地址的指针变量next
链表的优缺点:
- 优点:增加数据、删除数据十分方便,
只需要改变指针的指向即可完成- 缺点:查找不方便,每次查找往往都伴随着大量遍历
数组的优缺点:
- 优点:访问某个特定位置的元素十分方便
- 缺点:增加、删除元素极为麻烦
5.总结
(1)本文讲解了有关指针的基础知识、使用方法以及注意事项,希望各位读者能从中有所收获
(2)指针是一个十分灵活的工具,关键就是:
要理解为何要使用指针,以及指针能做些什么
其实明白了这里两点后,就能基本知道指针该如何使用了(指概念理论方面),剩下只要多练习就可以完全把握指针了
(3)指针看起来很难,但是只要明白其本质,并多多练习,就可以掌握,这里尤其是推荐:多编写一些与链表有关的项目 ,笔者觉得对于新手来说,练习链表的使用,是熟悉指针的最好方法之一。
网友评论