技术标签: 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;
}
文章浏览阅读1.3w次。转载自 http://www.miui.com/thread-2003672-1-1.html 当手机在刷错包或者误修改删除系统文件后会出现无法开机或者是移动定制(联通合约机)版想刷标准版,这时就会用到线刷,首先就是安装线刷驱动。 在XP和win7上线刷是比较方便的,用那个驱动自动安装版,直接就可以安装好,完成线刷。不过现在也有好多机友换成了win8/8.1系统,再使用这个_mt65驱动
文章浏览阅读1k次。SonarQube是一个代码质量管理平台,可以扫描监测代码并给出质量评价及修改建议,通过插件机制支持25+中开发语言,可以很容易与gradle\maven\jenkins等工具进行集成,是非常流行的代码质量管控平台。通CheckStyle、findbugs等工具定位不同,SonarQube定位于平台,有完善的管理机制及强大的管理页面,并通过插件支持checkstyle及findbugs等既有的流..._sonar的客户端区别
文章浏览阅读3.4k次,点赞2次,收藏27次。神经图灵机是LSTM、GRU的改进版本,本质上依然包含一个外部记忆结构、可对记忆进行读写操作,主要针对读写操作进行了改进,或者说提出了一种新的读写操作思路。神经图灵机之所以叫这个名字是因为它通过深度学习模型模拟了图灵机,但是我觉得如果先去介绍图灵机的概念,就会搞得很混乱,所以这里主要从神经图灵机改进了LSTM的哪些方面入手进行讲解,同时,由于模型的结构比较复杂,为了让思路更清晰,这次也会分开几..._神经图灵机方法改进
文章浏览阅读2.8k次。一、模型迭代方法机器学习模型在实际应用的场景,通常要根据新增的数据下进行模型的迭代,常见的模型迭代方法有以下几种:1、全量数据重新训练一个模型,直接合并历史训练数据与新增的数据,模型直接离线学习全量数据,学习得到一个全新的模型。优缺点:这也是实际最为常见的模型迭代方式,通常模型效果也是最好的,但这样模型迭代比较耗时,资源耗费比较多,实时性较差,特别是在大数据场景更为困难;2、模型融合的方法,将旧模..._模型迭代
文章浏览阅读2.3k次。1、前言上传图片一般采用异步上传的方式,但是异步上传带来不好的地方,就如果图片有改变或者删除,图片服务器端就会造成浪费。所以有时候就会和参数同步提交。笔者喜欢base64图片一起上传,但是图片过多时就会出现数据丢失等异常。因为tomcat的post请求默认是2M的长度限制。2、解决办法有两种:① 修改tomcat的servel.xml的配置文件,设置 maxPostSize=..._base64可以装换zip吗
文章浏览阅读1k次,点赞17次,收藏22次。Opencv自然场景文本识别系统(源码&教程)_opencv自然场景实时识别文字
文章浏览阅读1.3k次。拷贝虚拟机文件时间比较长,因为虚拟机 flat 文件很大,所以要等。脚本完成后,以复制虚拟机文件夹。将以下脚本内容写入文件。_exsi6.7快速克隆centos
文章浏览阅读2k次。本文主要实现基于二度好友的推荐。数学公式参考于:http://blog.csdn.net/qq_14950717/article/details/52197565测试数据为自己随手画的关系图把图片整理成文本信息如下:a b c d e f yb c a f gc a b dd c a e h q re f h d af e a b gg h f bh e g i di j m n ..._本关任务:使用 spark core 知识完成 " 好友推荐 " 的程序。
文章浏览阅读367次。南京大学高级程序设计期末复习总结,c++面向对象编程_南京大学高级程序设计
文章浏览阅读3.1k次,点赞2次,收藏12次。实现朴素贝叶斯分类器,并且根据李航《统计机器学习》第四章提供的数据训练与测试,结果与书中一致分别实现了朴素贝叶斯以及带有laplace平滑的朴素贝叶斯%书中例题实现朴素贝叶斯%特征1的取值集合A1=[1;2;3];%特征2的取值集合A2=[4;5;6];%S M LAValues={A1;A2};%Y的取值集合YValue=[-1;1];%数据集和T=[ 1,4,-1;..._朴素贝叶斯 matlab训练和测试输出
文章浏览阅读1.6k次。Markdown 文本换行_markdowntext 换行
文章浏览阅读6.7w次,点赞2次,收藏37次。win10 2016长期服务版激活错误解决方法:打开“注册表编辑器”;(Windows + R然后输入Regedit)修改SkipRearm的值为1:(在HKEY_LOCAL_MACHINE–》SOFTWARE–》Microsoft–》Windows NT–》CurrentVersion–》SoftwareProtectionPlatform里面,将SkipRearm的值修改为1)重..._错误: 0xc0000022 在运行 microsoft windows 非核心版本的计算机上,运行“slui.ex