C语言程序设计笔记---009
C语言函数篇章
前言:
(1)数学中的函数:
一元一次:f(x) = 2x + 1
二元一次:f(x,y) = x + y
(2)C语言中:
同样可以实现函数的功能以及应用
/知识点汇总/
1、函数的定义
(1)定义:
a、函数就是在计算机科学中的子程序。
b、由一个或多个语句块组成,负责完成某香特定的任务,具有独立性。
c、一般会有输入参数并有返回值,提供某些功能的封装等。
d、函数是C语言的基本单位,在C语言程序中发挥着极其重要的作用。
(2)C语言中函数的分类:
a、库函数
b、自定义函数
(3)补充:
a、编译器Debug模式为调试模式–程序员
b、编译器Release模式为发布模式—生成.exe文件
1.1、库函数:
a、前言:当程序中一些函数功能,频繁且大量的用到,那么把这些函数集中放在一个函数库里,则称为库函数。
b、概念:封装函数的集合
c、作用:库函数的作用是,方便调用和可移植性和提高程序的效率。
库函数的使用:
不需要专门去记,我们可以通过查找了解它们的使用方式。
这里推荐一个网站和一个应用程序
(1)www.cplusplus.com
(2)msdn
通过这些方式,我们可以查找到它们的信息,例如:函数名、形式参数、需要的头文件和返回值等必要的信息。
这些工具的语言都是英文,在学习编程的工程中我们需要学习英文,保证以后在第一时间可以了解计算机的最新技术。
常用的库函数:
a、I/O口函数 如:printf、scanf、getchar、putchar…
b、字符串操作函数 如:strcmp、strlen…
c、字符操作函数 如:toupper…
d、内存操作函数 如:memcpy、memcmp、memset…
e、时间/日期函数 如:time…
f、数学函数 如:sqrt、pow…
g、其他库函数
熟悉通过参考文档,学习库函数:
以strcpy()和memset()函数举例:
strcpy
char* strcpy (char* destination , const char* source) ;
memset
void* memset (void* ptr , int value , size_t num) ;
注意:使用库函数时,一定要在程序前声明对应的头文件。
1.1.1、库函数例程1
说明:strcpy()函数
#include <stdio.h>
#include <string.h>//调用strcpy()需要调用的头文件
int main()
{
char arr1[20] = {0};
char arr2[] = "hello world";
strcpy(arr1,arr2);//将arr2存储arr1的内容,即左复制给右
printf("%s\n",arr1);
//打印arr1这个字符串
//%s---以字符串的格式打印
return 0;
}
1.1.2、库函数例程2
说明:memset()函数
#include <stdio.h>
#include <string.h>//调用memset()函数需调用的头文件
int main()
{
char arr[] = "hello bit";
memset(arr,'x',5);
//翻译内存设置,将arr的前5个字符,设置存入x代替hello
printf("%s\n",arr);
return 0;
}
1.2、自定义函数:
概念:自定义函数由程序员自主设计,和普通的函数一样有函数名、返回类型、形式参数等。
格式:函数返回类型 函数名 函数参数
//函数的基本组成:
ret_type fun_name(paral,*)
{
statement;//语句项//函数体
}
//ret_type 返回类型
//fun_name 函数名
//paral 函数参数
1.2.1、自定义函数例程1
说明:写一个函数,找出两个数的较大值
/*例程1.2.1*/
#include <stdio.h>
//定义一个比较大小值的函数
int get_max(int x,int y)
{
if (x > y)
{
return x;//返回较大值
}
else
{
return y;
}
}
int main()
{
int a = 10;
int b = 20;
//函数的调用
int get_max = (a, b);
printf("max = %d\n",get_max);
return 0;
}
1.2.2、自定义函数例程2
说明:写一个函数交换两个整数的内容
易错:地址空间紊乱
/*例程1.2.2*/
#include <stdio.h>
//定义一个交换数据的函数
//void说明该函数不需要返回值
//只需要交换变量
void swap1(int x, int y)
{
int t = 0;
t = x;
x = y;
y = t;
}
int main()
{
int a = 10;
int b = 20;
printf("交换前:a = %d b = %d\n",a,b);
swap1(a,b);
printf("交换后:a = %d b = %d\n",a,b);
//printf("交换后:a = %d b = %d\n",x,y);
//不可以直接返回x,y,因为x,y是局部变量,作用域只可在自己函数内执行
//并且,return不可一次性返回,两个参数
return 0;
}
图示:
改错前:先复习指针的操作,利用取地址操作解决
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;//取出a的地址给指针变量pa,int类型相同
*pa = 20;//*pa解引用,给该地址赋值20,因为*pa = a等价
printf("%d\n",a);//20
return 0;
}
改错:地址空间紊乱,利用取地址操作解决
#include <stdio.h>
//定义一个交换数据的函数
//void说明该函数不需要返回值
//只需要交换变量
//swap1在被调用的时候,实参传给形参,其实形参是实参的临时拷贝
//所以改变形参,无法改变实参
//通过指针的操作,使变量在符合的地址上操作变量交换
void swap2(int* pa, int* pb)
{
int t = 0;
t = *pa;//因为地址相同*pa = a
*pa = *pb;//因为地址相同*pb = b
*pb = t;
}
int main()
{
int a = 10;
int b = 20;
printf("交换前:a = %d b = %d\n", a, b);
swap2(&a, &b);
printf("交换后:a = %d b = %d\n", a, b);
return 0;
}
图示:
2、函数的参数
函数的参数分类:实参、形参
实参:
a、真实传给函数的参数,叫实参。
b、实参可以是:常量、变量、表达式、函数等。
c、不论实参是什么类型,在进行函数调用时,都必须是确定的值,以便把这些值传给形参。
形参:
a、形式参数是指函数名后括号中的变量,因为形参只有在函数被调用时才分配内存单元,所以叫形式参数。
b、形参与局部变量相提并论,只有在自身函数有效,执行或调用完成后自动销毁。
2.1、函数的实参/形参例程
/*例程2.1*/
//形参int x, int y
void swap1(int x, int y)//传值调用
{
int t = 0;
t = x;
x = y;
y = t;
}
//形参int* pa, int* pb
void swap2(int* pa, int* pb)//传址调用
{
int t = 0;
t = *pa;//因为地址相同*pa = a
*pa = *pb;//因为地址相同*pb = b
*pb = t;
}
//实参a, b
printf("交换前:a = %d b = %d\n", a, b);
swap1(a, b);
printf("交换后:a = %d b = %d\n", a, b);
//实参&a, &b
printf("交换前:a = %d b = %d\n", a, b);
swap2(&a, &b);
printf("交换后:a = %d b = %d\n", a, b);
3、函数的调用
函数的调用:传址调用、传值调用
传值调用(单向传递):函数的形参和实参占用不同的内存块,对形参的修改不会影响实参。
传址调用(双向传递):是指把函数外部创建变量的内存地址传递给函数参数的一种调用方式,这种传参方式,可以让函数和函数外边的变量建立起联系,也就是函数内部可以直接操作函数外部的变量。
3.1、函数的调用例程1
/*例程3.1*/
#include <stdio.h>
//定义一个交换数据的函数
//void说明该函数不需要返回值
//只需要交换变量
void swap1(int x, int y)
{
int t = 0;
t = x;
x = y;
y = t;
}
void swap2(int* pa, int* pb)
{
int t = 0;
t = *pa;//因为地址相同*pa = a
*pa = *pb;//因为地址相同*pb = b
*pb = t;
}
int main()
{
int a = 10;
int b = 20;
printf("交换前:a = %d b = %d\n", a, b);
swap1(a, b);//传值调用
swap2(&a, &b);//传址调用
printf("交换后:a = %d b = %d\n", a, b);
return 0;
}
3.2、函数的调用例程2
说明:写一个函数可以判断是不是素数(素数只能由1和本身整除的数)
/*例程3.2*/
#include <stdio.h>
//判断是否为素数的函数
//要求函数功能简洁、单一、独立,方便移植共享
int in_prime(int n)
{
int j = 0;
//2---n-1的数试除
for (j = 2; j < n; j++)
{
if (n%j == 0)
{
return 0;//不是素数返回0
}
}
if (j == n)
{
return 1;//素数返回1
}
}
int main()
{
int i = 0;
printf("请输入一个整数:");
scanf("%d",&i);
//调用判断是否为素数的函数
if (in_prime(i) == 1)
{
printf("是素数\n");
}
else
{
printf("不是素数\n");
}
return 0;
}
3.3、函数的调用例程3
说明:写一个函数判断是否为闰年
/*例程3.3*/
#include <stdio.h>
//判断是否为闰年的函数
int years(int x)
{
if ((x % 4 == 0) && (x % 100 != 0) || (x % 400 == 0))
{
return 0;
}
else
{
return 1;
}
}
int main()
{
int year = 0;
printf("请输入年份:");
scanf("%d",&year);
//调用year()函数
if (years(year) == 0)
{
printf("%d:", year);
printf("是闰年\n");
}
else
{
printf("%d:", year);
printf("不是闰年\n");
}
//if ((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0))
//{
// printf("%d:", year);
// printf("是闰年\n");
//}
//else
//{
// printf("%d:", year);
// printf("不是闰年\n");
//}
return 0;
}
3.4、函数的调用例程4
说明:写一个函数,实现一个整型有序数组的二分查找
/*例程3.4*/
#include <stdio.h>
#include <string.h>
//利用二分查找确认下标的函数
//形参数:数组用char a[]数组接收,返回值是数组下标所以是int binary_search不是char,
//int k与实参一样表示被找数,实参名与形参名可相同
//int s = int len数组的大小
//本质的写法char* a地址,int binary_search(int* a,int k,int s)
//所以int len = sizeof(arr) / sizeof(arr[0]);语句需要先计算好再传参
int binary_search(char a[],int k,int s)
{
//定义左右下标
int left = 0;
int right = s - 1;
//报错:int len = sizeof(a) / sizeof(a[0]);//char a[]本质上是地址
while (left<=right)
{
int mid = (left + right) / 2;
if (a[mid] > k)
{
right = mid - 1;
}
else if (a[mid] < k)
{
left = mid + 1;
}
else
{
return mid;
}
}
return -1;
}
int main()
{
int k = 7;
char arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,10 };
int len =sizeof(arr)/sizeof(arr[0]);//求出数组长度,元素个数
//调用二分查找函数,找到返回被找数的下标
//找不到返回EOF-1
//实参数:在有len个元素的arr数组中找k
//数组arr传参,实际传递的不是数组本身
//仅仅是传过去的是数组首元素的地址
int ret = binary_search(arr,k,len);
if (-1 == ret)
{
printf("找不到\n");
}
else
{
printf("找到了,下标是:%d\n",ret);
}
return 0;
}
3.5、函数的调用例程5
说明:写一个函数实现,调用一次num加1一次
/*例程3.5*/
#include <stdio.h>
//实现加1的功能
//改变函数外的变量,需用指针变量
void add(int* p)//传址调用
{
(*p)++;
}
int main()
{
int num = 0;
while (1)
{
add(&num);
printf("%d\n", num);
if (num == 100)
{
break;
}
}
return 0;
}
4、函数的嵌套调用与链式访问
函数的嵌套:函数可嵌套调用,但不可嵌套定义,函数是相互独立的。
链式访问:将一个函数的返回值作为另一个函数的参数。
4.1、函数的嵌套例程1
/*例程4.1*/
#include <stdio.h>
void test1()
{
printf("hehe\n");
}
int test2()//函数的调用嵌套
{
test1();
return 0;
}
int main()
{
test2();//函数的调用嵌套
return 0;
}
4.2、函数的链式访问例程1
/*例程4.2*/
#include <stdio.h>
#include <string.h>
int main()
{
int len = strlen("abc");
printf("%d\n",len);
//链式访问:将一个函数的返回值作为另一个函数的参数
printf("%d\n", strlen("abc"));
char arr1[20] = { 0 };
char arr2[] = "bit";
//strcpy(arr1, arr2);
//printf("%s\n",arr1);
//链式访问:将strcpy(arr1, arr2)函数的返回值,直接作为printf()函数的参数
printf("%s\n",strcpy(arr1,arr2));
return 0;
}
4.3、函数的链式访问例程2
/*例程4.3*/
#include <stdio.h>
int main()
{
//printf()查MSDN可知,返回值是屏幕打印的字符个数
printf("%d", printf("%d", printf("%d", 43 )));//4321
return 0;
}
5、函数的声明与定义
函数的声明:
a、一般出现在函数的使用之前,要满足先声明后使用。
b、函数的声明一般要放在头文件中的。
c、函数的声明主要的目的在于告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。
5.1、函数的声明与定义例程
/*例程5.1*/
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
//所以需要在执行前对add()函数先声明,再使用
int add(int x, int y);//声明函数
int c = add(a, b);
printf("%d\n",c);
return 0;
}
//调用的函数放主函数后面时
//main()读取到add(a,b)会有警告
//因为在main()之前未扫描到add()函数
int add(int x, int y)
{
return x + y;
}
6、程序的模块化编写
程序的模块化编写:
a、方便程序的功能函数集成以及函数的调用
b、模块化编程使得分工明确,大的项目可有多人分开编写
c、最后集成起来,加快的项目完成的工期,节约了程序员的人力资源
/通常写一些频繁大量的功能函数时,就需要分配文件:
函数声明放在——头文件.h
函数定义使用放在——源文件.c
每一个函数都可以分成这两个文件编写。/
/程序的模块化编写说明/
以写一个计算器程序为例子,分为四个模块
A程序员:写加法部份,add.c add.h
B程序员:写减法部份,sub.c sub.h
C程序员:写乘法部份,…
D程序员:写除法部份,…
E:最后将ABCD集成在主函数文件中,执行对应功能
/程序的模块化编写例程:为了更好理解,见同工程目录下的add.c add.h main.c中/
/程序的模块化编写:兼职/实际应用/
比如:分享或者贩卖自己的技术文件
假设A不会写计算器,想购买一个成品
B会编写程序,但是不想开源,那么就可以体现模块化编程的好处了
具体怎么体现模块化优势呢?
1.不想卖源码:那么只需要卖程序.h头文件,不卖.c源文件;
2.在工程目录点击属性—配置属性—常规—配置类型—应用程序–改为:静态库
3.静态库:由二进制组成的机器语言,这样操作后,只需要卖.lib静态库文件和.h头文件,既可以实现功能,也可以不开源源码
4.相当于加密售卖,需要注意的是,卖静态库要正常使用实现功能,那么需要先将.h头文件和.lib静态库文件,添加进客户的工程文件里,再在工程文件main().c文件开头,使用#pragma comment(lib,“sub.lib”)在文件开头编译导入一下即可。
导入静态库命令:#pragma comment(lib,sub.lib);
7、参考文献
感谢:部分知识参考学习于该博主写得很好很详细。
原文链接:https://blog.csdn.net/qq_65285898/article/details/124417677