C语言day7 函数(一)


函数是C的building block,这一句话就说明了函数的重要性。
说起函数,无非就是返回值,形参实参(即函数之间的通信,实参把信息从主调传给被调,返回值把信息从被调传回主调)等等

define 声明的符号常量可以在程序的所有函数直接使用

#include <stdio.h>
#define WIDTH 40
void star();//特别容易忘记函数原型!!!忘记不会报错,但是会有警告
//这个star函数没有参数也没有返回值,所以不需要和主调函数通信
int main()
{
    star();
    printf("GIGATHINK, INC.\n");
    printf("101 Megabuck Plaza\n");
    printf("Megapolis, CA, 94904\n");
    star();
    return 0;
}
void star()
{
    for(int i=0;i<WIDTH;i++)//这里可以直接使用WIDTH
        printf("*");
    printf("\n");
}
****************************************
GIGATHINK, INC.
101 Megabuck Plaza
Megapolis, CA, 94904
****************************************

也可以把函数原型放在main函数里面声明变量处,经过尝试,发现这个原型放在哪里都行,连放在star函数里面都可以!!!

但是作为规范,还是放在main前面

另外,声明void star();相当于把参数的void省略了,但是返回值的void不可以省略,如果也省略程序会报错

#include <stdio.h>
#define WIDTH 40
void star();
int main()
{
    void star();
    star();
    printf("GIGATHINK, INC.\n");
    printf("101 Megabuck Plaza\n");
    printf("Megapolis, CA, 94904\n");
    star();
    return 0;
}
void star()
{
    for(int i=0;i<WIDTH;i++)
        printf("*");
    printf("\n");
}

很多规范实际上是为了和编译器交流
在这里插入图片描述

如果把返回值和参数的void都省略,那么函数定义的时候也要把二者都省略,虽然没报错,但是有警告,编译器

#include <stdio.h>
#define WIDTH 40
star();//声明
int main()
{
    star();
    printf("GIGATHINK, INC.\n");
    printf("101 Megabuck Plaza\n");
    printf("Megapolis, CA, 94904\n");
    star();
    return 0;
}

star()//定义
{
    for(int i=0;i<WIDTH;i++)
        printf("*");
    printf("\n");
}

不管怎么着,返回值的void还是要写的,否则编译器就会自动认为是int
在这里插入图片描述

把上面的显示改为居中,乍一看觉得这些程序都特别简单,但实际上也必须自己亲自动手编出来才行,在这个过程中,或发现很多错误,自己常犯的习惯性错误

  • define那三个符号常量字符串时竟然用的单引号,完全没发现,三个字符串都是单引号,而且下面的打印转换说明用的%c·····,要是面试现场编程犯这种低级错误,我就没工作了
  • 打印单个换行符,老是用双引号括换行符,写成"\n",好几次了,不要再犯这种低级错误了
#include <stdio.h>
#define WIDTH 40
#define NAME "GIGATHINK, INC"
#define ADDRESS "101 Megabuck Plaza"
#define PLACE "Megapolis, CA, 94904"

void star();
void show_n_spaces(int n);
int main()
{
    star();
    show_n_spaces(13);
    printf("%s.", NAME);
    show_n_spaces(13);
    putchar('\n');
    show_n_spaces(11);
    printf("%s", ADDRESS);
    show_n_spaces(11);
    putchar('\n');
    show_n_spaces(10);
    printf("%s", PLACE);
    show_n_spaces(10);
    putchar('\n');
    star();
    return 0;
}

void star()
{
    for(int i=0;i<WIDTH;i++)
        printf("*");
    printf("\n");
}
void show_n_spaces(int n)
{
    for(int i=0;i<n;i++)
        putchar(' ');
}

效果,程序的空格数都是自己算的····

****************************************
             GIGATHINK, INC.
           101 Megabuck Plaza
          Megapolis, CA, 94904
****************************************

形参:被调函数私有的局部变量;实参:主调函数传给被调函数的实际值

要会用C的专用术语表述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
我的猜测:对于printf 和scanf函数,函数声明应该会像ANSI C标准之前那样,直接不写参数的类型和个数,所以编译器才察觉不到错误,造成之前我们说的那种陷阱(下图最后一行红框)的。

在书上找到了确切答案:原来有部分原型这种方式,还有一个头文件专门处理这类函数
在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述

总之一个是变量,另一个是给这个变量赋的值

函数的黑盒视角

被调函数里面到底做了啥,主调函数是不可见的

主调函数做了什么,被调函数也是不知道且不关心的,它只需要主调函数把实际参数值传给自己就好了

编译器看到圆括号就知道前面的标识符是一个函数名

之前看到这句话印象很深刻,从来没想到圆括号竟然用的这么专一,也是第一次知道编译器会根据什么来获取信息,但是后来就一直在想,诶,if,while也要用圆括号啊,他们显然不是函数名,那么编译器怎么办?自己想通了,if之流是保留字关键字呀,编译器看到非保留字才认为是函数名

还有,强制类型转换符,以及算数和逻辑运算中很多时候也要用圆括号,编译器怎么办?很简单,这些case下,圆括号前面根本没有标识符呀哈哈哈哈

狡猾的编译器

return返回值把信息从被调函数传回主调函数

在这里插入图片描述

大概我们电脑中用的很多驱动程序也是这个意思?其实就是要去调用另一个重要的函数,more or less有那个意思吧

示例,返回两个数的较小值,

#include <stdio.h>

int imin(int, int);
int main()
{
    int x, y;
    printf("enter a pair of integers:(q to quit)\n");
    while(scanf("%d %d", &x, &y)==2)
          {
              printf("The lesser of %d and %d is %d\n", x, y, imin(x, y));
              printf("enter a pair of integers:(q to quit)\n");
          }
    printf("Bye!\n");
    return 0;
}

int imin(int x, int y)
{
    return (x<=y)?x:y;
}
enter a pair of integers:(q to quit)
2 45
The lesser of 2 and 45 is 2
enter a pair of integers:(q to quit)
3 6
The lesser of 3 and 6 is 3
enter a pair of integers:(q to quit)

我用while(scanf("%d %d", &x, &y)==2)还不是很熟,如果输入过多的数字,程序只看前两个数字

enter a pair of integers:(q to quit)
2 3 4
The lesser of 2 and 3 is 2

在这里插入图片描述

不写函数原型会怎么样?

这是一个例子,把函数原型注释掉,错误调用imin函数,一是输入参数数目不对,二是输入的参数类型不对,我发现不写函数原型,则第一种错误会被编译器报错,而第二种不会。

#include <stdio.h>
//int imin(int, int);
int main()
{
    printf("The lesser of %d and %d is %d\n", 3, 5, imin(3));
    printf("The lesser of %d and %d is %d\n", 3, 5, imin(3.0, 5.0));
    printf("Bye!\n");
    return 0;
}

int imin(int x, int y)
{
    return (x<=y)?x:y;
}

执行结果:错误的。
分析原因:这涉及到函数的通信机制,涉及到栈,即主调函数在调用被调函数的时候,会把自己的所有变量push到栈中,栈是一种临时存储区,这个过程中主调函数就把要传给被调函数的实参以实参的类型存在栈中;然后控制权从主调函数移交给被调函数,被调函数会根据它的形参的参数类型去读取栈中的值,

所以第一种情况里,主调只传了一个int值,但是被调会从相邻的内存取出另一个4字节的内容作为第二个参数,而一般内存中大多数单元值都是0,所以得到了下面的结果

第2中情况,主调函数传入两个double值,(C的浮点数常量会被直接转换为double,就算是定义了float类型,在作为参数传递的时候也会被编译器自动转换为double类型),所以被调函数读取两个int变量时,共读取了8字节,相当于只读了3.0,那么3.0的前四个字节当然都是0了,后四个字节是3.0,所以相当于是在比较3和0。如果有函数原型,编译器检查到实参的类型不是int,会自动转换为int。

The lesser of 3 and 5 is 0
The lesser of 3 and 5 is 0
Bye!

所以说函数原型的作用主要是在规范参数和返回值的数据类型,保证函数调用过程中读取参数不会出错。可能会实现自动类型转换哦

如果函数真的没有参数,要在原型中用void明确说明

在这里插入图片描述

把函数定义写在第一次调用它之前,相当于函数原型(牛!)

在这里插入图片描述

函数类型(即返回值的类型)

在这里插入图片描述

递归

一直没认真学过递归,很想搞清楚
这段话信息量蛮多的,第一段看着就觉得很像循环,循环的难点就是通过条件测试结束循环,否则会无限循环。
循环的效率竟然比递归高,怎么衡量的这个效率呢?
以前高中学习数列,常用递归表达式,确实很简单,一个简单的表达式就把无限长的数列表示出来了,可谓方案简洁
在这里插入图片描述

示例1(n++, ++n, n+1是完全不同的!!!递归不能用++ / --运算符!!!!)

我写的递归结束条件:++n,最终的执行结果是错的

#include <stdio.h>
void up_and_down(int);
int main()
{
    up_and_down(1);
    return 0;
}

void up_and_down(int n)
{
    printf("level %d: n location %p\n", n, &n);
    if(n<4)//让递归调用停止的语句
        up_and_down(++n);
    printf("LEVEL %d: n location %p\n", n, &n);
}

第四级递归结束竟然还是打印的LEVEL4???

仔细分析,第一行是main调用up_and_down打印的,第一级递归,n=1
第二行是up_and_down调用自己,n=2,第二级递归
第三行是第二级调用第三级,n=3
第四行是第三季调用第四季,n=4
第五行,递归结束,是第四级递归的up_and_down函数的最后一行,n=4
第六行, 退回到第三级,但是++n使得第三级的最后一行的n已经是4了!!!!!!因为先递增在执行递归调用,这就是关键,造成错误的原因,那么由此可见,==递归里面是不可以用递增递减运算符的!!!==因为会造成前一级递归的递归调用语句后面的语句的n变量变化!!!

level 1: n location 0061ff20
level 2: n location 0061ff00
level 3: n location 0061fee0
level 4: n location 0061fec0
LEVEL 4: n location 0061fec0
LEVEL 4: n location 0061fee0
LEVEL 3: n location 0061ff00
LEVEL 2: n location 0061ff20

如果写n++,则每次递归调用函数时,n的值仍然是1,因为后缀递增运算符是先执行函数,再递增n的值

up_and_down(n++);

原来递归只能老老实实用n+1
在这里插入图片描述

下面是正确结果(n+1)

up_and_down(n+1);

可以看到相同级别的递归的变量内存地址是一样的,非常对称

说实话,我之前根本没想到还会输出LEVEL3,LEVEL2,LEVEL1几句,因为我不知道层层递归

level 1: n location 0061ff20
level 2: n location 0061ff00
level 3: n location 0061fee0
level 4: n location 0061fec0
LEVEL 4: n location 0061fec0
LEVEL 3: n location 0061fee0
LEVEL 2: n location 0061ff00
LEVEL 1: n location 0061ff20

几个重要的点
在这里插入图片描述
在这里插入图片描述

尾递归 tail recursion

在这里插入图片描述
尾递归和循环一毛一样,因为递归调用后面没有要用递归变量的其他语句了。

递归VS循环

在这里插入图片描述
下面来比较一下循环和尾递归

循环

#include <stdio.h>
int fac(int);
int main()
{
    printf("The factorial of %d is %d.\n", 10, fac(10));
    return 0;
}
int fac(int n)
{
    int fac=1;
    for(int i=1;i<=n;i++)
        fac *= i;
    return fac;
}

递归,我写这个程序的时候头晕晕的,感觉是误打误撞写出来的······递归果然有点打脑壳,要考虑它的每一级调用······这还是最简单的尾递归···

#include <stdio.h>
int fac(int);
int main()
{
    printf("The factorial of %d is %d.\n", 10, fac(10));
    return 0;
}
int fac(int n)
{
    int y = n;
    if(n>1)
        y *= fac(n-1);
    return y;
}

两种方式都得到了正确的结果

The factorial of 10 is 3628800.

递归也可以写为

#include <stdio.h>
int fac(int);
int main()
{
    printf("The factorial of %d is %d.\n", 10, fac(10));
    return 0;
}
int fac(int n)
{
    int y=1;//必须初始化为1
    if(n>1)
        y = n * fac(n-1);
    else if(n==0)
        y = 1;
    return y;
}

改良版本的程序(把递归和循环都放在一个文件中,并且考虑了输入数值的范围,把返回值的数值范围增大了,还使得可以多次输入)

哇塞!我忘记写return 0 竟然没有报错

#include <stdio.h>
unsigned int loop(int);
unsigned int recur(int);
int main()
{
    int n;
    printf("This program calculates factorials.\nEnter a value in the range of 0-12(q to quit)\n");
    while(scanf("%d", &n)==1)
    {
        printf("loop: The factorial of %d is %d.\n", n, loop(n));
        printf("recursion: The factorial of %d is %d.\n", n, recur(n));
    }
    printf("Bye!\n");
    return 0;
}
unsigned int loop(int n)
{
    unsigned int y = 1;//必须赋初值,0!也解决了
    for(int i=1;i<=n;i++)
        y *= i;
    return y;
}
unsigned int recur(int n)
{
    unsigned int y=1;
    if(n>1)
        y = n * recur(n-1);
    else//n=0
        y=1;
    return y;
}
This program calculates factorials.
Enter a value in the range of 0-12(q to quit)
0
loop: The factorial of 0 is 1.
recursion: The factorial of 0 is 1.
1
loop: The factorial of 1 is 1.
recursion: The factorial of 1 is 1.
2
loop: The factorial of 2 is 2.
recursion: The factorial of 2 is 2.
3
loop: The factorial of 3 is 6.
recursion: The factorial of 3 is 6.
4
loop: The factorial of 4 is 24.
recursion: The factorial of 4 is 24.
5
loop: The factorial of 5 is 120.
recursion: The factorial of 5 is 120.
6
loop: The factorial of 6 is 720.
recursion: The factorial of 6 is 720.
10
loop: The factorial of 10 is 3628800.
recursion: The factorial of 10 is 3628800.

示例 十进制转2进制(倒序输出二进制数的关键:在递归调用语句后面打印每一个余数(二进制位))

在这里插入图片描述
二进制也一样,比如6,6%2=0;所以最后一个二进制位是0;
6/2=3,3%2=1,所以倒数第二个二进制位是1;
3/2=1,1%2=1,所以倒数第3个二进制位是1;而3/2已经是1,小于2了,所以不会有二进制位了,所以二进制就是110

注意:

  • 转换结束的标志就是整数相除的商小于2,即等于0或者1
  • 对于正整数,奇数的最后一个二进制位是1,偶数的最后一位是0,因为除了最后一位的前面都是偶数,只有最后一位可能有区别。
#include <stdio.h>
void to_binary(int);
int main()
{
    int n;
    printf("enter a integer to convert to binary:(q to quit)\n");
    while(scanf("%d", &n)==1)
    {
        printf("decimal:%d\nbinary: ", n);
        to_binary(n);
        putchar('\n');
    }
    printf("Bye!\n");
    return 0;
}

void to_binary(int n)
{
    int a, b;
    a = n/2;
    b = n%2;
    if(n>1)
        to_binary(a);
    putchar(b?'1':'0');
    return;//不写也可以
}

哇塞! 递归的感觉出来了,每一级存储了私有的变量所以才能实现正确顺序输出,即先输出后计算到的值,
具体地说,n=6为例,a=3,b=0,
然后n=3, a=1,b=1,
然后n=1, a=0, b=1
这6个变量是不同的变量,全部客观存在,存储于不同的地址,
二进制的三位数就是a>1之前得到的所有余数b的值,先得到的是最后一位,最后得到的是第一位,所以6的二进制是110

所以要在所有递归调用返回的时候打印字符,这是倒序输出的关键

enter a integer to convert to binary:(q to quit)
6
decimal:6
binary: 110
10
decimal:10
binary: 1010
255
decimal:255
binary: 11111111
167
decimal:167
binary: 10100111

我之前把putchar这句代码写在if判断之前,结果打印的不是倒序

void to_binary(int n)
{
    int a, b;
    a = n/2;
    b = n%2;
    putchar(b?'1':'0');
    if(n>1)
        to_binary(a);
    return;//不写也可以
}

结果是反着的

enter a integer to convert to binary:(q to quit)
6
decimal:6
binary: 011
10
decimal:10
binary: 0101
255
decimal:255
binary: 11111111
167
decimal:167
binary: 11100101

其中我还没反应过来用putchar(b?‘1’:‘0’);来将数值转换为字符打印,不错,很棒

双递归 double recursion 斐波那契数列

#include <stdio.h>
unsigned int fibo(unsigned int);
int main()
{
    int n;
    printf("enter a positive integer:(q to quit)\n");
    while(scanf("%u", &n)==1)
    {
        if(n==1)
            printf("The %ust number in the Fibonacci list is: %u\n", n, fibo(n));
        else if(n==2)
            printf("The %und number in the Fibonacci list is: %u\n", n, fibo(n));
        else if(n==3)
            printf("The %urd number in the Fibonacci list is: %u\n", n, fibo(n));
        else
            printf("The %uth number in the Fibonacci list is: %u\n", n, fibo(n));
        printf("enter a positive integer:(q to quit)\n");
    }
    printf("Bye!\n");
    return 0;
}
unsigned int fibo(unsigned int n)
{
    if(n==1 || n==2)
        return 1;
    else
        return fibo(n-1) + fibo(n-2);
}
enter a positive integer:(q to quit)
1
The 1st number in the Fibonacci list is: 1
enter a positive integer:(q to quit)
2
The 2nd number in the Fibonacci list is: 1
enter a positive integer:(q to quit)
3
The 3rd number in the Fibonacci list is: 2
enter a positive integer:(q to quit)
4
The 4th number in the Fibonacci list is: 3
enter a positive integer:(q to quit)
5
The 5th number in the Fibonacci list is: 5
enter a positive integer:(q to quit)
6
The 6th number in the Fibonacci list is: 8
enter a positive integer:(q to quit)
7
The 7th number in the Fibonacci list is: 13
enter a positive integer:(q to quit)
8
The 8th number in the Fibonacci list is: 21
enter a positive integer:(q to quit)
q
Bye!

程序中的if可以用switch代替

#include <stdio.h>
unsigned int fibo(unsigned int);
int main()
{
    int n;
    printf("enter a positive integer:(q to quit)\n");
    while(scanf("%u", &n)==1)
    {
        switch(n)
        {
        case 1:
            printf("The %ust number in the Fibonacci list is: %u\n", n, fibo(n));break;
        case 2:
            printf("The %und number in the Fibonacci list is: %u\n", n, fibo(n));break;
        case 3:
            printf("The %urd number in the Fibonacci list is: %u\n", n, fibo(n));break;
        default:
            printf("The %uth number in the Fibonacci list is: %u\n", n, fibo(n));
        }

        printf("enter a positive integer:(q to quit)\n");
    }
    printf("Bye!\n");
    return 0;
}
unsigned int fibo(unsigned int n)
{
    if(n==1 || n==2)
        return 1;
    else
        return fibo(n-1) + fibo(n-2);
}

双递归的可怕之处在于:指数增长
我试了一下,上面的程序,输入100的话,要等好久好久都不出来结果·······
在这里插入图片描述

所有C函数是平等的 main()调用自己

大概过程这个术语是老的,以前pascal什么的爱用吧,现在基本不用过程这个词了,(没有返回值的函数好像也可以叫做过程,我还是统一只说函数吧)
在这里插入图片描述
小小尝试了一下,不知道main怎么调用自己,因为递归调用要求这个函数一定要有参数,通过这个参数的变化而终止递归,所以main得有参数,可是谁给main的参数传递值呢??我没解决这个问题,所以没成功实现main调用main
这是我的实验程序(无法通过编译)

#include <stdio.h>
int main(int n)
{
    int n=4;
    if(n>1)
        main(n-1);
    printf("main\n");
    return 0;
}

其他函数调用main我也没实现,因为递归调用main则必须给main传参,参数的改变使得最终可以结束递归,那么又是一样的问题,编译器第一次调用main 的时候,谁给main传参????

好晕,一团乱麻

#include <stdio.h>
void call_main(int);
int main(int m)
{
    call_main(m);
    return 0;
}
void call_main(int n)
{
    printf("MAIN\n");
    if(n>1)
        main(n-1);
    printf("main\n");
    return;
}
#include <stdio.h>
int call_main(int);
int main()
{
    printf("MAIN\n");
    call_main(3);
    return 0;
}
int call_main(int n)
{
    if(n>5)
        main();

    else if(n<5)
        //printf("main\n");
        return;
}

不同系统编译多个源文件的程序

gcc, clang是命令行编译器

UNIX系统

在这里插入图片描述

Linux

和unix没啥区别
在这里插入图片描述

Mac & Windows(用项目来管理)

在这里插入图片描述

把函数原型和#define定义的符号变量放在头文件中

在这里插入图片描述在这里插入图片描述

示例 酒店房费

在这里插入图片描述
我用循环实现的价格变化

#include <stdio.h>
#include <math.h>
#include "hotel.h"

int main()
{
    unsigned short n;
    unsigned short nights;
    double unit_price, price;
    printf("enter a hotel number(1,2,3,4) and nights:(q to quit)\n");
    while(scanf("%hu %hu", &n, &nights)==2)
    {
        switch(n)
        {
        case 1:
            unit_price = HOTEL1;
            price = get_price(unit_price, nights);break;
        case 2:
            unit_price = HOTEL2;
            price = get_price(unit_price, nights);break;
        case 3:
            unit_price = HOTEL3;
            price = get_price(unit_price, nights);break;
        case 4:
            unit_price = HOTEL4;
            price = get_price(unit_price, nights);break;
        default:
            printf("hotel number:1,2,3,4\n");break;
        }
        printf("your hotel: hotel%u\nunit price:%.2f\ntotal price:%.2f\n", n, unit_price, price);
    }

    printf("Bye!\n");
    return 0;
}
double get_price(double unit, unsigned short nights)
{
    double price=0, scale=1.0;
    for(int i=0;i<nights;i++)
    {
        scale *= pow(.95, i);
        price += scale;}
    price *= unit;
    return price;
}

头文件

#define HOTEL1 100
#define HOTEL2 150
#define HOTEL3 200
#define HOTEL4 250
double get_price(double, unsigned short);
enter a hotel number(1,2,3,4) and nights:(q to quit)
1 1
your hotel: hotel1
unit price:100.00
total price:100.00
1 2
your hotel: hotel1
unit price:100.00
total price:195.00
1 3
your hotel: hotel1
unit price:100.00
total price:280.74
2 1
your hotel: hotel2
unit price:150.00
total price:150.00
2 2
your hotel: hotel2
unit price:150.00
total price:292.50
4 1
your hotel: hotel4
unit price:250.00
total price:250.00
4 2
your hotel: hotel4
unit price:250.00
total price:487.50
3 1
your hotel: hotel3
unit price:200.00
total price:200.00
q
Bye!

拿着纸笔认真推了几分钟,终于用递归写出来了,看来写程序还是要用纸笔推导才能又快又好啊

思路:这是一个等比数列的求和问题,假设原价是 x x x,则住n天的总价是:
x + 0.95 x + 0.9 5 2 x + ⋯ = x ( 1 + 0.95 + 0.9 5 2 + ⋯   ) x+0.95x+0.95^2x+\cdots=x(1+0.95+0.95^2+\cdots) x+0.95x+0.952x+=x(1+0.95+0.952+)
x是住一天的价格,即单价
后面括号里的1, 0.95, 0.95的幂次都是对单价的一个缩小scale,所以直接递归或者循环求出这一坨scale的和,再直接乘以单价就是正解

循环写起来特别顺手,但是递归还很生分,有几个点:

  • 递归函数的参数是一个变化量(假设是n),他需要增大或者减小以达到if的递归测试退出条件,尽量只写一个参数,这个参数相当于for的循环变量
  • 递归函数的if测试表达式就是上述变化参数n的关系表达式
  • 在if测试表达式之前应该利用变化参数n求出需要的值,然后在if表达式后面的递归调用中减小或增大变化参数n的值
#include <stdio.h>
#include <math.h>
#include "hotel.h"

int main()
{
    unsigned short n;
    unsigned short nights;
    double unit_price, price;
    printf("enter a hotel number(1,2,3,4) and nights:(q to quit)\n");
    while(scanf("%hu %hu", &n, &nights)==2)
    {
        switch(n)
        {
        case 1:
            unit_price = HOTEL1;
            price = get_price(unit_price, nights);break;
        case 2:
            unit_price = HOTEL2;
            price = get_price(unit_price, nights);break;
        case 3:
            unit_price = HOTEL3;
            price = get_price(unit_price, nights);break;
        case 4:
            unit_price = HOTEL4;
            price = get_price(unit_price, nights);break;
        default:
            printf("hotel number:1,2,3,4\n");break;
        }
        printf("your hotel: hotel%u\nunit price:%.2f\ntotal price:%.2f\n", n, unit_price, price);
    }

    printf("Bye!\n");
    return 0;
}
double get_price(double unit, unsigned short nights)
{return unit*get_scale(nights);}

double get_scale(unsigned short nights)
{
    double scale=pow(.95, nights-1);
    if(nights>1)
        scale += get_scale(nights-1);
    return scale;
}

头文件只是多了一个原型

double get_scale(unsigned short);

正确输出

enter a hotel number(1,2,3,4) and nights:(q to quit)
1 1
your hotel: hotel1
unit price:100.00
total price:100.00
1 2
your hotel: hotel1
unit price:100.00
total price:195.00
1 3
your hotel: hotel1
unit price:100.00
total price:285.25
2 1
your hotel: hotel2
unit price:150.00
total price:150.00
2 2
your hotel: hotel2
unit price:150.00
total price:292.50
3 1
your hotel: hotel3
unit price:200.00
total price:200.00
3 2
your hotel: hotel3
unit price:200.00
total price:390.00
4 1
your hotel: hotel4
unit price:250.00
total price:250.00
4 2
your hotel: hotel4
unit price:250.00
total price:487.50
q
Bye!

根据书的指导,改进了用户界面

#include <stdio.h>
#include <math.h>
#include "hotel.h"

int main()
{
    double price, unit_price, nights;
    unsigned short code;
    while((code=choose_hotel())!=QUIT)
    {
        switch(code)
    {
    case 1:
        unit_price = HOTEL1;break;
    case 2:
        unit_price = HOTEL2;break;
    case 3:
        unit_price = HOTEL3;break;
    case 4:
        unit_price = HOTEL4;break;
    case 5:
        printf("Thank you and goodbye.\n");
    default:
        printf("Enter an integer from 1 to 5, please\n");break;

    }
    nights = get_nights();
    price = get_price(unit_price, nights);
    printf("Total price: %.2f\n", price);
    }
    printf("Thank you and goodbye.\n");


    return 0;
}


double get_price(double unit, unsigned short nights)
{return unit*get_scale(nights);}

double get_scale(unsigned short nights)
{
    double scale=pow(.95, nights-1);
    if(nights>1)
        scale += get_scale(nights-1);
    return scale;
}

void star()
{
    for(int i=0;i<50;i++)
        putchar('*');
    putchar('\n');
}

unsigned short choose_hotel()
{
    int status;
    unsigned short code;

    star();
    printf("Enter the number of the desired hotel:\n");
    printf("1)%-20s", NAME1);printf("2)%-20s\n", NAME2);
    printf("3)%-20s", NAME3);printf("4)%-20s\n", NAME4);
    printf("5)%-20s\n", "quit");
    star();
    while(((status = scanf("%hu", &code))!=1)  || (code<1 || code>5))
    {
        if(status!=1)
            scanf("%*s");//处理非整数输入,跳到下一个空白字符
        printf("Please enter an integer from 1 to 5, such as 2.\n");

    }
    return code;
}

unsigned int get_nights()
{
    unsigned short nights;
    printf("How many nights are needed?\n");
    while(scanf("%hu", &nights)!=1)
    {
        scanf("%*s");//处理非整数输入
        printf("Please enter an integer from 1 to 5, such as 992.\n");
    }
    return nights;
}

头文件

#define HOTEL1 100
#define HOTEL2 150
#define HOTEL3 200
#define HOTEL4 250
#define NAME1 "Fairfield Arms"
#define NAME2 "Hotel Olympic"
#define NAME3 "Chertworthy Plaza"
#define NAME4 "The Stockton"
#define QUIT 5
double get_price(double, unsigned short);
double get_scale(unsigned short);
void star(void);
unsigned int get_nights();
unsigned short choose_hotel();

输出

**************************************************
1
How many nights are needed?
1
Total price: 100.00
**************************************************
Enter the number of the desired hotel:
1)Fairfield Arms      2)Hotel Olympic
3)Chertworthy Plaza   4)The Stockton
5)quit
**************************************************
1
How many nights are needed?
1.2
Total price: 100.00
**************************************************
Enter the number of the desired hotel:
1)Fairfield Arms      2)Hotel Olympic
3)Chertworthy Plaza   4)The Stockton
5)quit
**************************************************
Please enter an integer from 1 to 5, such as 2.
1.2
How many nights are needed?
Please enter an integer from 1 to 5, such as 992.
1
Total price: 100.00
**************************************************
Enter the number of the desired hotel:
1)Fairfield Arms      2)Hotel Olympic
3)Chertworthy Plaza   4)The Stockton
5)quit
**************************************************
a
Please enter an integer from 1 to 5, such as 2.
2
How many nights are needed?
1
Total price: 150.00
**************************************************
Enter the number of the desired hotel:
1)Fairfield Arms      2)Hotel Olympic
3)Chertworthy Plaza   4)The Stockton
5)quit
**************************************************
5
Thank you and goodbye.

通过这一程序发现的问题:(弄了接近一小时·····很心累的)

  • 用户界面还是稍微装点一下,一般我们写程序都喜欢像算法竞赛那样,干干净净,必要的输入和输出之外啥也不要,但是平时的应用还是要友好一点
  • 调试发现,输入小数不会进入到这个while里!!!while(scanf("%hu", &nights)!=1)我很震惊好嘛,竟然只是输入字母进去了。对于浮点数是通过转换说明自动转换为整数了,当然都是趋0截断。上面的输出可以看到。
  • scanf("%*s");之前学过, ∗ * 在scanf和printf中是跳过的作用,即不会被用到。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值