技术标签: 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.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态
文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境
文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn
文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker
文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机
文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk
文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入
文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。 Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。
文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动
文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计
文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;gt;Jni-&amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图
文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法