C语言问答进阶--6、函数(1)

news2024/11/25 2:35:03

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等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。

欢迎关注。助您在编程路上越走越好!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2036039.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自学嵌入式第十九天高级编程篇 文件2

标准IO的返回值判断 feof&#xff1a;判断文件流指针是否到达结尾。 feof(src) //如果文件流指针到达末尾&#xff0c;函数返回值为真 ferror&#xff1a;判断文件流指针是否出错。搭配clearerr使用可以跳过报错的地方&#xff0c;读取后面的数据 ferror(src) //如…

短视频SDK解决方案,良好的二次开发可扩展性

短视频已成为当代社交与内容消费的重要载体&#xff0c;其影响力与日俱增&#xff0c;面对这一蓬勃发展的市场&#xff0c;如何高效、创新地打造短视频应用&#xff0c;成为众多开发者和企业的核心关切。在此背景下&#xff0c;美摄科技凭借其深厚的技术积累与前瞻性的市场洞察…

奇异值分解(SVD)

1 奇异值分解(SVD)简介 Beltrami 和 Jordan 被认为是奇异值分解&#xff08;Singular Value Decomposition&#xff0c;SVD&#xff09;的共同开创者&#xff0c;二人于19世纪70年代相继提出了相关理论。奇异值分解主要解决的问题是数据降维。在高维度的数据中&#xff0c;数据…

什么是流批一体?怎样理解流批一体?

目录 一、流式处理与批量处理概述 1.流式处理 2.批量处理 3.流批一体的定义 二、流批一体的关键特点 三、流批一体的技术实现 四、应用场景 五、实施流批一体的考虑因素 流批一体听起来很简单&#xff0c;但内涵却十分复杂。它包含了计算语义、编程模型、API、调度、执行、shuf…

Halcon玩转机器视觉专栏特殊声明

欢迎来到 PaQiuQiu 的空间 本文为【Halcon玩转机器视觉专栏特殊声明】&#xff0c;方便大家更合理的订阅! &#x1f4e2; ~特殊声明~ 鉴于很多童鞋在订阅专栏过程中&#xff0c;对于专栏中涉及到的资料&#xff08;比如中文学习手册&#xff09;和源码&#xff08;C#联合Halco…

背包九讲(动态规划)

文章目录 01背包问题题目描述解题思路&#xff1a;上代码&#xff1a;思路2&#xff1a;二维代码&#xff1a;优化代码&#xff1a; 完全背包问题题目描述&#xff1a;解题思路&#xff1a;二维代码&#xff1a;优化代码&#xff1a; 多重背包问题题目描述&#xff1a;解题思路…

有趣的rce漏洞复现分析(1)

目录 eval长度限制突破 第一种方法 第二种方法 无字母数字webshell之命令执行 php7 php5 eval长度限制突破 php eval函数参数限制在16个字符的情况下&#xff0c;如何拿到webshell呢 首先&#xff0c;我们还是先把环境搭好&#xff08;此次的所有漏洞环境我都部署在Ubu…

MySQL的索引事务和JDBC编程

目录 索引 查看索引 创建索引 删除索引 底层数据结构&#xff08;这个很重要哦&#xff0c;面试容易问&#xff09; 事务 事务的使用 事务的基本特性 并发执行事务可能产生的问题 MySQL提供的四种事务隔离级别 JDBC编程 JDBC的来源&#xff08;一定要了解&#xff…

[WUSTCTF2020]颜值成绩查询

打开题目 输入1 输出 输入1会提示学号不存在 输入1/**/or/**/11#,过滤了空格。 1/**/order/**/by/**/3# 存在 1/**/order/**/by/**/4# 不存在 绕过 爆破表名 -1/**/Union/**/Select/**/1,2,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/tabl…

8.1.数据库基础技术-数据库基本概念

数据库基本概念 数据库系统概述三级模式两级映射概念模式内模式外模式三级模式两级映像练习题 数据库设计练习题 数据库系统概述 数据&#xff1a;是数据库中存储的基本对象&#xff0c;是描述事物的符号记录。 数据的分类&#xff1a;文本、图形、图像、音频、视频。 数据库…

Unity动画模块 之 2D IK(反向动力学)

本文仅作笔记学习和分享&#xff0c;不用做任何商业用途 本文包括但不限于unity官方手册&#xff0c;unity唐老狮等教程知识&#xff0c;如有不足还请斧正​ 1.什么是IK 反向动力学 IK&#xff08;Inverse Kinematics&#xff09;是一种方法&#xff0c;可以根据某些子关节的最…

C++初阶:内存管理详解

✨✨小新课堂开课了&#xff0c;欢迎欢迎~✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C&#xff1a;由浅入深篇 小新的主页&#xff1a;编程版小新-CSDN博客 1.C/C内存分布 我们先来看下面一段代码和相…

Haproxy简介及配置详解

一、Haproxy简介 官网&#xff1a; 企业版网站: https://www.haproxy.com 社区版网站: http://www.haproxy.org github: https://github.com/haprox Haproxy是法国人Willy Tarreaus开发的一款开源软件&#xff0c;能够提供高性能、负载均衡以及基于HTTP和TCP应用个代理&…

微信自动回复的设置

如何在微信上高效回复客户&#xff0c;提供良好的用户体验是很重要的。 但常常因为一人管理太多号&#xff0c;消息回复不过来&#xff1b;同时太多客户咨询&#xff0c;手忙脚乱&#xff1b;回复的话术让人感到不专业。 没关系&#xff0c;小编有办法。给大家分享几个小技巧…

【聚类算法】

聚类算法是一种无监督学习方法&#xff0c;用于将数据集中的数据点自动分组到不同的类别中&#xff0c;这些类别也称为“簇”或“群”。聚类的目标是让同一簇内的数据点尽可能相似&#xff0c;而不同簇之间的数据点尽可能不相似。聚类算法广泛应用于多种领域&#xff0c;如数据…

NASA:ARCTAS_AircraftRemoteSensing_P3B_AATS14_Data

ARCTAS P-3B Aircraft AATS14 Data 简介 ARCTAS_AircraftRemoteSensing_P3B_AATS14_Data 包含在 "从飞机和卫星观测对流层成分的北极研究"&#xff08;ARCTAS&#xff09;任务期间通过 P-3B 飞机上的艾姆斯 14 通道机载跟踪太阳光度计&#xff08;AATS14&#xff0…

初始化React Native项目

node 版本 高于16版本会出现错误 Error: error:0308010C:digital envelope routines::unsupported (Node.js v19.4.0) openssl 3.0版本与node 版本不一致冲突 react-native 初始化项目版本为 镜像使用淘宝镜像源 npx nrm use taobao npx react-native0.67 init FirstApp …

vue 中使用 lodash Debounce防抖不生效

需求&#xff1a;搜索按钮增加防抖功能 代码1 <template><el-button type"primary" icon"el-icon-search" click"searchClick">搜索</el-button> </template><script> import { debounce } from "lodash&q…

使用Nvm切换nodeJs高版本之后,使用npm install一闪而过

先说现象,最近又有几个项目接手,其中有一个使用NVM切换至高版本node后,出现如下症状; 没有任何提示,然后翻看文件目录,node_modules目录没有创建,同时在全局 npm config set prefix 设置的目录下 多了一个 pgn的快捷,指向项目目录。 使用百度或者chart-gtp,搜索到的答案…

VSCODE ESP-IDF 内置 JTAG 接口断点单步调试笔记

环境配置 下载VSCODE之后&#xff0c;安装VSCODE的ESP-IDF插件。 还可安装c/c与python的语言插件 进行配置&#xff1a;ctrlshiftP 打开配置选项&#xff0c;输入ESP-IDF:Configure ESP-IDF extension 根据情况选择即可&#xff1a; 点击“Configure Tools” 出现…