笔记整理8: 指针
到指针了呀,C语言的精华之所在。正确的使用指针,可以有效地表示复杂的数据结构、动态地分配内存、方便地使用字符串、有效而方便地使用数组及直接处理内存地址等。(辨别一个人是新手还是老手的第二步就是看他能否灵活地使用指针)
指针
高级语言中的变量具有三个属性:变量的名、变量的值和变量的地址。在访问一个变量时,我们有两种途径,即直接访问和间接访问。
1,直接访问
通过变量名a直接访问变量名a内的数据。
2,间接访问
将变量a的地址存放在另一个变量b中,通过访问变量b,间接达到访问变量a的目的。
如何达到间接访问呢,这里就需要采用的指针了。变量b也就是所谓的指针变量,所谓的指针变量就是保存其他变量地址的变量。
1, 指针变量的定义
类型*指针变量名
如 int *a;
这里的a就为指针变量了(非*a),在此之前我们需要了解两个指针运算符。
- ( & ):取地址运数符
- ( * ) :指针内容运数符
那么我们在取一个变量地址时,就可以用定义后的a=&b;
表示,想要表示b的值时,就可以用b=*a
表示(前面为直接访问,后面为间接访问)
注意:
1,定义完指针变量后,应习惯性的使a=NULL
。NULL表示空指针。(指向空,是没有分配空间的)
2,定义的类型后是指针变量名,并非表达式。
3,指针取地址不能跨界(这里我将其描述为一维指针),即一维指针不能指向指针。
4,register定义的变量不能被取地址,因为其被定义于cpu内了,而非于内存内。
5,对a赋值,改变a的指向。对*a赋值,改变a所指向的值。
6,()
的优先级大于*
。
沿用老师的话,指针第一定律
p指向a,则*p就是a
2, 指针与函数
- 指针作为函数的参数
变量的地址在调用函数时作为实参,被调用的函数使用指针变量作为形参接收传递的地址。(这种常会出一些坑人的题目)
如:
#include <stdio.h>
point( char *p ) //自己先做一下,然后往右划
{
p+=3;
*p+=3;
}
main( )
{
char b[4]= {'a', 'x', 'y', 'w'}, *p=b; //答案是a
point( p );
printf( "%c", *p );
}
- 函数返回指针
数据类型*函数名(参数列表)
{
......
}
这个在函数里面曾提到过,不一样的地方是这里返回的是一个该数据类型的指针,所以数据类型后加了一个*号。
- 指向函数的指针
类型标识符(*指针变量名)()
我现在觉得这个完全可以被函数返回指针替代,并没有多大的使用价值。
3, 指针与数组
在使用指针时,其中最好玩的就是它与数组的结合了,用指针表示数组是真的很方便喔。
- 使用方法
在一维数组中我们假设定义了一个int a[3];
,那我们就得到了a[0],a[1],a[2]。这三个变量,而其中a就是a[0]的地址(二维数组类似),那么我们再定义一个指针int*p;
后,就可以用p=a;
来表示指针p指向了a这个数组,且指向的是第一个变量a[0]。定义完后我们需要注意一下几点。
1,p指向了a[0],那么p+1就指向了a[1]。
2,*(p+1)=*(a+1)
p=&a[0]
3,指针变量加下标后即代表一个数,如p[1]=a[1]
。
4,p是变量,a是常量。
沿用老师的话,指针第二定律
p[i]==*(p+i)==*(a+i)==a[i]
- 指针的基本运算
这里我们需要知道指针的基本运算是针对两个指针指向同类型的数组使用的。同时我们也只需要了解一下减法运算方可。
#include<stdio.h>
int main( )
{
char a[ ]={"The sky is blue.\n"};
char *p=a;
while(*p) p++;
printf ("%d\n", p-a);
}
减法运算最常用于计算一个字符串的串长,但我觉得挺鸡助的,因为明明一个strlen可以搞定的事嘛。
4, 字符指针
字符指针可以指向一个字符串,其存放的是一个字符串常量的首地址。这里我们需要将其与字符数组区分开。
字符指针 char *str="bad guy";
字符数组 char string[]="bad guy";
注意:
1,str是一个变量,可以改变str的指向使它指向不同的字符串,但不能改变str所指向的字符串常量。(该字符串于堆内)
2,string是一个数组,可以改变数组中的每一个变量。
5, 指针数组
类型*数组名[常量表达式]
int*p[5];
一个数组中的每个元素均为指针类型,即由指针变量构成的数组,这种数组称之为指针数组,即指针的集合。那么这个数组中的每一个指针都可以指向一个字符型数据(字符串)。好多个字符指针呀。
指针数组实用性最强的地方是作为main函数的参数出现,但老师并未在此作过多要求,我也不过多介绍了,感兴趣的可以自行查阅。
6, 数组指针(行指针)
类型(*数组名)[常量表达式]
int(*p)[5];
我们知道普通指针指向一个一维数组后我们可以用p[i]表示a[i],这个用起来也非常的爽快。可遇到二维数组后,我们再用普通指针的话,就无法用p[i][j]表示a[i][j]了,这便是跨界了。比较一下下面的两个代码。
main()
{
int a[2][3]={1,2,3,4,5,6};
int *p;
int i,j;
p=a;
for(i=0;i<6;i++)
printf("%d",*(p++));
}
main()
{
int a[2][3]={1,2,3,4,5,6};
int (*p)[3];
int i,j;
p=a;
for(i=0;i<2;i++)
for(j=0;j<3;j++)
printf("%d",p[i][j]);
}
我们可以发现针对二维数组,我们想达到p[i][j]==a[i][j]的效果,普通指针已经不管用了,所以我们这里就需要用到高级指针,其中之一即数组指针。那为何又叫它行指针呢,因为它这里的常量表达式的值需要和被指向的二维数组的列值保持一致。
7, 指向指针的指针(第二个高级指针)
char**p
或char*p[]
(第二种常用于子函数的形参中)
既然是指向指针的指针,那么我们就应该使它指向指针。那么该如何使用呢,我们经常在函数中使用它。例如下面这个(对程序名排序后输出)。
#include<stdio.h>
#include<string.h>
void fun1(char *p[],int a);
main()
{
char *p[5]={"you","are","a","good","boy"};
int i,j;
fun1(p,5);
for(i=0;i<5;i++)
printf("%s\n",p[i]);
}
void fun1(char *p[],int a) //这里的高级指针即指向了指针数组p
{
int i,j;
char*h;
for(i=0;i<a-1-i;i++)
for(j=0;j<a-1-i;j++)
if(strcmp(p[j],p[j+1])>0)
{
h=p[j];
p[j]=p[j+1];
p[j+1]=h;
}
}
而对于第一个,我并没有经常使用,因为对于普通变量,已经有普通指针了。对于二维数组,可以使用数组指针。对于函数,可以使用第二个。但它的用法可以举例说明一下,如下面的输出指针数组。
#include <stdio.h>
main()
{
char *a[3]={"one","two","three"};
char **p;
int i;
for(i=0;i<3;i++)
{
p=a+i;
printf("%s\n",*p);
}
}
其实可直接输出printf("%s\n",*(a+i));完事。
越来越临近了,下个星期三就要考C语言了,发完这篇后,我也需要将时间分配到其它科目了,但后续结构体等内容我还是会陆续整理的。在此也希望自己能考一个好成绩吧。觉得写的还不错的话,麻烦小手轻点一个赞喔。