初识C语言(6)(指针和函数)

补充

不知道程序哪里出错?
事实:看出程序中的错误很难。
而你的室友也都很忙,不能帮你。
(这太糟了)。
介绍:逐语句调试!
按f11进入调试
再窗口选项中打开监视窗口,输入你想要的值。
按f11一句一句执行。
走你!
在这里插入图片描述

2.内存和指针

(1)内存

计算机有五大模块
动态存储,静态存储,中央处理器,输入设备和输出设备。其中输入输出设备就是鼠标,键盘,显示器这样的能让人和计算机交互的设备。
静态存储常用语存储数据,也就是我们的硬盘。特点是断电也可以保留数据。
中央处理器也就是CPU,进行数据的处理和计算,是电脑性能的重要指标。
内存也就是动态存储,负责临时数据的存储,CPU和硬盘之间的数据传递。相比于静态存储,传递数据的速度更快,但是断电时不能存储数据。
内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的。
所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。
为了表示方便,一般讲地址以十六进制进行存放。
为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。
在这里插入图片描述

变量是创建在内存中的,所以变量也有自己的地址。
打开我们的调试功能,打开内存窗口,就可以在程序运行的过程中看到内存的状况。
在这里插入图片描述
在这里插入图片描述

内存可以看做是一个宾馆
不同的地址看做不同的房间
房间里面有不同的变量。
用房间的门牌号就可以就可以找到对应的变量,获得对应的值。

(2)指针

所谓的指针指的就是地址。
存放指针的变量称之为指针变量。
下面就是指针变量的定义方式。

数据类型* 变量名
int* pa = 0;

还记得我们前面提到过的&符号吗。
它就是取地址符号,可以获得变量存储的地址。
那我们就取出一个变量的地址,然后用指针变量存储。

int a=0;
int* pa = &a;

地址可以用格式控制符&p来进行打印。
在这里插入图片描述

当我们获得一个地址,我们也可以通过地址找到变量的值,这和通过变量取得地址刚好相反。
用到了去地址符号*,他和取地址符号是互为逆运算。
在这里插入图片描述

还记得我们讲过的数组吗?把数组每个元素的地址打印出来。

#include<stdio.h>
int main()
{
	int a[] = {1,2,3,4,5,6};
	int i = 0;
	for (i = 0; i < 6; i++)
	{
		printf("%p", &a[i]);
	}
	return 0;
}

可得
在这里插入图片描述

int型的数据占四个字节,所以数组在内存中是连续存储的
二维数组类似于将行连成一条线,将元素连续存储。
(多维数组同理)

#include<stdio.h>
int main()
{
	int a[3][3] = { {1,2,3} ,{4,5,6}, {7,8,9} };
	int i = 0, j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 3; j++)
		{
			printf("%p\n", &a[i][j]);
		}

	}
	return 0;
}

在这里插入图片描述

这样我们就多了一种打印数组的方式
找到数组首元素的地址,去地址获得值。
然后对地址加一,去地址获得下一个值。

#include<stdio.h>
int main()
{
	int a[] = {1,2,3,4,5,6};
	int i = 0;
	for (i = 0; i < 6; i++)
	{
		printf("%d ", *(&a[0] + i));
	}
	return 0;
}

(int型数据虽然占用四个字节,但是对存储int型数据的数组元素地址加一时,地址的值会直接加4来到下一个数的地址,这涉及到指针偏移。同样,对其他类型也是同理,对地址加一会直接来到下一个数的地址)

#include<stdio.h>
int main()
{
	int a[3][3] = { {1,2,3} ,{4,5,6}, {7,8,9} };
	int i = 0, j = 0;
	for (i = 0; i < 9; i++)
	{
		printf("%p\n", (&a[0][0] + i));
	}
	return 0;
}

对元素地址加一之后打印地址,这里打印出的值间隔仍然为4。

要注意的是,数组名称本质上是首元素的地址。
因此也可以这样打印:

#include<stdio.h>
int main()
{
	int a[] = {1,2,3,4,5,6};
	int i = 0;
	for (i = 0; i < 6; i++)
	{
		printf("%d ", *(a + i));
	}
	return 0;
}

指针内容远不止如此,更进阶的内容会在后面的博客中讲到。

3.函数

(1)函数的组成
之前有提到过,我们使用的函数来自c标准库。
函数具体是什么呢?
它分为四个部分
返回值类型,函数名,参数,函数体。
这里就以main函数举例子
在这里插入图片描述

就和高中数学中的函数类似
如f(x)=2x+1,f就是函数名,x就是参数,2x+1就是函数体。
我们让x=1,
函数体执行后的值为3,那么3就是返回值。
是不是生动形象?
大部分的函数都有返回值,没有返回值的函数就是void类型。
在这里推荐一个网站
cplusplus

在这里插入图片描述

虽然是英文,但是你可以查询到函数的所有信息
从参数到返回值,包括头文件包含都十分权威,详细。
当然,这些函数远远不够我们的使用。我们可以自己编写函数。
此外,我们将简单的模块合并,用函数进行封装,可以让我们的代码更简洁,更有可读性。
我们先用函数编写一个简单的功能
如,对二数相加。
首先确定函数名和函数的返回类型。
我们要接收两个数,返回他们的和
所以返回类型为int。
然后传递参数给函数。函数为了接收参数而创建的变量为形式参数,要和原来参数的数据类型对应。形式参数属于局部变量,可以和原来的变量重名。
最后,我们返回二数之和,讲返回值进行打印。
具体函数如下

#include<stdio.h>
int add(int a, int b)
{
	return a + b;
}
int main()
{
	int a = 10;
	int b = 20;
	int c = 0;
	c = add(a,b);
	printf("%d", c);
	return 0;
}

在这里插入图片描述
具体的过程如上图。
求二数的较大者,同理。

#include<stdio.h>
int max(int a, int b)
{
	if (a > b)
	{
		return a;
	}
	else
	{
		return b;
	}
}
int main()
{
	int a = 10;
	int b = 20;
	int c = 0;
	c = max(a, b);
	printf("%d", c);
	return 0;
}

对二数进行交换

#include<stdio.h>
void swap(int a, int b)
{
	int tmp = a;
	a = b;
	b = tmp;
}
int main()
{
	int a = 10;
	int b = 20;
	swap(a, b);
	printf("%d,%d", a,b);
	return 0;
}

在这里插入图片描述

为什么没有交换成功?在这里插入图片描述

这涉及到函数栈帧的概念。
对函数进行反汇编,将语言转成汇编代码。
在这里插入图片描述

我们会看到一些从来没有见过的代码,这就是在执行代码时计算机的内部操作。
里面有很多细节,单纯描述很难逐步理解,我也不会做动图来描述,就只讲最关键的部分。
1.函数在每次被调用时,都会在内存中开辟新的区域,区域之间互不影响,分开执行。
2.函数在传递参数的时候,会临时创建一个变量,将值传递过去,这个变量和原本变量互不相干。(传递数组不会创建临时数组,而是直接传递数组的地址,用数组的地址找到数组进行操作。)
3.函数运行完毕后,函数原本占用的空间便被释放出来,回到原来调用函数的位置,原本的函数销毁。
这就可以解释为什么局部变量只能在区域内使用,也能解释为什么值交换失败了。
交换的值只是形式参数,原本的值没有受到影响。
那我们应该怎么编写这个函数呢?
既然临时变量和原来的变量没有关系,那我们就用指针直接传递地址过去,让函数通过地址找到原本的变量进行修改。

#include<stdio.h>
void swap(int* pa, int* pb)
{
	int tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}
int main()
{
	int a = 10;
	int b = 20;
	int* pa = &a;
	int* pb = &b;
	swap(pa, pb);
	printf("%d,%d", a, b);
	return 0;
}

在这里插入图片描述

成功!
这种方式称之为传址调用。
那么这就是函数的初步内容了。
下一章,让我们把前面的所有知识结合到一起,做一个小游戏。
敬请期待!
在这里插入图片描述

客官赏个赞再走8

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值