c语言知识点温习-3-指针

1.关于指针的零碎知识点
可以这样理解指针,前面学过整型,浮点型,字符型这些数据类型。整型存放整数,浮点型存放小数,字符型存放字符,而指针存放变量的地址,可以通过这个地址间接访问变量。
指针属于无符号的整数,占4个字节。
定义:指针变量的类型就是它指向的变量的类型。所以指针变量的定义形式如下:<指针变量的类型><*变量名>,比如int *p;但定义指针变量时最好不要这样去定义:int *p;而是写成这样的形式:int *p=NULL。
指针变量本身也占有一定的空间,并且也有自己的内存地址。
指针的赋值:给指针变量赋值时可以分成两种情况:
情况一:指针变量给指针变量赋值:

int *p,*q;
p=q;

情况二:非指针变量给指针变量赋值:

int a;
int *p;
p=&a;

其中&是取地址符,用来获取变量的地址,&是单目运算符,结合方式自右向左,优先级仅次于() [] . ->这4个。
指针的运算:(算术运算和关系运算)
算术运算:(+,-,++,- -)
指针变量的加减,其实是以指针所指向的类型空间为单位进行偏移。比如char型(char类型占1个字节)的指针,每加减1偏移1个字节;int型(int型占4个字节)指针,每加减一偏移4个字节;double型(double型占8个字节)指针每加减一,偏移8个字节
关系运算:
直接举例说明,存在两个指针p,q是同一数据类型(前提)的如果这两者之间的关系是p>q,说明p所指向的元素存储位置在q所指向变量的存储位置之后。
2.指针访问数组元素
首先强调一下:数组名是这个数组的首地址,比如有一个二维数组a[3][3],那么数组名是a,也就是这个数组的首地址。
首地址:一段内存空间中第一个存储单元的地址。那么存储单元又是什么东西呢。这里先给出一个层级关系:元素->一维数组->二维数组->三维数组->…->n维数组。
就按照这个层级关系,对于一维数组,它的存储单元就是上一层级,即元素;对于二维数组,那么它的存储单元就是一维数组;如果是一个n维数组(n>1),它的存储单元就是n-1维数组。

int a[5];//定义一个一维数组,这个数组包含5个元素,分别是a[0],a[1],a[2],a[3],a[4]
         //这5个元素都是这个一维数组的存储单元,a[0]是第一个存储单元
         //a是数组名,是这个数组的首地址,首地址就是a[0]的地址,即&a[0]

int b[2][2];//再定义一个二维数组,这个二维数组包含两个一维数组b[0],b[1]
            //这两个一维数组都是这个二维数组的存储单元,b[0]是这个二维数组的第一个存储单元
            //b是数组名,是这个数组的首地址,首地址就是b[0]的地址

注意上面的概念是数组的首地址,而不是整个数组的地址,整个数组的地址应该是这样的形式:&数组名,比如有一个一维数组a[5],那么整个数组的地址就是&a。它的偏移单位是整个数组所有元素的字节数之和。不过我们取整个数组的地址意义不大,但取数组首地址作用很大。
通过首地址我们可以访问数组中的所有元素。先看一个例子(看不懂下面有解释):

int a[5]={1,2,3,4,5};
int *p=a;//p指向a[0]
for(int i=0;i<5;i++)
	printf("%d",*(p+i));

*是取值运算符(但代码第二行*p的*不是取值运算符,只是说明这是个指针变量),可以和&取址运算符对比记忆,*作用于指针,作用是访问存放在指针中的那个地址对应的元素。而&作用的对象是非指针变量,作用是获取该变量的地址。
看完了上面的代码,如果我们想要访问一维数组a[5]中的元素a[3],首地址是数组名a,首地址的定义是一段内存空间中第一个存储单元的地址,也就是a就是数组中第一个元素a[0]的地址(这里不懂去看上面的层级关系和首地址的定义),如果要获取下一个存储单元的地址,只需加1,即a+1。
而上面代码的指针变量p存放的就是首地址,所以我们访问第二个存储单元,也是+1。
另外,除了上面那个代码,也可以这样访问

int a[5]={1,2,3,4,5};
int *p=a;//p指向a[0]
for(int i=0;i<5;i++)
	printf("%d",*p++);

我们还可以这样做

int a[5]={1,2,3,4,5};
int *p=a;//p指向a[0]
for(int i=0;i<5;i++)
	printf("%d",*(a+i));

如果是访问二维数组b[2][2]中的元素呢?b是数组的首地址,也就是第一个一维数组b[0]的地址,如果获取第二个存储单元的地址也就是第二个一维数组的地址,也是只需加1,即b+1。但现在的存储单元是一维数组,我们要访问的是元素,但我们现在进行到了一维数组这一步,这就要借助上面一维数组访问元素的方法,不过这里一维数组的名字分别是b[0],b[1],而不是b,因为b是二维数组的名字。
比如访问二维数组b[0][1]这个元素可以用
法一:*(b[0]+1)
法二:*(*b+1)因为b是第一个存储单元(b[0])的地址,所以*b等价于b[0],也可以这样说b等价于&b[0]。
3.指针数组和数组指针(行指针)
指针数组:形如int *p[3];
1)其中常数3指出数组中元素的个数;int是类型说明符,指出每个元素指针的类型。
2)和普通数组一样,指针数组要占用一片连续的存储单元,数组名是这片存储单元的首地址。
3)指针数组与一维数组:

int a[]={1,0,0};
int b[]={0,1,0);
int c[]={0,0,1};
int *p[3]={a,b,c};

指针数组与二维数组:

#include <stdio.h>
#include<stdlib.h>
#define M 2
#define N 2
int main()
{
	char a[M][N]={'a','b','c','d'};
	char *p[M];
	for(int i=0;i<M;i++)
		p[i]=a[i];
    printf("%c",*(p[1]+1));//输出结果是:d
	return 0;
}

数组指针(行指针)
行指针变量:是由m个元素组成的一维数组的指针变量。如果定义了一个行指针p,则p的增值(偏移)是以一个一维数组的长度为单位。
行指针定义格式:<类型说明符>(*指针变量名)[常量表达式],形如int (*p)[4];其中“(*指针变量名)”两边的括号不能少,缺少了就是指针数组。
案例:求3名学生中随机一名学生的平均成绩

#include<stdio.h>
#define NUM_COURSE 4
#define NUM_STUDENT 3
int main()
{
	float score[NUM_STUDENT][NUM_COURSE]=
	{{75,94,67,96},{89,61,87,95},{95,80,99,84}};
	float (*p)[NUM_COURSE];
	float sum=0;
	int i,n;
	scanf("%d",&n);
	p=score+n-1;
	for(i=0;i<NUM_COURSE;i++)
		sum+=(*p)[i];
	printf("%.2f",sum/NUM_COURSE);
	return 0;
}

在这里插入图片描述

4.指针(型)函数和函数指针
指针型函数:形如:
<类型说明符><函数名>(参数表)
{
函数体
}
例:任意给定2个整数a,b,计算a+b,a
b,a%b

#include<stdio.h>
int *arithmetic(int a,int b)
{
    static int result[3];
    result[0]=a+b;
    result[1]=a*b;
    result[2]=a%b;
    return result;//返回数组地址
}
int main()
{
	int *p;
	int a,b,i;
	scanf("%d %d",&a,&b);
	p=arithmetic(a,b);
	for(i=0;i<3;i++)
        printf("%d ",*(p+i));
	return 0;
}

对于代码中出现的static,这里需要用static修饰,因为函数中的局部变量是存放在栈中,函数执行完之后自动释放,因此不应将局部变量的指针作为返回值,但加上static修饰就可以,因为static对栈变量的修饰,可以认为栈变量的生命周期延长到程序执行结束时。一般来说,栈变量的生命周期由OS管理,在退栈的过程中,栈变量的生命也就结束了。但加入static修饰之后,变量已经不再存储在栈中,而是和全局变量一起存储。同时,离开定义它的函数后不能使用,但如再次调用定义它的函数时,它又可继续使用, 而且保存了前次被调用后留下的值。
关于static更多的知识链接
在这里插入图片描述
函数指针
在c语言中,函数名就代表该函数的入口地址,就像数组名是数组的首地址。于是我们可以定义一个指针变量,在程序中像使用函数名一样使用函数的指针变量来调用函数。也就是说,一个函数一旦用某个指针指向,该指针就与函数名有着相同的作用。
函数指针定义形如:
<类型说明符>(*函数指针变量名)(参数表)
和普通指针一样,函数指针在使用前也要进行赋值,是指针指向一个已存在的函数的起始地址,形如<函数指针名> = <函数名>。等号右边的函数名所指出的必须是一个已声明过的,与函数指针具有相同返回值类型和相同形参表的函数。
例:

#include <stdio.h>
int one(int a,int b)
{
	......
}
int main()
{
	int (*fp)(int,int);
	int a;
	fp=one;
	a=(*fp)(1,2);
	......
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值