本次内容针对B站内C语言网课回顾学习内容为function,函数部分
首先介绍两个函数-strcpy、-memset:
strcpy -----> string copy :字符串拷贝
形似strlen ------> string length :字符串长度
memset -----> memory set :内存设置
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "Gwwen";
char arr2[20] = "***********";
// Gwwen\0
strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
-strcpy(arr2, arr1)的意思就是将arr1的字符串内容拷贝到字符串一内,如上述代码块所示,由于字符串会有\0的结束标志,如果F10调试,可以监视到arr2内的内容其实为Gwwen\0********,但打印只会显示Gwwen。
请注意,需要在开头声明#include<string.h>否则可能会报错
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "hello, world";
memset(arr, '*', 5);
printf("%s\n", arr);
// *****,world
return 0;
}
我对memset(arr , '*' , 5 )的理解就是将arr中的前5个字节用*来代替。另附百度百科介绍如下:
将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作。
同上,需要#include<string.h>
实际参数与形式参数
#include<stdio.h>
void swap1(int x, int y)
{
int temp = 0;
temp = x;
x = y;
y = temp;
}
void swap2(int* x, int* y)
{
// *x 为解地址操作,实际上为指针x存储的内容
int tep = 0;
tep = *x;
*x = *y;
*y = tep;
}
int main()
{
int a = 20;
int b = 10;
printf("a = %d, b = %d\n", a, b);
//swap1(a, b); // 传值调用
swap2(&a, &b); // 传址调用
printf("a = %d, b = %d\n", a, b); // 由于a、b和函数体内的x、y地址不同,无法做出交换
return 0;
}
如上述代码块所示,为了明确实际参数与形式参数的区别与使用,设计一段代码,需要用函数的形式来实现两个变量交换其值。
根据所学,分别使用swap1(传值调用)与swap2(传址调用)。
error:swap1是错误示范,因为当我们在主函数内申请了两个变量,他们会分别被计算机赋予两个地址。
swap1仅仅把这两个变量的值赋予给了函数,而函数内接收这两个值的临时变量又向计算机申请了两个新的地址,因此,当我们在函数内调换了两个变量的值后,由于在函数内的临时变量x与y的地址与主函数中的a与b不匹配,我们在主函数中打印的值不会发生变化。
相反,如果我们使用swap2,将a与b的地址传给函数,函数接收这两个变量的地址,再通过解地址操作进行变量的交换,这样可以使得函数内的变量地址与主函数内的变量地址相匹配,最终在打印中完成需求。
综上,当实参传递给形参的时候,形参只是实参的一份临时拷贝,对形参的修改不会改变实参,参考swap1。
下面给出上课时的对于这些定义的笔记解释
实际参数(实参):
真是传递给函数的参数,叫实参,实参可以是:常量、变量、表达式、函数等。
无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传递给形参。
形式参数(形参):
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数
形式参数当函数调用完成之后就自动销毁了。
因此形式参数只在函数中有效。
传值调用:
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
传址调用:
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数形式
这种传参方式可以让函数和函数外边的变量建立真正的联系,也就是函数内部可以直接操作函数外部的变量。
通过对函数的大致了解,更改之前的一道练习题:利用函数找出100到200之间的素数
#include<stdio.h>
int is_prime(int x)
{
int y = 0;
for (y = 2; y < x; y++)
{
if (x % y == 0) // 除得尽,不是素数,返回0
{
return 0;
}
/*else // 回答:根据调试发现,只要x % y != 0 都会直接跳到return 1
//而此时y = 2,没有达到2至y-1皆模
{
return 1; // 这个地方的错误是什么?
}
如果 x % y != 0 ,那么返回1,不就意味着此时x是素数嘛*/
}
return 1; // 我可以直接return 1,而不用再写一个判断条件,请问这样操作是得体的嘛?
}
int main()
{
int i = 0;
int count = 0;
for (i = 100; i <= 200; i++)
{
if (is_prime(i) == 1)
{
printf("%d ", i);
count++;
}
}
printf("\ncount = %d\n", count);
return 0;
}
在is_prime函数内部犯了一个错误:如果在if语句判断为非素数的else下直接return1,会出现bug,根据调试后发现,在代码运行到if语句时,判断当前x为非素数之后就直接认定此数为素数,这样违背了设立for循环的初衷:让2到x-1的数都除一遍x。
因为此时只有2除了x,3到x-1都直接因为else语句而跳过了,因此在这种情况下,代码执行完,里面所有的数都是无法被2整除的数。而并非素数。
只要将return1放到for循环外面就可以解决这个问题。
另外,在之前的作业中,还需要判断for循环结束之后,x与y是否相等,只有相等,才能认为x是素数,但在函数中,由于只需要返回特定值,我们不用多此一举,可以直接return 1。
同样的,再更改一道练习题:求1000年至2000年之间的闰年
#include<stdio.h>
int is_leap_year(int y)
{
if ((y % 4 == 0 && y%100 != 0) || (y % 400 == 0))
{
return 1;
}
else
{
return 0;
}
}
int main()
{
int count = 0;
int year = 0;
for (year = 1000; year <= 2000; year++)
{
//判断是否为闰年
if (1 == is_leap_year(year))
{
count++;
printf("%d ", year);
}
}
printf("\n count = %d\n", count);
return 0;
}
这个代码内容非常清晰,在函数内部的return 0和return 1理由也与之前一致。
接下来的一题包含指针的使用,是之前作业中的二分查找
#include<stdio.h>
// 本质上这里的arr是个指针
int binary_search(int arr[], int k,int sz)
{
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = (left + right) / 2;
if (arr[mid] < k)
{
left = mid + 1;
}
else if (arr[mid] > k)
{
right = mid - 1;
}
else
{
return mid;
}
}
return -1;
}
int main()
{
// 二分查找
// 在一个有序数列中查找具体某个数
// 如果找到了,返回这个数的下标,如果找不到,返回-1
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 7;
int sz = sizeof(arr) / sizeof(arr[0]);
// 传递过去的是数组首元素的地址
int ret = binary_search(arr, k,sz);
if (ret == -1)
{
printf("can't find it\n");
}
else
{
printf("find it ,the number is : %d\n", ret);
}
return 0;
}
在最开始的编写中,我并没有在-binary_serach函数中申请sz变量,我将arr与k传过去,企图在函数内部定义sz,进行sz = sizeof(arr)/sizeof(arr[0])的操作,结果失败了。
根据调试发现,arr是一个数组,老师上课讲过,通过函数传递数组变量时候,传递过去的是数组首元素的地址,因此,我们必须在主函数内部计算完sz,再通过函数直接传递sz变量。而不能在函数内部计算。
以上就是对于function内容的初步学习,希望自己能继续加油!