《算法笔记知识点记录》第二章——快速入门3[函数、指针]

请添加图片描述

☘前言☘

今天是我开坑的第三天,大家最近应该都在忙期末把?答应我考完试来打卡好么0.0
今天依然是会介绍很多基础知识,但是会很实用,如果我有哪些没有讲清楚的,欢迎大家联系我,你提出的问题是我修改完善的基础,万分感谢。

  • 欢迎大家加入我的打卡队列,如果你刷完了对你有帮助请你评论一个打卡。
  • 如果你觉得这本书有用的话还希望多多支持作者。

如果觉得这个文章有用还希望大家交出素质三连呀。

🧑🏻作者简介:一个从工业设计改行学嵌入式的年轻人
✨联系方式:2201891280(QQ)
📔源码地址:https://gitee.com/xingleigao/algorithm-notes
全文大约阅读时间: 80min



🍭1. 基础知识点

有了上节课的一点点开头印象,我们这次就可以看看稍微更加难以理解的东东,这部分也是c语言经久不衰的灵魂——指针和函数
那我们废话不多说,开始今天的学习吧。

🐌1.1 函数

如果程序逻辑比较复杂,代码量比较大、或者是重复性功能比较多,那么全写在main函数就会显得十分杂乱。为了使代码更简洁、思路更加清晰,C语言提供了函数功能。函数是一个实现一定功能的语句集合,并在需要时可以反复调用而不必每次重新写一遍。
其中我们用到的sin()cos()都是已经定义好的函数方便我们调用。

🐰1.1.1 函数的定义

如果我们函数体内有大量的重复操作,我们就可以自己定义一个函数。
基本定义的方法为:

返回类型 函数名称(参数类型 参数){
	函数主体
}

举个例子

#include<cstdio>
void print1(){
	printf("Haha,\n");
	printf("Good idea!\n");
}
int main(){
	print1();
	return 0;
}

执行结果就是

Haha,
Good idea!

我们看这个函数的类型,是void,所谓的void就是空,即不返回任何结果。
然后是我们调用的时候,由于不需要传参 所以括号里就是空的啥也没有,这种叫做无参函数


再看一个例子

#include<cstdio>
int judge(int x);
int main(){
	int a, ans;
	scanf("%d",&a);
	ans = judge(a);
	printf("%d\n",ans);
	return 0;
}
int judege(int x){
	if(x > 0) return 1;
	else if(x == 0)	return 0;
	else return -1;
}

输入-4执行结果就是:

-1

上面的代码就是将a传给x然后返回对应的值。
需要注意的点是
我这里故意把函数在后面定义了,如果不加第二行的那个声明编译器就会报错。所以如果你想写一个函数做占位符就要在最上面加声明。
如果你细心的话,会发现a传入怎么就变成了x了呢?
我们来看两个概念,就是全局变量和局部变量。


全局变量
全局变量就是定义之后所有程序段内都有效的变量。(可以理解为所有模块都能用)
举个例子

#include<cstdio>
int x;
void chage(){
	x = x + 1;
}
int main(){
	x = 10;
	change();
	printf("%d\n",x);
	return 0;
}

输出结果11
因为x是定义在最前面,每个函数都能找到这个变量的位置。所以就可以对它进行修改。


局部变量
相对的有一个概念就是局部变量,就是只有在函数体内部生效的变量。(可以理解为只在一小段程序内有效)
其实我们之前就接触到这个局部变量。for循环中的第一个初始化的时候如果声明变量,在for循环结束就会销毁。这是不是就是局部变量?
看个例子:

#include<cstdio>
void change(int x){
	x = x + 1;
}
int main(){
	int x = 10;
	change(x);
	printf("%d\n",x);
	return 0;
}

执行的结果就是10
为啥是10 内,其实因为change内的xmain函数的x并不是同一个x,change内的x只是main函数内x的一个副本。

( 这句话你们可以以后再看)每个函数都有自己的调用栈,当调用一个函数的时候,会创建自己的内存空间,让后把对应的传入参数复制到自己的栈内存区。

如果希望传入的值发生改变应该怎么整呢?其实主要问题就是要让在一个新函数内找到原来的变量的位置。所以要传入一个地址。(超纲了。。。下面讲)
最后,传入参数可以有多个,如下面的例子。

#include<cstdio>
int MAX(int a, int b, int c){
	int M;
	if(a >= b && a >= c)	M = a;
	else if(b >= a && b >= c)	M = b;
	else M = c;
	return M;
}
int main(){
	int a, b, c;
	scanf("%d%d%d",&a, &b, &c);
	printf("%d\n",MAX(a, b, c);
	return 0;
}

上面的程序就是打印三个值中最大的那个!

🐮1.1.2 main函数

我们一直在用main函数还没好好看看它233
无论一个程序多么复杂,程序执行都是先找名字叫做main的函数进行执行。
基本结果就是

int main(){
	return 0;
}

其中的return 0就代表程序正常结束了。虽然现在main可以void。但是我不太建议,因为以后程序可能有一些问题返回不同的错误码非常有必要。

🐟1.1.3 数组作为函数传参

函数也是可以作为参数传入的。但是不需要写第一维的长度(本质还是传入了一个指针0.0)。
重要的是,数组作为传参时,在函数中对数组元素的修改就等于对原数组元素的修改。(毕竟指针都过来了,读到的元素也是之前的元素-.-)
示例:

#include<cstdio>
void change(int a[], int b[][5]){
	a[0] = 1;
	a[1] = 3;
	a[2] = 5;
	b[0][0] = 1;
}
int main(){
	int a[3] = {0};
	int b[5][5] = {0};
	change(a,b);
	for(int i = 0;i < 3; i++)
		printf("%d ",a[i]);
	return 0;
}

上面的输出结果就是1 3 5,因为对数组的修改就是直接修改。(先记住吧。下面就会讲到,就是直接操作的原本元素)

🐬1.1.4 函数的递归调用

递归就是函数自己调用自己的过程。

#include<cstdio>
int F(int n){
	if(n == 0)	return 1;
	else return F(n-1) * n;
}
int main(){
	int n;
	scanf("%d",&n);
	printf("%d\n",F(n));
	return 0;
}

输入数据:3 输出结果:6
我自信看完指针,这块你会有更深的理解0.0,先记住,等下解密。

⚔️1.2 指针

🗡1.2.1 指针究竟是什么

先放上一张神图。大名鼎鼎的冯·诺伊曼体系结构。
在这里插入图片描述
可以发现运算器能直接读取的程序是在存储器内的。那么运算器怎么拿到这个数据呢?
首先我们先明确,因为变量的读取都是从内存中的,所以一定会有一片空间在内存中。我们把它想象成有一栋公寓有一堆房间,我们想要找到我们的房间是不是得有个房间号?请添加图片描述
然后有没有发现102 103 104 105这四个房间我没画隔断,因为很多地方是按照门来编号的,但是一个大房间可能对应多个门呀。

  • 上面的门的编号方式就是编制方式(可以一个门一个号,也可以两个门一个号),地址就每个门的房号,然后数据其实就是门内的内容。
  • 我们根据房号找到家,计算机就是根据地址找到数据

指针其实就是地址。也就是上面的房号。
我们假如忘了门号就看看外面门上的标识牌对吧?计算机内如果变量想要直到自己的地址也是需要看看自己的房号,怎么去看呢?用&,因为是变量自己看所以就是&x就拿到地址啦0.0
举个例子:

#include<cstdio>
int main(){
	int a = 0;
	printf("%d %d\n", &a,a);
	return 0;
}

输出结果可能是:2686748 1
其中地址是一个unsigned类型的整数(64位是unsigned longlong),至于为啥是无符号的,你见过谁家房号是负数么0.0

🛡1.2.2 指针变量

指针变量用来存放地址,然后可以看下图,就是指针变量,比较厉害,手里有把钥匙,并且这个钥匙可以开对应的门。
请添加图片描述
然后由于这是一把钥匙,所以在声明它的时候需要在它前面加个*,表示它是一把钥匙。也就是int *p;,一般来说都是把*放在变量前面。
同时*也表示拿钥匙开门,*p就表示拿到对应的数据,所以图上的就是104号房间对应的元素。
刚才知道&是取地址,就是看看房间号,那么p = &a就可以把p这把钥匙变成a所对应的地址。此时再去*p就是a的值。
看下面的程序:

#include<cstdio>
int main(){
	int a;
	int *p = &a;
	*p = 233;
	printf("%d %d", *p, a);
	return 0;
}

输出结果是233 233,因为开房间的优先级很高,在赋值的时候是先打开门,再把数据写入,所以就把值写入到了a之中。最后输出就是两次a的值。


最后说明一下,不同的指针是不同的,比如下面的两把钥匙是不一样的。因为102-105是连着的,所以第一把钥匙钥匙+1实际地址会加4,而右边的指针对应的只有一个门,+1的时候只会加1,
其实对应的就是int *char *类型的指针,其中一个长度为4,另一个为1。
这个指针的类型叫做基类型
请添加图片描述

🔧1.2.3 指针与数组

之前有提到过数组就是一片连续的空间,数组名称也作为数组的首地址使用。
请添加图片描述
根据上面提到的指针加1等于加对应的数据长度,所以a+i&a[i]是完全相同的。
在枚举元素的时候可以这么写:

#include<cstdio>
int main(){
	int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	for(int *p = a; p < a +10;p+)
		printf("%d ",*p);
	return 0;
}

指针的减法

#include<cstdio>
int main(){
	int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	int *p = a;
	int *q = &a[5];
	printf("p = %d ", p);
	printf("q = %d ", q);
	printf("q - p = %d",q - p);
}

输出结果是:p = 2686688 q = 2686708 q - p = 5
会不会感觉震惊?这个q-p竟然不是20???,其实指针的减法是计算两个指针直接差多少个基类型,因为是int所以返回的就是相差多少个int。

⚒1.2.4 使用指针变量作为函数参数

指针变量也可以作为函数的参数,这时会把变量的地址传入函数。如果在函数中对这个地址中的元素进行修改,那么原本的数据也会修改。
例如:

#include<cstdio>
void swap(int *a, int *b){
	int temp = *a;	
	*a = * b;
	*b = temp;
}
int main(){
	int a = 1, b = 2;
	swap(&a,&b);
	printf("%d %d",a , b);
	return 0;
}

结果就是2 1,是不是就做到了?但是为啥呢?我们来看看这个过程
请添加图片描述
可以发现在执行的过程中,swap直接改的就是原内存空间的值,当然改成功啦0.0


我们再看一个错误写法:

void swap(int *a,int *b){
	int *temp = a;
	a = b;
	b = temp;
}

这个如果看图就是讲swap调用栈里的a和b的值改了,并没有对之前的a和b造成任何影响。

🔨1.2.6 引用

引用是C++中一个强有力的写法,引用时候不产生副本,只是给原变量起了个别名。对引用变量的操作就是对原始变量的操作。
看一个例子:

#include<cstdio>
void change(int &b){
	b = 1;
}
int main(){
	int x = 10;
	change(x);
	printf("%d\n",x);
	return 0;
}

上面打印的结果就是1
其实底层实现的话就是使用的指针,我猜想应该是使用的不可修改指针进行实现的。感兴趣的话可以看看这篇博客。理解C++中引用的底层实现


但是要注意:

#include<cstdio>
void swap(int &a, int &b){
	int temp = a;
	a = b;
	b = temp;
}
int main(){
	int a = 1, b = 2;
	swap(&a,&b);
	return 0;
}

上面的写法会直接报错,因为a和b的地址都是常量,所有声明的变量的地址都是常量不可进行修改!!!所以引用的时候不能传入变量的取地址。

关于指针的一些知识点我写了一篇文章做总结,如果大家有兴趣的话可以去阅读一下,保证会有更深的理解0.0
关于室友收租懂了指针那点事


🐳课后习题

今天的题目难度也不高,也不老多 哈哈哈哈哈,我写完会放题解,大家写完了可以在评论区打卡哟!我觉得,题解我放评论区吧,这样不用修改文章。(大家有问题可以联系我,今天太晚了,题解等明天把)

题目
2.6小节——C/C++快速入门->函数
2.7小节——C/C++快速入门->指针

题解:评论区见,置顶没有就是我没写完0.0,大佬们刷完打个卡

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XingleiGao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值