A:现在我们将研究函数这个概念。
其实这个概念很简单,和中学的时候学的y=f(x)来对照着看,对于一个参数x,会得到一个值y,就会发现它和C中的函数是一个道理。
只是C函数中可以包含不传入任何参数的函数。
A:函数可以被分为两大类:一类是库函数,一类是用户自定义函数。库函数包含了一些程序所需要的基本的需求,库函数毕竟不需要程序员再写了,减少了程序开发的难度和复杂度。
Q:以前的printf函数、scanf函数就是库函数吧。
A:是的。
A:库函数和用户自定义函数又可以根据函数参数个数分为无参函数和有参函数。
Q:有参函数我明白,如果要计算两个数的和,需要传递过来两个加数;那么无参函数呢?这种形式有什么特别意义吗?
A:函数,本质是什么,只是做一件事,那么做这件事的时候就一定要被传递过来参数吗?当然不一定了。
如下,返回值为void的函数,后面的参数列表为空。
void printHello()
{
printf("Hello!");
}
此函数的实例代码:
#include <iostream>
using namespace std;
void printHello()
{
printf("Hello!");
}
int main()
{
printHello();
return 0;
}
Q:那函数的基本格式是什么?
A:
返回值类型 函数名(参数列表)
{
说明部分;
执行部分;
}
用个加法的函数来对比:
int add(int x,int y)
{
int sum=x+y;
return sum;
}
Q: return sum; 代表什么?
A:这个return语句是配合函数的返回值int的;因为函数有返回值,这里就是告诉编译器这个函数的返回值就是return后面的表达式。
Q:所以上面返回值为void的 printHello()函数里面就不必有return语句了?
A:是的。但是,标准的函数格式中还是要这个return语句的,格式如下:
return ;
这样也代表了什么值也没返回。
#include <iostream>
using namespace std;
void printHello()
{
printf("Hello!");
return ;
}
int main()
{
printHello();
return 0;
}
Q:那么printHello()函数中括号内的参数列表可以用void吗?
A:是的,可以,void表示空。
#include <iostream>
using namespace std;
void printHello(void)
{
printf("Hello!");
return ;
}
int main()
{
printHello();
return 0;
}
【void类型到底是什么类型】
void类型,就是空类型。它可以表示是函数的参数类型,可以是函数的返回值类型,那么它可以定义自己的变量吗?
void a;
这里的a是什么?它是空类型,空的就代表什么也没有,那么a还有什么用?
况且这么声明编译也会出错:
更别说尝试下用别的类型变量给它赋值了。
那么void的作用究竟是什么?也许它仅仅是一种意义上的作用。
【void *类型】
虽然void看起来没什么大用处,但是它和一个表示指针的*号在一起就可以表达很重要的作用。void *代表着可以指向任何数据类型的指针,它同样可以作为函数的返回值类型和函数的参数类型。或者说,用这种类型声明的参数类型或返回值类型可以用任何指针类型来代替,它起到了模板的作用!
void *pv;
int i=1;
int *pi=&i;
pv=pi; //这种赋值是完全可以的!当然,反过来就不可以了!
但是注意,这个时候pv并不是变成了int *类型了,它依然是自己的类型void *,只是可以强制转化成int *类型来打印该要的数据。
下面会出现编译错误:
cout<<*pv<<endl; //打印pv指向的数据的值
下面编译就正确而且执行正确:
cout<<pv<<endl; //打印pv的值
cout<<(int *)pv<<endl; //打印pv所对应的整型指针的值,即是此时pv的值
cout<<*(int *)pv<<endl; //打印pv指向的整型数据的值
动态内存申请函数malloc函数的原型的返回值就是void *类型。
void *malloc(int n);
这个表示申请n字节的内存空间,而一般都有个保存如果成功申请而得到的指针,可以是
int *类型、char *类型、double *类型等等。
当然得有强制类型转化。
int *pi=(int *)malloc(40);
这表示申请了40个字节的整型数据空间;
double *pd=(double *)malloc(40);
这表示申请了40个字节的double类型数据空间。
Q:add函数int add(int x,int y)的参数列表表明有两个整型参数,那么可以简略写成int x,y吗?即把这个地方变成:int add(int x,y) 这样可以吗?
A:这样写是不可以的。如果这么写,会出现如下编译的错误:
函数的参数列表明确要求是得每个参数都是类型名+变量名。
或许这样能让程序员更明确这里是两个这种类型的变量,而不是像声明变量那样。
Q:为什么一定要这样呢?
A:
Q:那么很多代码中main函数的最后会有个return 0,main函数的返回值传递给谁?
int main()
{
//代码
return 0;
}
A:传递给操作系统。
Q:为什么是传递给操作系统?
A:main函数是程序执行时的入口点,也是程序执行结束的出口点;正如main函数可能调用别的函数,别的函数也有个return语句(假设此函数有返回值的时候),那么这个return语句把结果给了创建它的main函数,main函数只是把结果给了创建它的操作系统。
Q:0代表着是什么结果?
A:说到这里,这又要从操作系统说起,程序在计算机里执行,是要受操作系统统一管理的,而操作系统对这样的情况很感兴趣:这个程序执行到最后是正常了还是异常了,这里就用0或是非0(一般用1来表示);返回0代表正常,返回1代表不正常;
#include <iostream>
using namespace std;
int main()
{
cout<<"Thanks!"<<endl;
return 0;
}
#include <iostream>
using namespace std;
int main()
{
cout<<"Thanks!"<<endl;
return 1;
}
Q:那么操作系统得到了这个信息又有什么用呢?
A:单看执行,没什么区别。实际,这个值的最直接作用是给操作系统一个交代:告诉操作系统,这个执行的程序执行完之后是正常结束的还是非正常结束的。正常的就返回0,异常的就返回1.那么操作系统需要这个值做什么呢?第一方面:操作系统能得到程序运行正常与否的信息,可以更好的管理计算机内部的各种资源;二来:
Q:那么返回值可以是float或char等类型吗?
A:其实这个并没什么影响,只是人们更习惯用int类型而已;
#include <iostream>
using namespace std;
float main()
{
cout<<"Hello!"<<endl;
return 1.5;
}
#include <iostream>
using namespace std;
char main()
{
cout<<"Hello!"<<endl;
return 'a';
}
都可以正常执行。
返回值用int其实只是一种约定形成的规范,int看起来更容易处理点;
Q:main函数也可以用void吧?
A:是的。
#include "iostream.h"
void main()
{
cout<<"Hello "<<endl;
}
虽然这样可以正确编译和执行,但是推荐在main函数中加入return返回值,毕竟这是标准。
Q:既然,main函数的返回值可以是void,那么这个void可以省略吗?
A:问的好。如下:
#include "iostream.h"
main()
{
cout<<"Hello "<<endl;
}
但是有个警告:
C语言有个函数返回值的默认类型,如果不加为int.
#include "iostream.h"
main()
{
cout<<"Hello "<<endl;
return 0;
}
编译和执行均正确。
这说明main还是在没有写返回值是什么类型的时候,也默认为int类型的。
#include <iostream>
using namespace std;
int main()
{
cout<<"ChenXi"<<endl;
}
这个程序会产生一个编译时的警告:定义main函数的时候有int型的返回值,但是结果没有返回值;假定返回值为void.
Q:什么是返回值的默认返回类型?
A:这个是这样的,如果你声明了一个函数,但是没有明确表示此函数的返回值类型,那么C编译器默认此函数返回值为int类型。但是,C++编译器一般都不允许一个函数不显式声明它的返回值类型。
#include <iostream>
using namespace std;
add(float x,float y)
{
return x+y;
}
int main()
{
cout<<add(12.4,10.5)<<endl;
return 0;
}
add函数并没有明确标识它的返回类型:当然,在C中被默认为int类型。
add(12.4,10.5)的计算是先得到了22.9这个值,那么这都被当作return里的值了,为什么最后还是被变成了22?
当然,这要从编译器角度去说了:编译器发现add函数返回值是int,当然它只会将函数内部得到的任何值强制转换成整型,那么得到的22也就不是意外了。
Q:以前说过那个add函数,那么add函数可不可以放到main函数中去定义?
#include "iostream.h"
int main()
{
int add(int x,int y)
{
return x+y;
}
int sum=add(3,5);
cout<<sum<<endl;
return 0;
}
A:为什么你会想到把add函数的定义放到main函数中呢?
Q:觉得这样挺简洁的!
A:不过这个会编译错误:
看起来似乎也有可以的可能;然而,实际上这样是绝对不可以的;
当然原因就在于程序的执行时的方式是在调用函数的时候会找到函数的入口点,然后依次去执行,如果在一个函数中定义了另外一个函数,那么执行的时候该怎么算呢?这个将使得执行变得很混乱,所以,这样一条规定要记牢: 函数定义不可以在别的函数中。
当然,有的程序设计语言支持嵌套定义函数。
Q:既然main函数有返回值,那么平常用的cout<<输出一个数据,那么它有返回值吗?
A:当然有的。cout是一个输出流类的对象, << 是一个运算符,只不过是被重载了;
如下代码:
#include <iostream>
using namespace std;
int main()
{
int i;
cout<<(cin>>i)<<endl;
return 0;
}
这个将会是什么结果呢?
在老版本的IDE中,这里面的2是输入的变量i的值,结果得到的似乎是个地址。
我们来看看cin输入整型数的时候调用的函数(此位于Include文件夹下istream文件中):
_Myt& operator>>(int& _X)
{iostate _St = goodbit;
const sentry _Ok(*this);
if (_Ok)
{long _Y;
const _Nget& _Fac = _USE(getloc(), _Nget);
_TRY_IO_BEGIN
_Fac.get(_Iter(rdbuf()), _Iter(0), *this, _St, _Y);
_CATCH_IO_END
if (_St & failbit || _Y < INT_MIN || INT_MAX < _Y)
_St |= failbit;
else
_X = _Y; }
setstate(_St);
return (*this); }
我们发现它返回的是此对象的引用。
下面代码是对add函数代码的简化版:
#include "iostream.h"
int add(int a,int b)
{
int sum;
sum=a+b;
return sum;
}
int main()
{
int sum,a=1,b=2;
sum=add(a,b);
cout<<sum<<endl;
return 0;
}
只不过是个简单的函数调用;
下面的呢?
#include "iostream.h"
int add(int a,int b)
{
return a+b;
}
int main()
{
int sum,a=1,b=2;
sum=add(a,b);
cout<<sum<<endl;
return 0;
}
只是函数的return语句那出现了不一样,但是当然结果是不会变的;
第二种当然要简洁多了。
使用函数前,必须至少先声明,定义可以放到后面再做。
#include "iostream.h"
int main()
{
int i=1;
void Message(int);
Message(i);
return 0;
}
void Message(int n)
{
cout<<n<<endl;
}
我们发现void Message(int);出现在代码中,不再以前看到main函数的初始部分;
它是什么?
它是一个声明,表明存在这个函数Message,需要被使用的;
我们也得出一个结论:要使用一个函数,只要在使用之前声明了就可以了;不必要非得在main函数的开始部分才可以声明;
其实这也很符合编译器的思路,有了声明语句,编译器自然将声明的东西放进符号表,留后面需要的时候调用,当然这个顺序是可以随意的,只要在使用之前声明就可以了。
#include <iostream>
using namespace std;
int main()
{
int a=1,b=3;
cout<<a<<endl;
cout<<b<<endl;
int add(int x,int y);
cout<<add(a,b)<<endl;
return 0;
}
int add(int x,int y)
{
return x+y;
}
可以看出,这个程序对函数的声明是放在代码中间的,并不是以往的放在main的最开始;
当然,它可以顺利通过编译,运行正常。原因是什么?就是它是在函数具体要使用的前面,只要这些就够了:编译器就能知道程序员的意思了!
【exit函数】
#include <iostream>
using namespace std;
int main()
{
void pf(int x);
int x;
cin>>x;
pf(x);
cout<<x<<endl;
return 0;
}
void pf(int x)
{
if(x==0)
exit(0);
}
这个是关于exit用法的程序,pf函数内部实现的是如果输入的x的值是0,那么就退出!
那么这个退出是算退出这个函数了呢,还是整个程序了呢?
可以看出来,exit函数很彻底,它可以被放置在函数的任何位置,直接调用就可以退出这个程序的执行!而,用于函数返回的只是那个retrun语句,大家要记牢。
现在介绍一个强制结束程序的函数:
#include <iostream>
using namespace std;
int main()
{
cout<<"Hello "<<endl;
abort();
int i=3;
cout<<i<<endl;
return 0;
}
这个函数是abort函数,它的作用就是强制结束程序,不管程序是什么状态。
我们可以发现,abort函数是在打印Hello之后出现的,后面还要打印的i就没办法打印了!
而,我们把abort函数改为return 1; 结果就不一样了!
#include <iostream>
using namespace std;
int main()
{
cout<<"Hello "<<endl;
return 1;
int i=3;
cout<<i<<endl;
return 0;
}
它当然不会出现第一个的不正常程序结束的标识!
#include <iostream>
using namespace std;
int main()
{
cout<<"Hello "<<endl;
exit(0);
}
#include <iostream>
using namespace std;
int main()
{
cout<<"Hello "<<endl;
return 0;
}
这两个程序有什么不同呢?
微风不燥,阳光正好,你就像风一样经过这里,愿你停留的片刻温暖舒心。
我是程序员小迷(致力于C、C++、Java、Kotlin、Android、iOS、Shell、JavaScript、TypeScript、Python等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。
欢迎关注。助您在编程路上越走越好!