zhyDaDa的个人站点

目录


引入

求 0-10 和 20-30 和 35-45 的和

#include <stdio.h>
void main()
{
    int i, sum;
    for (sum = 0, i = 1; i <= 10; i++)
    {
        sum += i;
    }
    printf("%d到%d的和是%d\n", 1, 10, sum);

    for (sum = 0, i = 20; i <= 30; i++)
    {
        sum += i;
    }
    printf("%d到%d的和是%d\n", 20, 30, sum);

    for (sum = 0, i = 35; i <= 45; i++)
    {
        sum += i;
    }
    printf("%d到%d的和是%d\n", 35, 45, sum);
}

代码复制是程序质量不良的表现!

这是因为以后做代码维护的时候要修改很多处

#include <stdio.h>
void sum(int begin, int end){
    int i;
    int sum = 0;
    for (i = begin; i <= end; i++)
    {
        sum += i;
    }
    printf("%d到%d的和是%d\n", begin, end, sum);
}

void main()
{
    sum(1, 10);
    sum(20, 30);
    sum(35, 45);
}

将重复部分提取出来, 方便以后的调用

定义与使用

什么是函数?

函数是一块代码, 接收零个或多个值, 做一件事情, 并返回零个或一个

函数的定义

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

void 是返回类型
sum 是函数名
int begin, int end 是参数表
花括号内的代码块 称为函数体

函数的调用

函数名(参数值)

注意: ()起到了重要的作用
+ ()表示前面的表达式是一个函数并且在调用ta, 而不是一个函数指针
+ 即便没有参数也要()
+ 有参数的话, 需要给出正确的 数量和顺序
+ 这些值会按照顺序给函数参数表中的参数(相当于变量)初始化赋值

函数的返回

函数知道每一次是哪里调用了ta, 会返回到正确的地方

函数的原型

函数定义与调用先后关系的讲究

要在调用函数的上面定义函数, 原因如下:
+ C的编译器以自上而下的顺序分析代码
+ 在遇到调用的函数的时候, ta需要知道其定义
+ 即, 参数的 个数/类型/顺序
+ 这样才能检查调用是否正确

如果定义语句放在调用之下(且不做声明), 其结果要视编译器而定

比如, 如下的代码会发出警告, 在某些严格的编译器上甚至会出现编译失败

#include <stdio.h>
void main()
{
    sum(1, 10);
    sum(20, 30);
    sum(35, 45);
}

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

原因: 当编译器读到调用位置时, 会做出猜测, ta猜测sum函数的声明是如下的样子

int sum (int, int)

但当ta读到sum定义时发现与之不符, 于是报出了类型冲突的警告
不过有些高级编译器能自行纠正, 仍旧能正确运行代码

联系上述分析, 如果做出如下修改就可以正确运行

#include <stdio.h>
void main()
{
    sum(1, 10);
    sum(20, 30);
    sum(35, 45);
}

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

不过, 编译器仍旧会报出形如 “这个声明是我自己猜的哈” 这样的warning

函数的原型与声明

函数的原型用于告诉编译器一个函数的长相, 包括:
+ 名称
+ 参数(数量以及类型)
+ 返回类型

函数的声明就是展示函数原型的语句
通俗来讲, 声明就是告诉编译器 “你不用猜了”

#include <stdio.h>
void sum(int begin, int end);

void main()
{
    sum(1, 10);
    sum(20, 30);
    sum(35, 45);
}

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

注意:
+ 函数在声明之后, 遇到定义时, 会再次检验两者是否一致
+ 现在的编程习惯是将函数的原型声明写在调用位置函数的外面
+ 原型中可以不用写函数的名称(即便与定义不一样也不会warning), 但一般仍旧会写上(这对于人类阅读者是有意义的)

参数传递

可以传递给函数的值是表达式的结果, 这就包括了:
+ 字面量
+ 变量
+ 函数的返回值
+ 计算的结果

类型不匹配

调用函数时给的值与参数类型不匹配是C语言传统上最大的漏洞
编译器会自作主张地帮你转换类型, 但总会不尽如人意

传参

C语言在调用函数的时候, 永远只能传值给函数
注意: 每个函数都有自己的变量空间
参数也位于这个独立的空间中, 和其他函数没有关系
习惯上, 参数表中的参数称作 “形参” , 调用函数时传入的是 “实参”
但容易产生误解, 误认为把参数传给了函数
因此, 推荐称他们 “参数”“值”

本地变量

函数的每次运行, 就会产生一个独立的 变量空间
在这个空间中的变量是只有这次运行独有的, 称作 本地变量(也可以成为局部变量/自动变量)

之所以有局部和本地的区别是因为英文都是local
之所以叫自动变量, 是因为ta的生存期是自动的

注意:
+ 定义在函数内部的变量就是本地变量
+ 参数也是本地变量

变量的生存期和作用域

生存期 变量开始出现到消亡
作用域 在(代码的)什么范围内可以访问这个变量(即可以其作用)
即一对大括号内的范围

#include <stdio.h>
void swap(int x, int y)
{
    int t = x;
    x = y;
    y = t;
}
void main()
{
    int a = 5, b = 6;
    swap(a, b);
    printf("a=%d, b=%d\n", a, b);
}

上述函数的结果是:
a=5, b=6

当进入swap函数的时候:

ab仍旧存在, 即在生存期内
ab不在作用域内, 无法在当前上下文去访问他们

作为生存来说, 他们还在
作为作用来说, 他们不在当前作用域内

当返回main函数的时候:

txy都不存在了
因为swap函数的运行已经结束了

本地变量的规则

  1. 本地变量是定义在块内的
  2. 既可以定义在函数的块内
  3. 也可以定义在语句的块内

语句可以是forif之类
也可以随意拉一对大括号!

  1. 程序运行进入这个块之前, 其中的变量不存在
    离开这个块, 其中的变量就消失了
  2. 块外面定义的变量在里面仍旧有效
  3. 块里面定义了和外面同名的变量, 则以块内的为准(会掩盖外侧的)
  4. 不能在一个块内定义同名的变量
  5. 本地变量不会被默认初始化
  6. 参数在进入函数的时候就被初始化了(一定要给函数参数要求的值 以初始化)

补充

定义无参数的函数

如果写做void f(void)没有任何问题
在传统C中, 如果写做void f(), 编译器会认为传参不确定, 而非不传参

好心的编译器又会帮你猜想, 然后把事情搞糟
当然最糟糕的是, 不报错

总之, 写void f(void)不会有错

关于int main()的事儿, 很复杂, 在下面讨论

逗号运算符

之前有提到, 逗号,也是一种运算符, 那和调用函数时用到的,如何区分?
+ 调用函数时, 圆括号中的逗号,标点符号
+ 在独立的圆括号内, 表示一个运算式, 此时逗号,运算符

两个参数: f(a,b)
一个参数: f((a,b))

函数里的函数

C语言不允许函数嵌套定义
可以在函数里放函数的声明, 但不能放ta的BODY

奇形怪状

int i, j, sum(int a, int b);
这里定义的sum 函数返回为 int
不算错, 当然不推荐
return (i)
返回 i 值而已
但会误使人认为return是一个函数, 因此也不推荐

关于main函数

  • 首先, int main()也是一个函数, ta被称为程序的入口
  • 写成int main(void)不会有错
  • return 的值是有意义的!!!

在Windows中, 用bat执行这一C语言程序, 会得到return的值
如果用if errorlevel 1...就可以了解错误原因

一般来说, 返回0表示函数正常的运行了
其他非零返回值用来表明错误原因

Avatar photo
我是 zhyDaDa

前端/UI/交互/独立游戏/JPOP/电吉他/游戏配乐/网球/纸牌魔术

发表回复