AutoLeaders控制组——(C语言)指针 10.19日笔记---------------------------------------邱文硕
序言————————————————笔记来源于《C语言程序设计·在线实践·微课视频》、凯翁C语言教程及自我总结,如有不足,请见谅。
说明:ALN表示auto leaders note即“要点” ALQ表示auto leaders question即“问题”
1.指针的基本概念及指针变量的使用与运算
-
指针,顾名思义呢,就是具有指向作用的针,而在C语言中,通常指的是指针变量,有它自己的用途。指针变量:指针是一个变量的地址,而指针变量是一个专门用来另一个变量地址的变量。通常将指针类型的数据(即指针常量,指针变量的值)简称为指针,说白了,就是知道别人(即某个其他变量)的家在哪,可以迅速把它叫过来的甚至替身的工具。
-
怎样定义指针变量呢
语法 示例 说明 类型名*指针变量名 int * pi; float * pf; char * pc; (1)符号 * 用来区别指针变量和普通变量。(2)指针变量在使用前必须先赋值。(3)指针变量的值是一个内存地址。 ALN:a. 一个指针变量必须要有类型;
b.一个指针变量被指定某类型后,只能指向同类型的变量,如表中pf只能指向float类型的变量。
-
指针变量的赋值:对指针变量赋值后,指针变量就指向确定的内存存储单元。
int a=5; int *pi; //pi为指向int型的指针变量,简称int型指针变量 pi=&a; //将a的地址存入指针变量pi中。
-
指针变量的基本运算
(1)取地址运算
(2)间接访问运算
printf("%d\n",*pi); //该语句的作用是输出指针变量pi所指向变量的值
-
指针变量的引用
#include <stdio.h> int main{ int a; int *pa; scanf("%d",&a); pa=&a; *pa=*pa+5; printf("%x\n %x\n %d\n",&a,pa,*pa); return 0; }
运行结果
4 19ff2c 19ff2c 9
指针变量pa指向变量a的值,指针变量pa就是变量a的地址(&a),而指针变量pa指向的储存单元就是变量a的值,也可以用*pa来表示,见图7-6[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QBZ86ayD-1666505903129)(C:/Users/86176/Desktop/CamScanner%2010-23-2022%2013.52/1.jpg)]
2.指针与数组的联系.
-
一维数组的指针
为了使指针变量p指向数组a,需要将数组a的首地址赋给指针变量p,或者把数组a的首元素的地址赋给指针变量p
表7-2 使指针变量p指向一维数组a首地址的方法
序号 语句 说明 1 int a[5],*p;p=a; 把数组a的首地址赋给指针变量p 2 int a[5],*p;p=&a[0]; 把数组a的首元素的地址赋给p 3 int a[5],*p=a; 用初始化的方式,把数组a的首地址赋给p 4 int a[5],*p=&a[0]; 用初始化的方式把数组a的首元素a[0]的地址赋给指针变量p ALN:数组名表示数组的首地址,语句“p=a”表示将数组a的首地址赋给指针变量p,而不是将数组各元素的值赋给指针变量p。
-
引用数组元素时的指针运算
指针的加减运算与整数数据类型的加减操作意义不同,以加法为例如
int *q,a[5]={10,20,30,40,50},*p;
p=&a[0]; q=&a[1];
ALN:执行p+1并不是简单的将p的值加数字1,而且p是指针变量,不能进行相关运算,而是加上一个数组元素所占用的字节数。
如果a数组的元素类型为int型,则p+1表示使p的值加四个bit,使它指向下一个元素;同样的,当数组元素类型为char类型时,p+1表示使p的值加一个bit。
*ALL:请辨析*p+1与 (p+1)的区别
**答:*p表示指针变量所指代的类型的值, p+1表示该类型的值加一,而 (p+1)表示让p指针所指的地址移动,之后再取值。
ALN:指针也能自加p++或++p和自减p–或–p;以及比较运算p<q;两个指针相减p-q(ALN:需要满足p大于q,结果是“两个地址之差除以数组元素所占的字节长度”,以此可知两个指针变量的相对距离);
#include <stdio.h> int main () { int a[5],*p,i; p=&a[0]; for(i=0;i<5;i++) scanf("%d",p+i); for(i=0;i<5;i++) printf("%d ",*(p+i)); return 0; }
输入; 1 2 3 4 5 输出; 1 2 3 4 5
ALN:实际上,C语言在编译时,对数组元素a[i]就是按照(a+i)处理的,首先计算数组首元素的地址加上位移量,得到a[i]的地址,然后按照指针的方式访问储存单元的内容。可见[ ]实际上是变址运算符
综合上述说明可知下表为i的数组可以用以下几种方法表示如表7-3
-
指针的指针(多级指针)与指针数组
(1)多级指针
指针记录着目标数据对象的首地址和类型,那么能不能有一个指针记录着一个指针的首地址和类型呢?我们对一个指针再次取地址看看能不能通过。
#include <stdio.h> int main() { int n=123; int *pn=&n; printf("pn=%u\n",pn); printf("&pn=%u\n",&pn); return 0; }
最终输出pn=14678860
&pn=14678856,可见对pn取地址确实还可以获得一个指针。
那么,这个指针是什么类型呢?再加一个星号就可以了。int*的指针类型为int **。
int ** 是一个 int *类型数据对象的指针,所以又被称为二级指针,和普通指针一样都是用 *来获取目标数据对象的。
(2)指针数组
int arr1[5]={1,2,3,4,5}; int arr2[5]={5,4,3,2,1}; int arr3[5]={1,2,3,4,5}; int *pToArr[3]; pToArr[0]=arr1; //arr1转换为首元素指针,int[5]到int* pToArr[1]=arr2; //arr2转换为首元素指针,int[5]到int* pToArr[2]=arr3; //arr3转换为首元素指针,int[5]到int*
pToArr[0]的类型为int*,指向arr1的第一个元素
pToArr[0]的类型为int*,指向arr2的第一个元素
pToArr[0]的类型为int*,指向arr3的第一个元素
pToArr的类型为int*[3],是一个数组。它有3个元素,每个元素的类型为int *,它被称为指针数组。接着,再写入以下代码。
for (int i=0;i<3;i++) { int **pToArr + i; for(int j=0;j<5;j++); printf("%d",*(*p+j)); printf("\n"); }
1 2 3 4 5
5 4 3 2 1
1 2 3 4 5
pToArr是一个int *[3]类型的数组,若出现在表达式p=pToArr+i中,即转换为指向首元素的指针,即int *[3]转为int **。
ALN:数组指针与指针数组的辨析,如( )a[10]为数组指针(指向数组的指针),而指针数组(由指针构成的数组)如 a * [10],主要看有没有括号将星号包括,有的话就是一个指针,否则即数组 这里主要涉及到符号的优先级,数组[]的优先级高于*而低于(),所以上述情况就可以合理解释了
补充:标识符id, 如int ( * id)[4],1.优先计算()内的指针,所以id是一个指针,2.接着计算数组[],上一步的指针指向一个数组,3.现在轮到指针 * 了,上一步的数组元素为指针,4.数组元素指向int。id标识符的类型为指针数组指针,它指向一个int*[4] 的数组,而这个数组中的元素也为指针。
函数指针数组:int ( * func[10])(char*,double);声明中出现两对括号,( *func[10])为单纯的括号,优先级最高,而(char *,double)是函数声明的括号。
3.指针与字符串
在C语言中,有两种操作字符串的方法
(1).使用字符数组。
(2).使用字符型指针变量。
-
字符指针
可以不定义数组而定义一个字符类型的指针变量,并将字符串的起始地址赋给指针变量,通过指针变量实现字符串的操作。
-
字符数组与字符指针变量的比较
(1)储存单元的个数及内容不同。系统根据字符数组的长度为其分配若干个存储单元,每个单元存放一个字符。而对一个字符指针变量,只分配一个存储单元(占4B),这个单元可以存放地址。
(2)赋值方式不同。可以对字符指针赋值而不能对数组名赋值。
(3)初始化的含义不同
char *a; a="Hello C"; //正确 char s[10]; s="hello C"; //错误
(4)指针变量的值能改变,而数组名代表地址常量,不能改变。
(5)字符数组中各元素的值是可以改变的,但字符指针变量指向的字符串常量中的内容不可改变。
-
关于指针处理字符串的几个基本应用
- 字符串的复制
#include <stdio.h> int main() { char str1[10],str2[10]; char *p1,*p2; p1=str1; p2=str2; printf("请输入原字符串:\n"); gets(p2); for(;*p2!='\0';p1++,p2++) //循环复制str2中的字符到str1 *p1=*p2; *p1='\0'; //str1结尾补\0 printf("复制之后的字符串为:%s\n",str1); return 0; }
-
字符串的连接
#include <stdio.h> int main () { char str1[10],str2[10],str3[20]; char *p1,*p2,*p; int i=0; p1=str1; p2=str2; p=str; printf("请输入字符串1:\n"); gets(p1); printf("请输入字符串2;\n"); gets(p2); while(*p1!=0) //复制str1到str* { *p=*p1; p++; p1++; i++; } for(;*p2!='\0';p1++,p2++,p++) //复制str2到str* *p=*p2; *p='\0'; //*str结尾补\0 printf("连接之后为:%s\n",str); return 0; }
4.指针与函数(偏向于指针的应用性)
-
函数的参数不仅可以是整型,浮点型,字符型,还可以是指针类型。
用指针作为函数的参数时,实参是指针表达式、即指针(地址)、指针变量或指针表达式,而形参是指针变量,因为只有指针变量才能接收指针。指针作函数参数时,实参最终传递给形参的是地址信息,这种函数调用一般可称为“传址调用”。
**意义所在**:用指针作为函数参数,将实参的指针表达式的值传递给形参的指针变量,这样,形参指针变量所指向的存储单元与实参指针表达式所指向的的存储单元相同,因此,在被调用函数中,通过形参指针变量可以间接访问实参指针表达式所指向的存储单元。当函数调用结束时虽然指针变量被撤销了,但是之前对实参指针表达式所指向的存储单元的修改并不会被撤销。这说明,通过使用指针作函数参数,可以间接实现在被调函数中对主调函数中的变量值进行修改。
ALN:据此,我们可以想到,之前scanf函数为什么在对变量赋值时,需要使用到“&”,这个符号就是获取变量的指针,通过指针实现变量值的修改。
这也是C语言与其他更为简单的语言的一个显著点的区别,所以一些学过其他语言的人在学习C语言时,因为第一次接触会认为很难,其实指针并不是想象中的那么难。(个人观点>_<)
-
变量的指针作函数的参数
给两数排序:输入两个整数,按照升序后输出。要求编写自定义函数,在主函数中调用并验证。
#include <stdio.h> void sort(int *p1,int *p2); //对sort()函数进行声明 int main () { int x,y; scanf("%d %d",&x,&y); sort(&x,&y); //调用sort()函数,实参为变量的指针 printf("%d,%d\n",x,y); return 0; } void sort(int *p1,int *p2) //sort()函数的定义,形参为指针变量 { int t; if(*p1>*p2) {t=*p1;*p1=*p2;*p2=t;} }
上述过程可以用图7-24 来生动解释
-
一维数组的指针作函数参数
编写函数,求长度为n的一维数组中的最大值。在主函数中调用并验证
#include <stdio.h> int max(int *p,int len); { int i,max; max=p[0]; for(i=0;i<len;i++) if(max<p[i]) max=p[i]; return max; } int main () { int x[5],maxnum,i; for(i=0;i<10;i++) scanf("%d",&x[i]); maxnum=max(x,10); printf("Max number=%d\n",maxnum); return 0; }
运行结果
10 9 8 7 6 Max number=78
-
指向函数的指针
定义一个函数,其存在一个存储空间,这个存储空间的起始地址称为函数的指针,函数名代表了存储空间的首地址。
语法 示例 说明 类型名(*指针变量名)(形参表); int (*p)(int); p两侧的括号表示p先于结合,是指针变量,然后在于后面的()结合,()表示是函数,及该指针变量指向的函数 定义两个函数,一个用来求两个整数中的最大值,另一个求最小值
#include <stdio.h>
int max(int a,int b)
{
int t;
if(a>b) t=a;
else t=b;
return t;
}
int min(int a,int b)
{
int t;
if(a<b) t=a;
else t=b;
teturn t;
}
int main()
{
int (*p )(int,int); //定义指向函数的指针变量p
int x,y,z;
scanf("%d %d ",&x,&y);
p=max; //使p指向max()函数
z=(*p)(x,y); //通过指针变量p调用max()函数,等价于max(x,y)
printf("Max number=%d\n",z);
z=(*p)(x,y); //通过函数名调用min函数
printf("Min number=%d\n",z);
return 0;
}
int main()
{
int (*p )(int,int); //定义指向函数的指针变量p
int x,y,z;
scanf("%d %d ",&x,&y);
p=max; //使p指向max()函数
z=(*p)(x,y); //通过指针变量p调用max()函数,等价于max(x,y)
printf(“Max number=%d\n”,z);
z=(*p)(x,y); //通过函数名调用min函数
printf(“Min number=%d\n”,z);
return 0;
}