技术标签: C语言 c语言 visualstudio 程序人生
目录
(4)写一个函数,每调用一次这个函数,就会将 num 的值增加1
函数是C语言的基本单位,在C语言程序中发挥着极其重要的作用
在维基百科中,函数的定义叫做子程序。
(1)一个大型程序中的某部分代码, 由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。
(2)一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软
件库。
(1)库函数:C语言内部提供的函数。
(2)自定义函数:自我发挥写出的函数。
我们在编写C语言代码的时候,总会频繁地使用一些功能:
比如:将信息按照一定的格式打印到屏幕上(printf)、在编程的过程中我们会频繁的做一些字符串的拷贝工作(strcpy)、在编程是我们也计算,总是会计算n的k次方这样的运算(pow)......
像上面的这些基本的功能,在编写程序时经常会用到。所以C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。
库函数的使用不需要专门去记,我们可以通过查找了解它们的使用方式。
这里推荐一个网站和一个应用程序
(2)msdn
通过这些方式,我们可以查找到它们的信息,例如:函数名、形式参数、需要的头文件和返回值等必要的信息。
这些工具的语言都是英文,在学习编程的工程中我们需要学习英文,保证以后在第一时间可以了解计算机的最新技术。
自定义函数由程序员自主设计,和普通的函数一样有函数名、返回类型、形式参数等。
基本结构如下:
ret_type fun_name(para1, * )
{
statement;//语句项
}
ret_type 返回类型
fun_name 函数名
para1 函数参数
#include<stdio.h>
int islarge(int a, int b)
{
if (a>=b)
{
return a;
}
else
{
return b;
}
}
//上述为实现程序的函数
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int c = islarge(a, b);
printf("%d", c);
return 0;
}
//输入:10 20
//输出:20
错误示范:
#include<stdio.h>
void swap(int a,int b)
{
int temp = 0;
temp = a;
a = b;
b = temp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d,b=%d\n", a, b);
swap(a, b);
printf("交换前:a=%d,b=%d\n", a, b);
return 0;
}
//输入:10 20
//输出:
//交换前:a=10,b=20
//交换后:a=10,b=20
//
正确程序:
#include<stdio.h>
void swap(int* pa, int* pb)
{
int temp = 0;
temp = *pa;
*pa = *pb;
*pb = temp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d,b=%d\n", a, b);
swap(&a, &b);
printf("交换前:a=%d,b=%d\n", a, b);
return 0;
}
//输入:10 20
//输出:
//交换前:a=10,b=20
//交换后:a=20,b=10
//
这个程序我先不讲错在哪里,到后面形参的部分再详细解释。
真实传给函数的参数,叫实参。
实参可以是:常量、变量、表达式、函数等。
在调用函数时,它们都必须有确定的值,以便把这些值传送给形参。
形式参数是指函数名后括号中的变量。
形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。因此形式参数只在函数中才有效。
下面是函数在处理数据时的处理思路:
#include<stdio.h>
int islarge(int a, int b)
//int是返回类型,括号里的int a和int b
{
if (a>=b)
{
return a;
}
else
{
return b;
}
}
//上述为实现程序的函数
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);//输入a,b的值
int c = islarge(a, b);//islarge有两个实参a和b,定义变量c接收islarge函数的返回值
printf("%d", c);
return 0;
}
形参实例化之后其实相当于实参的一份临时拷贝。
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
所以,我们在不改变函数实参的时候可以使用传值调用。
比如,我们写一个程序计算两个整数的和:
#include<stdio.h>
int add(int x,int y)
{
return x+y;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int c= add(a,b);
printf("%d\n",c);
return 0;
}
在这个程序中,我们只是使用a和b进行操作,而没有改变a和b的数值等属性,这时我们就可以使用传值调用,再将操作得到的值返回。
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
#include<stdio.h>
void swap(int* pa, int* pb)
{
int temp = 0;
temp = *pa;
*pa = *pb;
*pb = temp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d,b=%d\n", a, b);
swap(&a, &b);
printf("交换前:a=%d,b=%d\n", a, b);
return 0;
}
在这个程序中,我们改变了a和b的数值,这时我们就需要使用传址调用,因为在传值调用中形参的改变是不会影响实参的。
讲到这里,我们讲一讲上面使用传值调用交换数值的程序错在哪里:
#include<stdio.h>
void swap(int a,int b)//返回类型为void表示不返回,此处的int a与int b表示形式参数和它们的类型
{
int temp = 0;//定义一个临时变量
temp = a;//把a的值赋给temp
a = b;//把b的值赋给a
b = temp;//把temp的值赋给b,完成交换操作
//注意,因为形参只是实参的一份临时拷贝,在整个函数中我们改变的只是实参,出函数后形参被销毁无法改变实参
}
int main()
{
int a = 0;//创建变量a
int b = 0;//创建变量b
scanf("%d %d", &a, &b);//输入数值
printf("交换前:a=%d,b=%d\n", a, b);//展示
swap(a, b);//交换函数,将a,b传进去
printf("交换前:a=%d,b=%d\n", a, b);//实参依旧是a和b的原始值,没有达到我们的目的
return 0;
}
打个比方:就好像老师在练习册上留作业,你确实是写了,就是写在了你同学的练习册上。虽然确实做了正确的事,但是做完了也没什么用,你的作业本依旧是空的。(PS:偷把别人作业写了,阻止他学习,内卷的高级境界)
传址调用的程序传递的是实参的地址,这是实参的本质属性。
#include<stdio.h>
void swap(int* pa, int* pb)//返回类型为void表示不返回,此处的int* pa与int* pb表示形式参数和它们的类型
{
int temp = 0;//定义临时变量
temp = *pa;//用地址找到实参a并赋给temp
*pa = *pb;
//把用地址找到的实参b赋给用地址找到的实参a
*pb = temp;//用地址找到实参b并赋给temp
//跳出函数时,被销毁的形参只是两个指针变量,此时实参的交换已经完成
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d,b=%d\n", a, b);
swap(&a, &b);//传入地址
printf("交换前:a=%d,b=%d\n", a, b);
return 0;
}
这次,我们也干同样的事情,比如写作业。但这次我们定位到了你自己的作业本上,就可以实现写作业的任务。
函数1:
int isprime(int x)//这个形参用于接收需要判断的数字
{
int i = 2;
for (i=2; i<x; i++)//从2到这个数字减一逐一试除
{
if (x%i == 0)//如果有能除开的就表明它不是素数,返回0
{
return 0;
}
}
return 1;//在除完所有的数字均除不开时,为素数返回1
}
这个程序是可以改进的
比如说,4×4=16,而2×8=16或8×2=16也成立,16×1=16或1×16=16依旧成立。
我们不难看出,被乘数和乘数一定会有一个大于等于这个积开根号,一个小于等于这个积开根号,那么我们只需要试除到根号下x就完全可以判断一个数字的是否为素数。
函数2:
#include<math.h>
int isprime(int x)
{
int i = 2;
for (i=2; i<=sqrt(x); i++)//sqrt表示对参数开平方
{
if (x%i == 0)
{
return 0;
}
}
return 1;
}
判定条件: 对于整百的年份,闰年必定是400的倍数 ;对于不是整百的闰年,闰年是4的倍数
函数1:
int isleap(int year)
{
if (year % 400 == 0)
{
return 1;
}
if (year%4==0)
{
if (year % 100 != 0)
{
return 1;
}
}
return 0;
}
我们把这两个条件集成一下,得到函数2
函数2:
#include<stdio.h>
isleap(int year)
{
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
//((year是4的倍数)并且(year不是100的倍数))或者(year是400的倍数)
{
return 1;
}
else
{
return 0;
}
}
int search(int arr[], int a, int sz)//形参为数组、需要查找的整数、数组的元素个数
{
int left = 0;
int right = sz - 1;
int mid = 0;
while (left <= right)
{
mid = left + (right - left) / 2;//找中间的元素
if (arr[mid] > a)//中间元素大于查找值,就从右缩小一半的范围
{
right = mid-1;//可以使用--mid,不推荐
}
else if (arr[mid] < a)//中间元素小于查找值,就从左缩小一半的范围
{
left = mid+1;//可以使用++mid,不推荐
}
else
{
return mid;//找到了,返回下标
}
}
if (left>right) //正常情况下不会出现
{
return -1;//找不到,返回-1
}
}
#include<stdio.h>
void test(int* p)//在主程序内定义一个变量储存调用的次数,因为需要改变变量的值,所以进行传址调用
{
printf("hehe\n");
(*p)++;//解引用找到变量再加1,注意这个括号不能忘
//否则,*p++就表示每次这个指针先向后移动4个字节,然后解引用
}
函数可以根据需要进行相互调用。
#include<stdio.h>
int main()
{
printf("Hello world\n");
return 0;
}
这是每一个初学者都会写的代码,我们先调用了main函数,然后在main函数的内部又调用了printf函数,这就是嵌套调用。
我们为了减少不必要变量的定义,可以直接把一个函数的返回值作为另一个函数的参数。
#include<string.h>
#include<stdio.h>
int main()
{
char arr[20] = "abcdef";
printf("%d", strlen(arr));
return 0;
}
strlen函数的返回值变成了printf函数的参数,这就把这两个函数像锁链一样串联起来,也就是链式访问。
这个程序的输出是什么?
#include<stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
答案:4321
printf这个函数的返回值是它打印字符的个数,首先进入最外层的printf函数
这层函数需要第二层函数printf("%d", printf("%d", 43))的返回值
而第二层的printf函数又需要第三层函数printf("%d", 43)的返回值
在执行完第三层的printf("%d", 43)函数后,返回打印字符的个数2
printf("%d", printf("%d", 2))
第二层得到返回值2,打印2,而此时第二层函数也返回它打出的字符的个数1
printf("%d", 1)
最后打印1,也就形成了4321的输出结果
(1)函数的定义是指函数的具体实现,交待函数的功能实现。相当于我们平常创建自定义函数的步骤。
(2)函数不能嵌套定义
错误的定义方法:
int add(int x,int y)//加法函数
{
return x + y;
int sub(int x, int y)//这个减法函数被嵌套定义在了加法函数内部,这种写法是错的
{
return x - y;
}
}
正确的定义方法:
int add(int x,int y)//加法函数
{
return x + y;
}
int sub(int x, int y)//减法函数
{
return x - y;
}
对于函数来讲,数数平等,不能搞特权。
函数的声明
我们在写代码的时候可能会想:我把所有的代码写在一个源文件中,这样找起来不就方便了吗。
其实,这样的习惯对日后程序的开发是不利的。
我们的社会是有各自的分工的,当我们在开发一个程序的时候,我们往往只需要负责一个大的工程中的部分内容,比如一个人去写主程序,一个人写函数等等,而我们将工程的各个部分分开就可以更快地快找到bug并对应修复。
这样,当我们写一个函数时,就需要这样的文件分配:
每一个函数都可以分成这两个文件编写,也可以几个函数写在两个文件中。
这里涉及到一个代码加密的问题,我会补充。
(1)递归的定义
程序调用自身的编程技巧称为递归( recursion)。表示一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
递归的主要思考方式在于:把大事化小
(2)递归的两个必要条件
接受一个整型值(无符号),按照顺序打印它的每一位
例如:输入:1234,输出 :1 2 3 4
#include <stdio.h>
void print(int n)
{
if(n>9)
{
print(n/10);
}
printf("%d ", n%10);
}
int main()
{
int num = 1234;
print(num);
return 0;
}
(1)思想层面:大事化小
我们如果想要得到一个数字的每一位,就需要我们先%10得到最后一位,后/10除去最后一位,因为/10最后一位为余数,可以继续向前查找,直到这个数字成为一个一位数停止程序(因为如果这里是个一位数,a/10的值就是0,我们并不想打印0的每一位),所以在这里我们定义一个函数print(),它可以按顺序打印每一个值。
分步解决就是这样:
print(1234)
print(123) 4
print(12) 3 4
(2)实践讲解
print(1234);//这个函数从上到下,先递进后回归
//1234大于9,进入if语句,第一层
print(1234)
{
if(n>9)//n=1234,满足条件,进入if
{
print(123);
}
printf("%d ", n%10);//第一层,a%10=4
}
//print(123)展开,n=123满足条件,继续进入下一层
print(123)
{
if(n>9)//a/10=123,满足条件,进入if
{
print(12);
}
printf("%d ", n%10);//第二层,a%10=3
}
//print(12)展开,a/10=1此时不满足条件,不会继续进入下一层的if语句
print(12)
{
if(n>9)//n=12,不满足条件,不进入if
{
print(1);
}
printf("%d ", n%10);//第三层,a%10=2
}
print(1)
{
if(n>9)//n=1,不满足条件,不进入if
{
print(0);
}
printf("%d ", n%10);//第三层,a%10=1
}
递归的“递”此时已经完成,我们将这个代码整理一下,查看它时如何“归”的
print(1234)
{
{
{
{
printf("%d ",n%10);//第四层,a%10=1
}
printf("%d ", n%10);//第三层,a%10=2
}
printf("%d ", n%10);//第二层,a%10=3
}
printf("%d ", n%10);//第一层,a%10=4
}
//代码从第四层开始向外执行,故可以实现数字的按位打印
//输出:1 2 3 4
(1)什么是迭代
迭代实际上就是重复,如果只讨论我们比较熟悉的程序设计操作,迭代在程序中就表示循环。
(2)函数递归和迭代的优缺点
函数递归中我们一层一层调用函数,它的优点是所需代码量少,简洁。但缺点主要有两个,一方面,大量重复的计算拖慢了程序的运行速度;另一方面,函数每一次被调用的时候都需要在栈区开辟相应的空间,当递归过深时可能会出现栈溢出。(栈区的空间已经被用完了,程序无法继续进行了)
当我们使用迭代时,循环不需要大量调用函数,重复的计算会少很多,这个程序的运行速度会加快不少,只是这个程序的代码量会大很多。(下面这个程序不是很明显,但也确实更短)
程序:应用递归求斐波那契数列的第n项
斐波那契数列:1 1 2 3 5 8 13 ...(规律:第一二项为1,后一项等于前两项的和)
递归程序:
#include<stdio.h>
int fib(int m)
{
int ret = 0;
if (m<=2)
{
ret = 1;//第一二项为1
}
else
{
ret = fib(m - 1) + fib(m - 2);//三项及三项以后,后一项等于前两项的和
}
return ret;
}
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d",fib(n));
return 0;
}
迭代程序:
#include<stdio.h>
int fib(int m)
{
if (m < 2)//前两项为1
{
return 1;
}
else//后两项为前两项之和
{
int i = 0;
int a = 1;
int b = 1;
int c = 0;
for (i=m; i>2; i--)
{
c = a + b;
a = b;//把原来的第二个数变成新计算中的第一个数
b = c;//把算出的结果变为新计算的第二个数
}
return c;
}
}
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d",fib(n));
return 0;
}
文章浏览阅读461次。_electron vue项目打开慢
文章浏览阅读229次。接1、2、3、4篇。10、安装mysql支持安装fedora15或者16系统时若选择安装mysql数据库,则必须自行安装mysql开发包。因为自带默认数据库不会安装这个包。否则会遇到mysql错误:ogr_mysql.h:34:23: fatal error: my_global.h: No such file or directory#问题原因:找不到mysql头文件..._linux gdal netcdf5
文章浏览阅读1.2k次。Linux tc qdisc 模拟网络丢包延时_tc qdisc
文章浏览阅读336次。linux64bit 安装 jdk 1.7下载地址 : https://edelivery.oracle.com/otn-pub/java/jdk/7u21-b11/jdk-7u21-linux-x64.rpm0. 卸载rpm版的jdk: #rpm -qa|grep jdk 显示:jdk-1.6.0_10-fcs 卸载:#rpm -e --nodep..._liunx64位得jdk1.7
文章浏览阅读552次。开始听到负载均衡的时候,我第一反应想到的是链路负载均衡,在此之前主要是在学习网络方面知识,像在NA、NP阶段实验做链路负载均衡时常会遇到,后来还接触到SLB负载分担技术,这都是在链路基础上实现的。 其实负载均衡可以分为硬件实现负载均衡和软件实现负载均衡。 硬件实现负载均衡:常见F5和Array负载均衡器,配套专业维护服务,但是成本昂贵。 软件实现负载均衡:常见开源免费的负载均衡软件有Ngin..._中间件应用场景nginx lvs proxy
文章浏览阅读4.7k次。多维时序 | MATLAB实现CNN-LSTM多变量时序预测目录多维时序 | MATLAB实现CNN-LSTM多变量多步预测基本介绍模型特点程序设计学习总结参考资料基本介绍本次运行测试环境MATLAB2020b,MATLAB实现CNN-LSTM多变量多步预测。模型特点深度学习使用分布式的分层特征表示方法自动提取数据中的从最低层到最高层固有的抽象特征和隐藏不变结构. 为了充分利用单个模型的优点并提高预测性能, 现已提出了许多组合模型。CNN 是多层前馈神经网络, 已被证明在提取隐藏_cnn可以进行多步预测
文章浏览阅读219次。3.1 用户配置文件和密码配置文件3.2 用户组管理3.3 用户管理3.4 usermod命令3.5 用户密码管理3.6 mkpasswd命令_polkitd:input 用户和组
文章浏览阅读670次。主成分分析,即Principal Component Analysis(PCA),是多元统计中的重要内容,也广泛应用于机器学习和其它领域。它的主要作用是对高维数据进行降维。PCA把原先的n个特征用数目更少的k个特征取代,新特征是旧特征的线性组合,这些线性组合最大化样本方差,尽量使新的k个特征互不相关。关于PCA的更多介绍,请参考:https://en.wikipedia.org/wiki/Prin..._inprementation python code of pca
文章浏览阅读35次。发一下牢骚和主题无关:地址类型:32位的cpu,共4G间空,其中0-3G属于用户间空地址,3G-4G是内核间空地址。用户虚拟地址:用户间空程序的地址物理地址:cpu与内存之间的用使地址总线地址:外围总线和内存之间的用使地址内核逻辑地址:内存的分部或全体射映,大多数情况下,它与物理地址仅差一个偏移量。如Kmalloc分..._linux 内存条与内存地址
文章浏览阅读1.3k次,点赞2次,收藏16次。什么是自动化测试? 做测试好几年了,真正学习和实践自动化测试一年,自我感觉这一个年中收获许多。一直想动笔写一篇文章分享自动化测试实践中的一些经验。终于决定花点时间来做这件事儿。 首先理清自动化测试的概念,广义上来讲,自动化包括一切通过工具(程序)的方式来代替或辅助手工测试的行为都可以看做自动化,包括性能测试工具(loadrunner、jmeter),或自己所写的一段程序,用于_自动化测试中baw库指的什么
文章浏览阅读1.6w次。A0纸指的是一平方米大小的白银比例长方形纸(长为1189mm宽为841mm)。A0=1189mm*841mm A1=841mm*594mm 相当于1/2张A0纸 A2=594mm*420mm 相当于1/4.A1图纸大小尺寸:841mm*594mm 即长为841mm,宽为594mm 过去是以多少"开"(例如8开或16开等)来表示纸张的大小,我国采用国际标准,规定以 A0、A1、A2、.GB/T 14..._a0图纸尺寸
文章浏览阅读966次。最终效果图:UI说明:针对table本身进行增强的tree table组件。 tree的数据来源是单元格内a元素的自定义属性:level和type。具体代码如下:Java代码 DepartmentEmployeeIDposi_treetable canvas