2020第二次软件培训——函数、数组、字符串
函数
引例
第一个函数
#include<stdio.h>
void print_c(); //函数声明
void print_c() //函数定义
{
printf(" ###### \n");
printf("## ##\n");
printf("## \n");
printf("## \n");
printf("## \n");
printf("## ##\n");
printf(" ###### \n");
printf("\n");
}
int main()
{
print_c(); //函数调用
print_c();
print_c();
return 0;
}
什么是函数
c语言标准库本身提供了一些基本的函数,称为系统函数,如scanf函数、printf函数、sqrt函数、pow函数等等,除了系统函数外,我们还可以自己定义函数。
函数的作用
①main函数变得冗杂
②程序复杂度不断提高
③代码前后关联度高,改一处就牵一发而动全身
④变量的命名成了问题 简单的名字都用完了
⑤为了在程序中多次实现某功能,不得不多次写重复的代码
函数定义的一般格式
<函数返回值类型>函数名(类型名 形式参数1,类型名 形式参数2)
{
// 函数体
}
返回类型
函数的返回类型
函数的返回类型就是当系统执行过你的程序之后,应该交还给你什么
返回类型的种类
C语言的返回类型就是int , double , float 等等基本类型
比如经过各种计算,你的函数最后得到一个int整形,
那么在定义函数的时候就规定这是一个int型函数
返回类型的声明
返回类型的声明在两个地方体现:
第一就是函数头,需要告诉系统这是一个什么类型的函数——就像定义一个变量(int a = 0),定义函数的时候同样需要指定类型
第二是在函数结束后,你需要确确实实的返回给他一个你声明的该类型的值,也就是return。
动手写一个函数
输入两个整数,返回它们中较大的一个数
#include <stdio.h>
int max(int a, int b);
int max(int a, int b)
{
if(a > b)
return a; //函数遇到return语句会立刻返回
else
return b;
}
int main()
{
int a, b, c;
scanf("%d%d", &a, &b);
c = max(a, b);
printf("%d", c);
return 0;
}
不需要返回值的情况-void
若定义函数时指定函数为void,则函数不需要返回任何内容,可以不写return,或者写return(后面不接任何值)
动手写一个函数
输出***
***
***
(三行三列)
#include <stdio.h>
int print_();
int print_()
{
printf("###");
printf("\n");
}
int main()
{
print_();
print_();
print_();
return 0;
}
函数的参数
不论是否需要参数,在函数的定义是都应该在名字后面加上括号。
参数的定义与主函数中变量定义一样。
参数与主函数中传入的变量并没有直接的关系
令人困扰的问题:形式参数与实际参数
我们先来看一段代码
#include <stdio.h>
void change(int a, int b);
int main() {
int a = 0, b = 10;
printf("a = %d, b = %d\n", a, b);
change(a, b);
printf("a = %d, b = %d\n", a, b);
return 0;
}
void change(int a, int b) {
int temp;
temp = a;
a = b;
b = temp;
}
试着运行一下上面的代码。看看它的运行结果与你预期的是否一致。
输出结果
a = 0, b = 10
a = 0, b = 10
上面的代码看似完成了交换a, b的值的功能,而程序的运行结果却是两次输出内容保持一致。下面我们就来讲一下这个问题。
我们从函数的值传递讲起。
首先,main函数在调用change函数时,将main函数中a的值,也就是0传递给了change中的变量a,又将main函数中b的值,也就是10传递给了change中的变量b。这时,main函数与change函数没有其它任何关联:main中的a只是它的值与change中的a相等,而change函数中的a发生任何改变都不会影响到main中的a变量。这就好比你在主函数中写的代码是
change(0, 10);
主函数只是将其变量的值传给了change函数,它们只是名字相同,而没有任何关系。这好比我有一张表格,然后我去打印店复印了一份给你,你即使把那个表格撕掉,都不会影响我的表格。(其中的我就是main函数,你就是change函数)
在以上前提下,change函数中的a变量和b变量再怎么交换都不会影响到main函数中的变量。
那我们该用什么方式去解决“使用函数去交换两个变量的值的问题”呢?
这就是涉及到指针的知识了。等你们对指针有一定的了解后,我们会讲解这方面的知识点的。
注意事项
函数的定义不可以在其他函数中进行。新的函数的定义应当在主函数的外面。
函数定义位置的选择有两个:
1.定义在调用这个函数的函数上面
2.在调用这个函数的下面定义,但要现在上面声明这个函数
注:若使用第二种,声明应包括函数的返回值类型,名称和参数列表
如何使用函数
函数经过定义就可以通过调用他的名字来使用他。
如果函数有参数,则使用时应当填满所有参数,如果函数不含参数,也不可以丢掉空括号
如果函数没有返回值,则直接作为一句话输入在程序中即可
如果函数有返回值,则应该利用一个变量继承这个返回值。
做点题吧
1)设计函数计算1 * 2 * … * 10+11 * … * 20+21 * … * 30的和。
#include <stdio.h>
long long ans(int begin)
{
int i;
long long ans;
ans = 1;
for (i = begin; i< begin + 10; i++)
{
ans = ans * i;
}
return ans;
}
int main ()
{
long long sum;
sum = ans(1) + ans(11) + ans(21);
printf("%lld", sum);
}
编写程序,使用自定义函数计算下列分段函数𝑦 = 𝑓(𝑥)的值。
(保留三位小数)
#include<stdio.h>
#include<math.h>
float f(float x) {
if (x >= 0 && x <= 4) {
return sqrt(64 * x);
}
if (x > 4 && x <= 8) {
return pow(2, x);
}
}
int main() {
float x;
scanf("%f", &x);
printf("%.3f", f(x));
return 0;
}
已知体质指数
B
M
I
=
体
重
(
k
g
)
身
高
2
(
m
2
)
BMI=\frac{体重(kg)}{身高^2 (m^2)}
BMI=身高2(m2)体重(kg)
大多数人的BMI在[18.5 , 24]
现输入一整型数据n 代表身高或体重
求对应的身高或体重范围
#include <stdio.h>
#include <math.h>
float weight_max(float a)
{
return 24 * pow(a, 2);
}
float weight_min(float a)
{
return 18.5 * pow(a, 2);
}
float height_max(float a)
{
return sqrt(a / 18.5);
}
float height_min(float a)
{
return sqrt(a / 24.0);
}
int main()
{
float a;
scanf("%f", &a);
if (a >= 0.0 && a <= 3.0)
{
printf("体重范围为%.3f到%.3f", weight_min(a), weight_max(a));
}
else
{
printf("身高范围为%.3f到%.3f", height_min(a), height_max(a));
}
return 0;
}
数组
数组的定义
数组的概念:
简单来说,其实就是存储一批相同类型数据的地方。
数组的定义格式:
类型 数组名[常量表达式]
例如
int a[10];
char b[10];
float c[10];
double d[10];
###循环和数组密不可分
数组的定义是为了规范化、大批量的对数据进行操作,而循环又刚好是对数字进行批量操作的方式。在数组的输入输出等操作中,我们更多的是使用循环。
int a[10];
int i;
for (i = 0; i < 10; i++)
{
a[i] = i;
}
例子:存入十个同学的分数(整形),并且计算平均分,最高分和最低分。
数组初始化
int a[3] = {0};//将所有元素初始化为0
int a[3] = {1, 2, 3};//对每个元素分别赋值
int a[] = {1, 2, 3};//偷懒的写法,让编译器自己计算数组的大小
数组不能被动态定义
//以下代码为错误示范
int n, i;
printf("请输入数组的大小:");
scanf("%d", &n);
int a[n];
(ps:以上代码如果按照C99标准并不会报错)
数组的访问
数组名[下标]
例如:
a[0]//访问a数组的第1个元素
b[1]//访问b数组的第2个元素
c[2]//访问c数组的第3个元素
访问数组不要越界
数组越界不报错是因为在C语言中这种行为是没有明确定义的,最后的运行结果取决于编译器自己。但是这样做会导致程序出现难以察觉的bug!
二维数组
二维数组的定义
定义二维数组的方法与一维数组类似。
类型 数组名[常量表达式] [常量表达式]
例如:
int a[4] [4]; // 4行4列
char b[2] [3]; // 2行3列
double c[6] [3]; // 6行3列
二维数组的访问
int a[6] [6];//定义一个6行6列的二维数组
a[5] [5];//访问最后一个元素
与一维数组一样,都需要注意越界的问题!!!
二维数组的初始化
二维数组本质上是以数组作为数组元素的数组,在内存中是线性存放的。
与我们直观理解的矩阵不太一样。
int a[2] [2]={1, 2, 3, 4};
2.利用循环的嵌套输入输出
int a[2] [2]={{1, 2}, {3, 4}};
int a[2] [2]={{1, 2},{3, 4}};
int a[3] [4] ={0};//元素全部初始化为0
int a[][4] = {1, 2, 3, 4};//二维数组的初始化偷懒的写法,相当于a[1] [4]
字符数组
可以先定义指定长度的字符数组,然后再给每个元素单独赋值:
char str[10];
str[0] = ‘x’;
str[1] = ‘y’;
或者直接在定义的时候对字符数组进行初始化
char str1[5] = {‘e’, ‘s’, ‘t’, ‘a’, ‘\0’};//单独初始化元素
char str2[] = {‘e’, ‘s’, ‘t’, ‘a’, ‘\0’};//不写数组大小
char str3[] = {“esta”};//使用字符串常量初始化字符数组
char str4[] = “esta”;//使用字符串常量初始化,可以省略大括号
字符串处理函数
注意:使用字符串处理函数时,要导入string.h
strlen
使用方法:strlen(str)
用于返回指定字符串的长度。
strcpy
使用方法strcpy(str1, str2)
strcpy 函数用于拷贝字符串,将str2拷贝到str1中,包含最后的结束符 ‘\0’。
注意:为了避免溢出,必须确保用于存放的数组长度足以容纳待拷贝的字符串(长度需要包含结束符 ‘\0’)。
strcat
使用方法:strcat(str1, str2)
将一个字符连接到目标字符串后边,在此过程将覆盖第一个参数的结束符 ‘\0’。
strcmp
使用方法:strcmp(str1, str2)
用于比较两个字符串。
从第一个字符开始,依次比较每个字符的 ASCII 码大小,直到发现两个字符不相等或结束时(‘\0’)为止
返回值 | 意义 |
---|---|
<0 | 字符串1的字符小于字符串2对应位置的字符 |
=0 | 两个字符串的内容完全一致 |
>0 | 字符串1的字符大于字符串2对应位置的字符 |