在C语言学习中,我们知道它是面向过程进行编程的,强调的是功能行为,其主要框架为:数据结构+算法。在此也可以理解成:数据+函数。其实,函数在C语言学习中无时无刻不在使用,最为简单的#include<stdio.h>,这便是我们程序的开头,也是我们所调用的第一个函数,称为:库函数。里面包含着许多功能供我们直接进行使用,不需要在重复的进行编辑程序来实现一些重复率高的功能,如printf,scanf,况且这些函数的编辑也并不一定都是简单的。当我们的需求是特定的,没有现成的库函数供我们再来挥霍,这就需要我们进行自定义函数来完成我们的目标。接下来有5个小题来加深我们对函数的理解和认识!
练习题目:
1.实现一个函数,打印乘法口诀表,口诀表的行数和列数自己指定,输入9,输出9*9口诀表,输出12,输出12*12的乘法口诀表。
2.使用函数实现两个数的交换。
3.实现一个函数判断year是不是润年。
4.创建一个数组,实现函数init()初始化数组、实现empty()清空数组、实现reverse()函数完成数组元素的逆置。要求:自己设计函数的参数,返回值。
5.实现一个函数,判断一个数是不是素数。
题目分析及代码、结果展示:
1.输出9*9乘法表在上次习题处理已经练习过了,重点要采用循环的思想去寻找他们行数、列数之间的关系,从而得到循环的初值及限值条件,那么这个循环就能够跑起来,也就达到了我们的目标。但是,要是我们想随意的输出3*3、5*5、19*19的乘法表呢?难道我们需要每次都重复一遍上述的编码过程吗?我匿名反对这样做!!!这3个要求相似度极高,定然有他们的共同之处。我们可以从函数的角度考虑下,它们都是输出乘法表,这个可以作为它们的对应法则,要求输入的行数不同,也就是他们的自变量X不一样,得到的因变量Y也不一样。这样我们只需要得到这个对应法则,只要我们给定了X,自然可以得到我们想要的Y,这样9*9乘法表也就推广到i*i乘法表了!
#include<stdio.h>
void print_table(int line) //注意函数类型
{
int i = 0;
int j = 0;
for(i=0; i<=line; i++)
{
for(j=1; j<=i;j++) //跳出条件
{
printf("%d*%d=%2d ", i, j, i*j);
}
printf("\n"); //换行位置
}
}
int main()
{
int line = 0;
scanf("%d",&line);
print_table(line);
return 0;
}
上面就是我们的源代码和所得结果!来打印一波“如意乘法表”吧!
在此我们需要注意到的是:
1. 函数的使用,对这个问题进行的便利在那里,我们可以任意打出想要行列数的乘法表,要正确认识函数的优点!
2. 要注意函数内部的换行操作的位置,由于大括号较多,这个逻辑一定要清楚!
回味:
函数的使用会很方便,每次再打印乘法表的时候,只需要拷贝一份函数再调用下就行了。但是,这样将函数的声明、定义都放在一个源文件里面是合理常见的做法吗?这样你的函数思想不全都泄露了吗?那怎么卖钱呢!!!函数的声明、定义、使用都是有其特殊的规则的,不是简简单单的全部放在文件的开头处。
2. 交换两数的值、交换两字符串的各个字符、交换两数组中的元素,看到交换我们大多都想到创建第三个变量,作为载体。当然也可使用异或操作符直接进行交换,无需第三个变量。但是,如何用函数进行两数的交换呢?函数体要是简简单单的写为 swap(int a,int b),怕是达不到你的预期目标。在此,要一定分清楚什么是形参、什么是实参。函数的调用有传值调用和传址调用两种形式,形参作为实参的一份临时拷贝,形参与实参占用不同的内存块,形参值的交换在此是无法对实参产生实质性的作用的,然而传址调用就可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
#include<stdio.h>
void f_swap(int *p, int *a)
{
int tmp = 0;
tmp = *p;
*p = *a;
*a = tmp;
}
int main()
{
int i = 2;
int j = 6;
printf("%d %d\n", i, j);
f_swap(&i, &j);
printf("%d %d\n", i, j);
return 0;
}
上面就是使用函数交换两整数值,结果显而易见!
在此我们需要注意到的是:
1. 在函数体中,使用了指针变量,指向了i,j的地址,这样的操作就是传址调用。
2. 函数名的写法需要见名知意(在此的函数名好像不规范)。
回味:
在交换两址采用函数的写法时,这才是最基本的函数的传址调用。也可以自己尝试尝试用最初的想法(创建第三变量,直接交换两值,不考虑地址),看看会不会交换?也可以进入监视看看真正的形参和实参在内存中的存储到底是什么个样子,加深对传址、传值调用的理解。
3. 使用函数判断是否为闰年,在这我们依旧考虑判断1000--2000之内的闰年,其实与判断是否为素数相同,仅需要改变函数体中的判断条件即可,在此就直接给出源代码以及结果了(偷个懒,详情可以看看昨天的博客)。
#include<stdio.h>
int is_year(int year)
{
if(((year%4==0)&&(year%100!=0)) || (year%400==0))
{
return 1;
}
else
{
return 0;
}
}
int main()
{
int year = 0;
int count = 0;
for(year=1000; year<=2000; year++)
{
if(is_year(year) == 1)
{
count++;
printf("%d ",year);
}
}
printf("\n");
printf("%d ",count);
return 0;
}
4. 3个函数分别有三个相应的功能进行处理,Init函数即初始化数组,就是将一个数组的所有元素按照我们的要求进行重新赋值。那对于函数要求就需要知道数组名、元素个数、初始化要求,这三个要求就足以完成这项操作了,对数组进行遍历赋值即可。Empty函数,清空数组,其实就是将数组中的各个元素全部赋值为0,也就是Init函数的一个特殊情况。Reverse函数要进行数组的逆置,逆置可不是倒序输出。将第一个元素和最后一个元素进行交换,将第二个元素和倒数第二个元素进行交换...就可以完成逆置。最左侧与最右侧交换一次,在各向中间移动一次,再进行上面的操作,就可以写成循环的结构了,当左侧>右侧那么就可以停下 这部分的操作了,逆置也就结束了。
#include<stdio.h>
void Init_arr(int arr[], int sz, int j)
{
int i = 0;
for(i=0; i<=sz; i++)
{
arr[i] = j;
}
}
void Empty_arr(int arr[], int sz)
{
int i = 0;
for(i=0; i<=sz; i++)
{
arr[i] = 0;
}
}
void Reverse_arr(int arr[], int sz)
{
int left = 0;
int right = sz-1;
while(left<right)
{
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
left++;
right--;
}
}
int main()
{
int i = 0;
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
int sz = sizeof(arr)/sizeof(arr[0]);
for(i=0; i<=sz; i++)
{
printf("%d",arr[i]);
}
Init_arr(arr, sz, 2);
printf("\n");
for(i=0; i<=sz; i++)
{
printf("%d",arr[i]);
}
Empty_arr(arr, sz);
printf("\n");
for(i=0; i<=sz; i++)
{
printf("%d",arr[i]);
}
Reverse_arr(arr, sz);
printf("\n");
for(i=0; i<=sz; i++)
{
printf("%d",arr[i]);
}
return 0;
}
上图就是我们3个函数的源代码以及结果,但是这运行时出现的这个问题,我也不知道怎么搞,在数组初始化的时候,后面那一圈是什么东西???还有这三个函数为什么都是11位了...
在此我们需要注意到的是:
1. 我们将3个函数写在了一起,在运行的时候是可以得到3个结果,但是我们就会看到第一张图的情况。第一个函数的使用已经对第二次函数使用产生了影响,要想得到逆置的函数时,可以先将函数Init、Empty先屏蔽掉,就会出现第二张图的结果,是正确的。
2. 在进行Reverse函数逆置操作时,使用left、right进行操作是及其普遍和重要的,也是我们必须要掌握的!!!
回味:
这个bug出现我也是始料未及,学的太过浅薄了。这三个函数倒是都比较常规,期间对于数组长度的计算要熟练使用sizeof来进行计算,但是在函数体外就不能在进行计算了,sizeof(arr)=4,它计算的是字节数,sizeof是一个操作符,不论是什么类型的数组,在函数中sizeof(arr)都等于4,因为arr其实是首元素的地址,也就是可以看作指针类型,所有的指针的所占字节数都是4/8(32/64),与数组其类型无关。
5.素数的函数实现,逻辑也是很简单,与普通一样,注意优化即可,在此我们判断100--200间的素数,也不再对其进行赘述了。
#include<stdio.h>
int is_prime(int n)
{
int i = 0;
for(i=1; i<n; i++)
{
if(n%i == 0)
{
return 0;
}
}
return 1;
}
int main()
{
int i = 0;
for(i=100; i<=200; i++)
{
if(is_prime(i) == 1);
{
printf("%d ",i);
}
}
return 0;
}
这个源代码以及结果也是昨天刚刚写过的,正确性可以保证!
在这还是强调下函数命名规则,真的好挫...