栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

10天学会C语言——指针

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

10天学会C语言——指针

//个人感觉c语言最难的一部分

指针定义 定义

指针变量是一种特殊的变量,存储的是地址信息,定义方式 数据类型 *指针名。因为指针是一个变量,所以指针名就是地址的别名,指针名可以直接当地址使用,这个地址就是指针变量存储的地址。

注意:不同配置电脑,不同操作系统下,指针所占空间的大小有区别,可能4个字节,可能8个字节以实测为准。

类型

指针要分不同的类型,作用就在于规定该指针所指向区域的大小,不同类型的指针,虽然地址值相同,但是如果类型不同,指针偏移时,偏移的空间大小也不同。比如int *p;char *p;int (*p)[3],三个指针+1后偏移的大小分别是4字节,1字节和12字节。 

指针类型只和偏移量有关,但是指向同一个地址的不同类型指针的值是一样的,赋值的时候用哪个都可以。赋值成功之后,指针偏移取决于此时指针的类型。

运算符优先级

https://blog.csdn.net/changexhao/article/details/82556761

不同的运算符优先级顺序,导致指针的类型各不相同。

比如

int *a指针,指向整型数
int **a二级指针,指向一个指针,被指向的指针指向整型数
int (*a)[10]指针,指向含有10个int类型数据的数组
int (*a)(int )指针,指向一个函数,返回值为int型,含有一个int型参数
int(**a)[10] 二级指针,指向一个指针,被指向的指针指向含有10个整型数的数组
int*(*a)[10]指针,指向含有10个整型指针的数组
int(*(*a)(int,int))(int)指针,指向一个函数,该函数返回值类型是个函数指针,且含有2个int型参数,返回值指针指向另外一个函数,这个函数有一个int参数,返回值类型为int型。
int a[10]数组,含有10个整型数
int *a[10]数组,含有10个整型指针
int (*a[10])(int )数组,含有10个函数指针,函数返回值为int型,且含有一个int型参数

注:用了指针后很容易出现Segmentation fault 段错误,一般是访问野指针或者空指针引起的,若出现此类错误,我们需要检查访问指针的相关代码是否访问了非法内存。

指针作用

有了指针之后,变量的访问方式便多了一种。一种方式是通过变量名直接访问所存储的变量值,第二种是方式是间接访问,指针变量p存储所访问变量的地址,通过*p运算取内容,访问到变量值。注意:*运算符在定义时表示指针,除此之外*运算符表示取地址的里面内容。

所以有了指针,在函数传参时,传入指针就是传入地址,通过*指针便可以操作该地址内的数据,操作之后,就算不返回数据,函数结束后,那片地址的数据还是发生了变化。比如两数交换,可以直接在主函数中,通过变量赋值的方法,交换数据,也可以在自定义函数中,传入两变量的地址,通过*指针,直接修改变量的值,交换数据,函数结束后,虽然形参指针的空间被释放了,但是两变量的值,还是改变了。

指针偏移

首先指针指向数组时,指针可以当成数组来用,访问数据时,可以用指针偏移或者下标法来访问数据,指针偏移访问数据比下标法效率高得多,下标法更形象,各有好处。其次数组名虽然是个地址,但是是个常量表达式,不能自增或者自减改变自身的值,只有变量才能改变自身的值。

一维数组和二维数组的偏移

指针偏移时,偏移的是指针自身类型的空间大小,所以:

数组名+1偏移的大小,取决于数组名的类型,这个数组名指向区域占多大,则偏移多大。一维数组int a[4],a=&a[0],所以a的类型是int,a+1偏移一个int类型的大小。二维数组int a[2][5],a=&a[0],因为a[0]是一个含5个int类型的一维数组,所以a的类型是int *[5],a+1偏移5个int类型大小。

数组名取地址+1偏移的大小,取决于取地址后的指针类型。二维数组int a[2][5],&a,类型是int*[2][5],&a+1偏移10个int类型。&a[0],类型是int *[5],&a[0]+1偏移5个int类型。&a[0][0],   类型是int *,&a[0][0]+1偏移1个int类型。一维数组int a[4], &a,类型是int*[4].&a+1偏移4个int类型,&a[0],类型是int*,&a[0]+1偏移一个int类型。

注意:数组名和指针不互用的情况:eg:int a[5],int*p=a,sizeof(a)和sizeof(p)值不同和数组名不可以自加,指针名可以自加。

二维数组的下标法与指针偏移的转换

对于二维数组int a [3][4],a[i]=*(a+i),a[i][j]=*(a[i]+j)=*(*(a+i)+j)。

指针分类 数组指针

二维数组传参是后,形参一般是一维数组指针,因为二维数组名类型是一维数组指针,,一维数组指针+-就会得到二维数组行地址的数据,一维数组指针取内容就会得到二维数组列地址的数据,再取一次内容就会进入到数据进行修改。

对于指针数组的指针,数组存的是指针,而数组名代表首元素地址,那么数组名的类型就是一个二级指针类型,传参的时候,形参用二维指针来承接,仍然可以修改指针数组里存储的指针值。

函数指针

函数指针类似java中的多态。函数指针,能指向同类型的函数(返回值相同,参数类型和个数相同)。主函数运行时,能根据不同的情况,让函数指针指向不同的函数,运行结果各不相同。有了函数指针,函数调用就有了两种方式,第一种函数名直接调用,第二种函数指针赋值为函数名或者&函数名。调用时,需要先对指针取内容进入到函数,再传参调用。

为什么为函数指针赋值可以赋值函数名或者&函数名呢?

函数和数组都分配有一段存储空间,以地址的角度,函数名数组名都是一个空间首地址,函数指针获取了地址后,取内容才能跳转到函数,运行函数。

对于函数int a(),函数名取地址再用,此时函数名代表的是整个函数这个对象a,取地址得到函数这个对象的首地址&a,类型是int(*)(),值等于函数入口空间的地址。函数名直接用,代表函数这个对象,也代表的是函数入口空间的地址,类型是int (),这个类型本质上是个*,值等于函数这个对象的首地址。函数名取内容再用,此时函数名代表函数这个对象的首地址,取内容就得到函数这个对象,类型是int(),等价于函数名。

二级指针

二级指针管理着一块地址,这块地址里面放着一些元素的地址,二级指针可以通过取内容,让这块地址里面放其他元素的地址。

二级指针就是指向指针的指针,使用二级指针的名字就是使用一级指针的地址,经常通过*指针得到一级指针,或者改值为其他的一级指针。再*一次指针就可以得到一级指针的内容。

当函数的返回值为一级指针(一个元素的地址)时,我们可以在主函数中创建一个一级指针来承接返回值。

比如:当函数没有返回值,但是函数里面malloc了一个数组的首地址int *m,如何将这块地址存起来,通过指针的特性,在主函数中也定义一个一级指针int *p和二级指针int **pp,pp=&p,传参传&p或者pp(类型是个二级指针),将m赋值给*二级指针(就是p),最后m和p都指向了那块数组,在主函数中也可以通过p或者*pp访问到这块数组了。这样二级指针pp的值没有改变,即他管理的地址还是p的地址没变,但是这块地址存储的一级指针(不可访问的元素的地址)变成了m指向的地址(可访问元素的地址)。

注意:二级指针不能简单的指向二维数组,int scores[3][4]; int **p=scores; *p与*scores的值不一样,*p是野指针会段错误。需要int *p2=(int *)scores; int **p3=&p2。这样**p3与**scores值相同。

指针函数和指针数组

指针函数时返回值为指针(各种指针都可以)的函数,传入参数,返回给我们需要的指针。

指针数组是一个存储指针(各种指针)的数组。存储的指针可以是函数指针,或者数组指针,或者普通指针等等。

注意:数组拷贝的问题,在函数里面直接开的数组不能被主函数使用,因为函数运行完毕,数组也会消失。要么把数组传进来依次赋值,要么直接在函数里面malloc空间,函数传参二级指针,在主函数里面就可以引用这片数组了。

#include
#include
#include
void copy1(int **cp1){
	int *p=(int *)malloc(5*sizeof(int));
	for(int i=0;i<5;i++){
		p[i]=i+1;
	}
	*cp1=p;
}

void copy2(int *cp2){
    int p[5]={0};
	for(int i=0;i<5;i++){
		p[i]=i+1;
		cp2[i]=p[i];
	}
}
int main(){
 int cp2[5]={0};
	 int *cp1;
	 copy1(&cp1);
	 copy2(cp2);
	
	  printf("cp1n");
	 for(int i=0;i<5;i++){
		 printf("%d ",cp1[i]);
	 }
	   printf("ncp2n");
	 for(int i=0;i<5;i++){
		 printf("%d ",cp2[i]);
	 }
}

作业案例

 1,矩阵的删改查

#include
#include
void shan(int (*p)[4],int h,int l){
	h=4;
	l=4;
	for(int i=0;i 

2,给定成绩,输入学生序号,查所有成绩和查询有不及格科目的学生并且输出不及格的科目

#include
#include 
#include
int* data(int (*p)[4],int loc){
	return *(p+loc-1);
}

void allprt(int *p){
	for(int i=0;i<4;i++){
		char a[5];
		switch(i){
			case 0:
			strcpy(a,"语文");
			break;
			case 1:
			strcpy(a,"数学");
			break;
			case 2:
			strcpy(a,"英语");
			break;
			case 3:
			strcpy(a,"物理");
			break;
		}
	printf("%s:%d ",a,*(p+i));
	}
	puts("");
}

void bjgprt(int (*bjgscore)[4],int **bjgrs,int len){
	for(int i=0;i 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1018953.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号