C语言_6 函数

简介: 是翁恺老师的132p C语言程序设计网课。

6.2.1 逻辑类型:表示关系运算和逻辑运算结果的量

bool类型
首先要包含头文件#include<stdbool.h>,然后可以使用bool,true,false。

bool b=6>5;

只要bool量不是0,都=1.

6.2.2 逻辑运算:对逻辑量进行与、或、非运算

逻辑运算是对逻辑量进行的运算,结果只有0或1。
逻辑量是关系运算或逻辑运算的结果。
|运算符|描述|示例|结果|
|--|--|--|--|
|!|逻辑非|!a|a的true或false反转|
|&&|逻辑与|a&&b|只有a&b都是true时结果才是true|
||||逻辑或|a||b|只有a&b都是false时结果才是false|

例:表达数学区间时,(4,6)或[4,6]:
同样像之前if一样,不可以写4<x<6这种式子。因为4<x的结果是一个逻辑值(0或1)
x>4&&x<6x>=4&&x<=6
判断一个字符是大写字母:c>='A'&&c<='Z'
优先级:!>&&>||
例:!done&&(count>MAX):done是0且count>MAX时结果为1

所有的优先级

优先级 运算符 结合性
1 () 从左到右
2 !,+,-,++,-- 从右到左(单目的+和-)
3 *,/,% 从左到右
4 +,- 从左到右
5 <,<=,>,>= 从左到右
6 ==,!= 从左到右
7 && 从左到右
8 \ \ 从左到右
9 =,+=,-=,*=,/=,%= 从右到左

短路:逻辑运算自左向右,如果左边足以决定结果,就不会进行右边的计算了
a==6&&b==1如果左边a!=6就终止了。
即:&&左边false就不做右边了;||左边是true就不做右边了。就算右边是赋值的话右边也不会做了。

int a=-1;
if(a>0&&a++>1){
    printf("OK\n");
}printf("%d\n",a);

这样输出结果a=-1,没有进行右边的a++。
所以不要把赋值和复合赋值组合进表达式!

6.2.3 条件运算符

条件运算符1:问号

cnt=(cnt>20)?cnt-10:cnt+10;
//格式:条件?条件满足时的值:条件不满足时的值

相当于

if(cnt>20)cnt-=10;
else cnt+=10;

这种条件运算符?的优先级高于赋值运算符,但是低于其他运算符,但是这样会很麻烦。
条件运算符自右向左,先计算右边的分支
不建议使用嵌套的条件表达式!太复杂,太难理解

条件运算符2:逗号
逗号是个运算符,连接两个表达式,并且以右边的表达式的值作为其结果。
逗号的优先级是最低的,所以两边的表达式会先运算,而右边的表达式的值留下来作为运算的结果。

i=3+4,5+6;//编译时会提示warning。因为赋值的优先级也要高于逗号,该式子其实是
(i=3+4),(5+6);//右边编译错误,i=7
如果写作i=(3+4,5+6);//编译时同样会warning,因为3+4没有用到;i=11

逗号表达式主要在for中使用

for(i=0,j=10;i<j; i++,j--)

目前来说,逗号表达式只有这一个用处。

7.1.1 初见函数

回想起区间[m,n]内素数求和的例子,对于每个数我们定义isPrime=1,如果最后仍然=1的话sum+=i.
这样让这一层循环显得很大,而这一层代码的功能很单纯
我们可以以函数的形式把这一堆代码取出来

int isPrime(int i)
{
    int ret=1;
    int k;
    for(k=2;k<i-1;k++){
        if(i%k==0){
            ret=0;
            break;//有1~i-1间的因数就可以判断i不是素数了,break节约时间
        }
    }
    return ret;
}

我们之前从第一个程序printf开始就是在用函数。只不过这次我们在自己定义了一个函数。
这样main里面很简洁,而且isPrime可以很方便地多次使用
PS: 程序中出现几段几乎完全相似的代码(除了字母不同),“代码复制”是程序不良的表现。因为将来做修改、维护的时候要维护很多处。
(吓得我立刻就去改大作业了)我们用函数替换,可以把重复的代码提出。

//闭区间内所有数求和的函数
void sum(int begin,int end)
{
    int i,sum=0;
    for(i=begin;i<=end;i++){
        sum+=i;
    }
    printf("%d到%d的和是%d\n",begin,end,sum);
}

int main()
{
   sum(1,10);
   return 0;
}

7.1.2 函数的定义和调用

函数是一块代码,接收0个/多个参数做一件事情,并返回0个/1个值。
可以先想象成数学中的函数y=f(x)。
调用函数时要写出 函数名(参数值);
()起到了表示函数调用的重要作用,即使没有参数我们也需要()。
不给括号的话,会给warning,而且函数不会被调用。
如果有参数,参数的数量、顺序都必须正确。而且函数知道每一次是哪里调用它,还会返回到正确的地方。

7.1.3 从函数中返回

int函数会有返回值return
return停止函数的执行,并返回一个值
return;
return 一个值;
可以写 c=函数();这样c=函数的返回值。
可以把这个值赋给变量/传给函数/丢掉。
没有返回值的函数:void,不能使用带值的return ,可以没有return 。调用的时候也不可以做返回值的赋值。

7.2.1 函数原型:用来告诉编译器这个函数长什么样

使用函数的先后顺序:先写函数再调用。在这里插入图片描述
可以先想象成数学函数y=f(x)
C的编译器自上而下分析你的代码,它会记住函数sum()长什么样子要几个参数,每个参数的类型如何,返回什么类型
如果把函数放在后面,C语言可能会猜,你在main函数里使用的这个函数是什么类型。如果后面发现后面的函数类型和它猜测的不符,就输出error
因此也可以先不用写完函数,光把一句函数头放到前面编译也能通过。
(事先声明了函数的样子)
下面函数还会判断一下和你之前的声明是否一致

void sum(int begin,int end);//声明

int main()
{
    //略去不表,其中用到了sum(a,b)
}

void sum(int begin,int end)//定义
{
    int i,sum=0;
     for(i=begin;i<=end;i++){
         sum+=i;
     }
     printf("%d到%d的和是%d\n",begin,end,sum);
}

函数头以分号结尾,就构成了函数的原型;
在函数里定义的参数类型与输入的变量的类型不一样,会发生自动类型转换。

double max(double a,double b);

在以前是把函数原型写在调用它的函数(main())里面的。
函数的原型里可以不写参数名,但一般仍然写上。

Void sum(int ,int)

不过对于人类读者,一般来说写上会更好理解。

7.2.2 参数传递:调用哪个函数的时候,是用表达式的值来初始化函数的参数

传递给函数的值可以是表达式的结果(max(a+b,c))
包括字面量、变量、函数返回值(函数调用里有函数)、计算结果
当调用函数的值与参数类型不匹配:编译器总是悄悄替你把类型转换好,但是这很可能不是你所期望的。这是C语言传统上最大的漏洞。
后续语言如c++/java在这方面会很严格。
这样的函数代码能交换a,b的值吗?

 void swap(int a,int b)//形参

int main()
{
   int a=5;
   b=6;
   swap(a,b);//实参
}
void swap(int a,int b)//形参
{
    int t=a;
    a=b;
    b=t;
}

不能。
C语言在调用函数时,永远只能传值给函数。
每个函数有它自己的变量空间,参数也位于这个独立的空间中,和其他函数没有关系。
后面递归还会再提。
过去对于函数参数表中的参数叫做形式参数。调用函数时给的数值叫做实际参数。
(见上图)
由于易误会,我们不再这么称呼。我们把参数表中叫做参数,调用函数的叫做值。

7.2.3 本地变量:定义在函数内部的变量是本地变量,参数也是本地变量

函数每次运行都会产生一个独立的变量空间,其中的变量是函数这一次运行所独有的。(本地变量)
所有我们定义在函数内部的变量就是本地变量。(我们现在学过的都是定义在函数内部的)我们写在函数参数表里的参数也是本地变量。

变量的生存期和作用域

生存期:这个变量出现和消亡的时间
作用域:在(代码的)什么范围内可以访问这个变量(这个变量可以起作用)
对于本地变量,这两个答案都是:大括号内(块)

void swap(int a,int b)//形参

int main()
{
  int a=5;
  b=6;
  swap(a,b);//实参
}
 void swap(int x,int y)//形参
 {
   int t=x;
   x=y;
   y=t;
 }

比如仍然是上例,进入swap函数之后(离开了自己的变量空间)a,b就没了。还在生存,但是不在当前的作用域了(显示:Not found in current context)
而回到原函数中之后,x,y,t就不存在了
所以在swap函数里交换a,b不会影响到原函数
本地变量的规则

  1. 本地变量定义在块内。可以是函数的块内,语句的块内,如:

if (a<b)int i=10;
离开if语句之后未定义使用i编译错误。(没声明)
程序运行进入这个块前,其中的变量不存在。离开后就消失了。
如果在块里定义了块外已经定义过的变量,就把块外的变量掩盖了;出来之后又回到块外的值。(C语言)
但是不能在块里面定义同名变量(多次定义)
本地变量不会被默认初始化,不会得到一个初始值。而参数进入函数的时候被初始化了。

7.2.4 函数庶事:一些细节,main()是啥

函数没有参数的时候写void f(void)。而写void f()在传统C语言中表示f的函数的参数表未知,并不表示没有参数。(编译器可能会猜测什么类型)
所以不要写空的括号
调用函数时的逗号和逗号运算符怎么区分?再加一层括号就是逗号运算符f((a,b))
C语言不允许函数嵌套定义。
(也最好不要写return (i);虽然意思上没变,但是会让人误会return 是个函数)
int main(void)也是个函数,所以return 0;也是有意义的。

目录
相关文章
|
5天前
|
存储 Serverless C语言
【C语言基础考研向】11 gets函数与puts函数及str系列字符串操作函数
本文介绍了C语言中的`gets`和`puts`函数,`gets`用于从标准输入读取字符串直至换行符,并自动添加字符串结束标志`\0`。`puts`则用于向标准输出打印字符串并自动换行。此外,文章还详细讲解了`str`系列字符串操作函数,包括统计字符串长度的`strlen`、复制字符串的`strcpy`、比较字符串的`strcmp`以及拼接字符串的`strcat`。通过示例代码展示了这些函数的具体应用及注意事项。
|
8天前
|
存储 C语言
C语言程序设计核心详解 第十章:位运算和c语言文件操作详解_文件操作函数
本文详细介绍了C语言中的位运算和文件操作。位运算包括按位与、或、异或、取反、左移和右移等六种运算符及其复合赋值运算符,每种运算符的功能和应用场景都有具体说明。文件操作部分则涵盖了文件的概念、分类、文件类型指针、文件的打开与关闭、读写操作及当前读写位置的调整等内容,提供了丰富的示例帮助理解。通过对本文的学习,读者可以全面掌握C语言中的位运算和文件处理技术。
|
8天前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。
|
13天前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
13天前
|
C语言
C语言 字符串操作函数
本文档详细介绍了多个常用的字符串操作函数,包括 `strlen`、`strcpy`、`strncpy`、`strcat`、`strncat`、`strcmp`、`strncpy`、`sprintf`、`itoa`、`strchr`、`strspn`、`strcspn`、`strstr` 和 `strtok`。每个函数均提供了语法说明、参数解释、返回值描述及示例代码。此外,还给出了部分函数的自实现版本,帮助读者深入理解其工作原理。通过这些函数,可以轻松地进行字符串长度计算、复制、连接、比较等操作。
|
14天前
|
SQL 关系型数据库 C语言
PostgreSQL SQL扩展 ---- C语言函数(三)
可以用C(或者与C兼容,比如C++)语言编写用户自定义函数(User-defined functions)。这些函数被编译到动态可加载目标文件(也称为共享库)中并被守护进程加载到服务中。“C语言函数”与“内部函数”的区别就在于动态加载这个特性,二者的实际编码约定本质上是相同的(因此,标准的内部函数库为用户自定义C语言函数提供了丰富的示例代码)
|
29天前
|
C语言
【C语言】字符串及其函数速览
【C语言】字符串及其函数速览
22 4
|
29天前
|
编译器 程序员 C语言
【C语言篇】从零带你全面了解函数(包括隐式声明等)(下篇)
⼀般情况下,企业中我们写代码时候,代码可能⽐较多,不会将所有的代码都放在⼀个⽂件中;我们往往会根据程序的功能,将代码拆分放在多个⽂件中。
|
1月前
|
测试技术 C语言
C语言中的void函数
C语言中的void函数
|
1月前
|
存储 安全 编译器
C语言中的scanf函数
C语言中的scanf函数