C语言学习笔记(二)-指针(一)

写在前面: 本系列内容均为自学内容,旨在补一下C语言知识,以便更好的学习STM32单片机,笔者没有系统的看过相关教学视频,所以不会有太多的理论解释,并且可能会存在很多问题,如果有大佬路过发现了,还望帮忙指正,感谢各位。

学习目标

1、将两个指针指向的数据进行交换;
2、指针与数组;
3、指针数组;
4、指向指针的指针

一、指针间的交换

首先了解一下指针:在上节内容中我们在程序中有过类似于int a=1;这样的定义,这里面的a一旦被定义就会在内存中被分配一定的内存用于存放这个变量,而用于存放变量a的内存便是a的地址,而这个地址就是指针,有了指针就会有指针变量,指针变量就是用于存放变量a的地址的变量,在实际使用的时候我们会用到“*”和“&”两种符号;下面看一个程序,使用的软件依然是《CodeBlocks》:
程序1:

#include "stdio.h"

int *p1,*p2,a,b,*t1,t2;	//带星号的是指针变量,不带的是常量变量

int main()
{
	scanf("%d,%d",&a,&b);
	//scanf_s("%d,%d",&a,&b);	//如果使用的是VS请用这句
	p1 = &a;	//让指针p1指向数据a所在的地址
	p2 = &b;	//让指针p2指向数据b所在的地址
	printf("%d,%d\n", p1, p2);	//输出两个指针的地址
	if (*p1 < *p2)	//交换数据,交换指向的地址
	{//不加星号,交换指针指向,交换指向的地址,同时地址中的数据也发生交换
		t1 = p1;
		p1 = p2;
		p2 = t1;
	}
	/*
	if (*p1 < *p2)	//交换数据,交换指向的地址
	{//加星号,那t2就不能加,这种情况下交换的是指针指向的数据,而不是地址
		t2 = *p1;
		*p1 = *p2;
		*p2 = t2;
	}*/
	printf("%d,%d\n",*p1,*p2);	//输出两个指针中存放的数值
	printf("%d,%d\n", p1, p2);	//输出两个指针交换后的地址
	printf("\n\n");
}

上面的程序中*p1、p2和t1就是指针变量,他们用于存放a和b的地址,或者说指针变量指向的地址是a和b的地址,此时如果操作两个指针变量即可对其中的数据进行操作,就像上面的程序,我们可以将两个指针的数据进行交换,也可以将数据所在的地址以及数据一起交换,下面的截图就是最后运行后的结果:
图2-1:
在这里插入图片描述
图片中,第一行是手动输入的变量a和b的数据,第二行是指针变量p1和p2在交换前所指向的变量的地址,即p1指向a,p2指向b,第三行是执行完交换程序后两指针中的数据,可以发现数据进行了交换,第四行的内容是两个指针变量交换后所指向的地址,可以发现地址也发生了改变,当然这是按照程序中第一个if语句执行后的结果,如果是按照第二个if语句,也就是屏蔽了的if语句,则只会把数据交换而不会交换指向的地址,这里就不在演示;
除了上述的程序,我们还可以用下面的程序来实现上面的功能:
程序2:

#include "stdio.h"

int *p1,*p2,a,b,t2;	//带星号的是指针变量,不带的是常量变量

void change(int *pt1,int *pt2); //声明一下该程序

int main()
{
	void change();
	scanf_s("%d,%d",&a,&b);
	p1 = &a;	//让指针p1指向数据a所在的地址
	p2 = &b;	//让指针p2指向数据b所在的地址
	printf("%d,%d\n", p1, p2);	//输出两个指针的地址
	change(p1,p2);
	printf("%d,%d\n",*p1,*p2);	//输出两个指针中存放的数值
	printf("%d,%d\n", p1, p2);	//输出两个指针交换后的地址
	printf("\n\n");
	//return 0;
}

void change(int *pt1,int *pt2)
{
	//这个程序只交换数据,不交换地址
	if (*pt1 < *pt2)
	{
		t2 = *pt1;
		*pt1 = *pt2;
		*pt2 = t2;
	}
}

上述程序中pt1和pt2是形式参数,这两个变量不起实际作用,只用来定义函数,在实际使用时,起实际作用的,也叫实际参数是p1和p2,这个程序跟上述程序一样,也是用来交换数据的,不过这个只交换数据,效果与上一个沉程序中的第二个if语句相同,下面是该程序运行后的结果:
图2-2:
在这里插入图片描述
可以看到指针变量p1和p2只交换了其中的数据并没有交换指向的地址;
在说一下下面的程序是没用的:
函数1:

void change(int *pt1,int *pt2)
{
	if (*pt1 < *pt2)
	{
		t2 = pt1;
		pt1 = pt2;
		pt2 = t2;
	}
}

这个函数与上面程序2中的函数相差不大,不过这个是想通过交换地址来达到交换数据的目的,但是这个函数并不能实现他想实现的效果,下面是用这个函数执行程序2后的结果:
图2-3:
在这里插入图片描述
依然是后两行是交换后的数据和地址,可以发现,与交换前相同,这是因为,在这个函数中,形参是pt1和pt2,而函数中的if语句中使用的确实pt1和pt2这与形参不相同,所以if语句不起作用,进而执行结果就是未进行交换。

二、指针与数组

1、指针与一维数组

首先我们做以下定义:int *p3,c[5]={1,2,3,4,5};然后做赋值操作p3=c;或p3=&c[0];
在上面的定义及赋值中,p3是指针,我们将p3做p3=c的赋值,其中的c就表示数组c[5]的首地址,当然同样的&c[0]同样表示数组首地址,因为数组是从0位开始,这时p3就指向了数组c的首地址;据此我们可以得到以下内容:
首先,p3+n和c+n都表示数组元素c[n]的地址,对于上述定义,p3+2和c+2都可以表示c[2]的地址;
第二,由于得到了地址那么相应的*(p3+n)和*(c+n)表示c[n]的数据;
第三,*(p3+n)可以用p3[n]表示;
程序3:

#include "stdio.h"

int *p3, c[5] = {1,2,3,4,5};

int main()
{
	p3 = c;	//c是数组c[]的首地址,也可以用下面的方法表示
	/*p3 = &c[0];*/
	printf("%d,%d\n", *p3+1, c[1]);	//这句与下面的一句相同
	/*printf("%d,%d\n", p3[1], c[1]);*/
	printf("%d,%d\n",p3+1,&c[1]);	//这句与下面的一句相同
	/*printf("%d,%d\n", &p3[1], &c[1]);*/
	printf("\n\n");
	//return 0;
}

程序3中,我们的目标是通过操作指针变量p3将数组c中第二位元素c[1]的值输出,程序中将数组相应的数字也输出了,同时地址也输出出来,以做对比,下面是运行后的结果:
图2-4:
在这里插入图片描述
通过图片可以看出,指针指向的数字及其地址与数组原本应该有的数字和地址相同,说明我们的程序实现了目标;

2、指针与二维数组

首先我们先定义:int *p4,d[3][4];然后做赋值操作:p4=d[0];
上面的定义与赋值操作中,我们定义了数组和指针,并且让指针指向数组的d[0][0]的地址,同时也是首地址,所以我们可以得到以下内容:
首先在指针和二维数组的操作中,我们可以用p4=d[0]的方式让指针指向二维数组的首地址;
第二,二维数组第一个数据是d[0][0],因此这个数据的地址也是整个数组的首地址;
第三,数组的首地址我们可以用d[0]来表示,相应的其他位的地址如d[1][2]的地址可以用d[1]+2,当然我们依然可以用&d[1][2]来表示地址,同样的数组第二行的首地址可以用d[1]来表示,这行的成员可以用如第三个成员,可以用d[1]+2的方式来表示;
程序4:

#include "stdio.h"

int *p4, d[3][4];	//指针和数组
int i, j;	//行数和列数
int main()
{
	p4 = d[0];	//第一次赋值是方便使用指针写程序,此时数组中无数据
	printf("plese input d[3][4] there:\n");
	//这个循环是方便写数组所需的个数个数字
	for (i = 0; i < 3; i++)	//行数
	{
		for (j = 0; j < 4; j++)//列数
			scanf("%d", p4++);
	}
	p4 = d[0];	//第二次赋值是让指针指向已有数据的数组
	//这个循环是将数组按照数组的应有形式输出
	for (i = 0; i < 3; i++)	//行数
	{
		for (j = 0; j < 4; j++)//列数
			printf("%4d",*p4++);
		printf("\n");//输出完一行后从下一行输出下一列
	}
	printf("\n\n");
	//return 0;
}

程序中需要注意的是,对指针进行了两次赋值,其中第一次是为了能用指针表示数组,所以先将数组的地址给到指针,以便在输入操作时能用指针表示,第二次赋值是因为第一次赋值的数组中没有数据,第二次将数组中的数据也一并给到指针,这样在后面的输出中就可以用指针表示了,下面是运行后的结果:
图2-5:
在这里插入图片描述
第一行不用管,第二行是手动输入的数据,程序中定义的数组一共有12个数据,输入时就需要输入12个,每个数据用空格隔开,此时按下回车就可以得到最后三行,最后三行就是程序中定义的二维数组,共3行4列;这里就不输出二维数组的地址了,简单说一下,二维数组中每行中相邻的两位数据的地址是连续的,下一行第一个数据的地址紧连着上一行最后一个数据的地址;

3、指针与字符数组

对于字符串,在使用输入输出函数时要使用格式字符“%s”,如printf(“%s”,s),下面直接来看程序:
程序5:

#include "stdio.h"

char f[30], * p6 = f;
int main()
{
	printf("pless input there:\n");
	gets(f);	//相当于scanf("%s %s %s",f1,f1,f3);
	printf("%s",p6);
	printf("\n\n");
	//return 0;
}

程序中输入函数使用的时gets(),这个函数的功能跟scanf是相同的,不过相对来说gets函数可以直接输出空格,但scanf需要通过格式字符来实现这个功能,下面是最终运行结果:
图2-6:
在这里插入图片描述
图片中,第二行是手动输入的字符串,其中包括英文字母、空格、标点符号,但是我们依然可以进行输出,第三行是输出后的结果;

三、指针数组

首先定义:int *p[3];这个定义表示定义一个指针数组,在这个定义中首先是定义一个数组,再将数组指针化,所以在这个定义中实际上有3个指针,这3个指针是这个指针数组的成员,并且这3个指针可以分别指向一个地址,其他与上述用法相类似,下面直接看程序:
程序7:

#include "stdio.h"

char* p7[4] = {"beijing","shanghai","hangzhou","zhejiang"};
int p;
int* p8[3];	//指针数组,里面包含3个指针
int q1[3] = { 7,8,9 }, q2[2][3] = { {1,2,3},{4,5,6} };
int main()
{
	for (p = 0; p < 4; p++)
		printf("%s\n",p7[p]);//指针数组输出要加上中括号和要输出的位号
	printf("\n");
	for (p = 0; p < 3; p++)
	{
		p8[p] = &q1[p];	//将q1相应位的地址赋予指针相应位
		printf("%d",*p8[p]);	//加星号,不加变地址
	}
	printf("\n\n");
	for (p = 0; p < 2; p++)
		p8[p] = q2[p];	//将二维数组每行首地址分别赋予指针数组的指针成员
	for (p = 0; p < 3; p++)
		printf("%d",*p8[1]+p);	//将第二行的数据输出
	printf("\n\n");
	//return 0;
}

程序中定义了两个指针数组p7和p8,这两个指针数组中第一个已经直接定义了成员,第二个并没有定义成员,在程序中,直接将指针数组p7的成员进行输出,指针数组p8,先是让其指向q1并输出,然后让数组中的个指针成员分别指向q2的各行首地址,最后选其中一行进行输出;最后运行后的结果如下:
图2-7:
在这里插入图片描述
通过结果可以看出,程序中想要输出的数据均成功输出;

四、指向指针的指针

指针除了可以指向整型变量、字符型变量、数组等,还可以指向指针,正常定义一个指针:int *p1,而定义指向指针的指针时要这样定义:int **p2,这时p2就可以指向指针了,当然p2可以这样写:*(*p2),下面看一下程序(以相对简单的一维数组为例,二维数组就不写了):
程序8:

#include "stdio.h"
#include "stdlib.h"

int g[5] = {1,2,3,4,5},*p9,**p10,t1;
//**p10是指向指针的指针,相当于*(*p10)
int main()
{
	//方式1
	p9 = g;//指针指向数组
	p10 = &p9;//指针指向指针
	for (t1 = 0; t1 < 5; t1++)
		printf("%d", *(*p10 + t1));
	printf("\n");
	//方式2
	for (p9 = g; p9 - g < 5; p9++)
	{
		p10 = &p9;
		printf("%d",**p10);
	}
	printf("\n\n");
	//return 0;
}

程序中用两种方式表达,都是先将数组的数据赋予指针变量p9,再将指针p9的数据赋予指向指针的指针变量p10,此时用p10输出,就可以输出数组的数据,下面看运行结果:
图2-8:
在这里插入图片描述
运行后,第一行是方式1输出的结果,第二行是方式2输出的结果,对比发现与定义的数组相同,则输出正确。
指针的内容先告一段落,后续还会进行指针的更新,以上内容如有错误还望指正,感谢观看。
以上仅作笔记参考,不作教学,感谢观看。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言是一种广泛应用于计算机科学和软件开发的编程语言。它具有强大的功能和灵活性,适用于开发各种类型的应用程序。 C语言专题精讲篇是一个对C语言进行深入学习和讲解的系列文章或课程。它汇总了C语言相关的重要知识点和技巧,旨在帮助学习者更好地理解和运用C语言。 这个专题中的笔记涵盖了C语言的各个方面,包括基本语法、数据类型、运算符、流程控制、函数、数组、指针、结构体、文件操作等。通过系统性的学习和总结,这些笔记可以帮助学习者逐步掌握C语言的核心概念和常用技巧。 在这个专题中,学习者可以学到如何编写简单的C程序,如何使用变量和运算符进行计算,如何使用条件和循环语句控制程序流程,如何使用函数进行代码的模块化,如何使用数组和指针进行数据的处理,如何使用结构体组织复杂数据,如何进行文件的读写等等。 C语言专题精讲篇的目的是帮助学习者全面、深入地了解C语言的各个方面,并能够独立编写和调试简单到中等难度的C程序。通过反复实践和练习,学习者可以逐渐提高自己的编程能力,并为进一步学习更高级的编程语言打下坚实的基础。 总之,C语言专题精讲篇的笔记汇总是一份重要的学习资料,可以帮助学习者系统地学习和掌握C语言的基础知识和常用技巧,为他们未来的编程之路打下坚实的基石。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值