C Programming: A Modern Approach

第10章 C程序的组织形式

2020-02-29  本文已影响0人  橡树人

英文原版:P219

本章来讨论一个程序包含多个函数时产生的几个问题。

思考:
当一个程序包含多个函数时,会出现哪些问题?

本章的主要内容有:

10.1 局部变量

有3种局部变量

什么是局部变量

在函数体内声明的变量就是该函数的局部变量

例1 局部非静态变量

int sum_digits(int n)
{
    int sum = 0; //非静态局部变量

    while(n>0){
     sum += n%10;
     n /= 10;
    }

    return sum;
}

非静态局部变量有哪些性质?

注:

  1. 什么是一个变量的存储期限?
    一个变量的存储期限就是从给变量分配存储单元开始,到销毁该存储单元时结束。
    一个局部非静态变量的存储期限就是从包含该变量的函数被调用时开始,到该函数返回时结束。
    一个局部非静态变量的存储期限是自动的,意味着当包含该变量的函数被调用时,该变量的存储单元是自动分配的;当该函数返回时,该变量的存储单元是自动销毁的;这两个过程并不需要程序员显式地参与
  2. 什么是变量的作用域?
    变量的作用域就是程序中引用到该变量的文本部分,从变量声明处开始,到最后一次引用处结束。
    一个局部变量具有块作用域,意味着从该变量的声明处开始,到包含该变量的函数体末尾结束,该变量都是可见的
    由于一个局部变量的作用域不会超出包含它的函数,所以在不同的函数里是可以有相同名字的局部变量的。

静态局部变量

静态局部变量有哪些性质?

解释:

总而言之,静态局部变量就是一个位置,该位置对其他函数是不可见的,是为相同函数的将来调用保存数据的。

例1 静态局部变量

void f()
{
    static int i;
}

解释:
由于局部静态变量i具有静态存储期限,所以当函数f退出后,i的值并不会丢失。

形式参数

形式参数有哪些性质?

形式参数跟局部非静态变量的区别就是:

10.2节 全局变量

函数间通信的方式有两种:

全局变量不在任何函数体内部声明,是在所有函数体的外部声明的。

全局变量有哪些性质?

解释:

例1 使用全局变量来实现栈

#include <stdbool.h>

#define STACK_SIZE 100

//全局变量
int contents[STACK_SIZE];
int top = 0;

void make_empty(vid)
{
    top = 0;
}

bool is_empty(void)
{
    return top == 0;
}

bool is_full(void)
{
    return top == STACK_SIZE;
}

void push(int i)
{
    if (is_full())
    {
        stack_overflow();
    }
    else 
    {
        contents[top++] = i;
    }
}

int pop(void)
{
    if (is_empty())
    {
        stack_overflow();
    }
    else 
    {
        return contents[--top];
    }
}

全局变量的优缺点

在大部分情形里,最好使用函数调用来进行函数间通信,而不是共享变量,理由有3个:

  1. 我们如果在程序维护期间修改了外部变量,则需要检查同一个文件里的每个函数,确认这个修改是如何影响该函数。
  2. 如果一个外部变量被赋了一个不正确的值,有可能很难确认受影响的函数。
  3. 依赖外部函变量的函数很难被其他程序复用。如果其他程序要复用改函数,则需要抽取它需要使用的外部变量。

哪些情形比较适合使用全局变量来进行函数间通信?

程序示例:猜数字

样例输出

Guess the secret number between 1 and 100.

A new number has been chosen.
Enter guess: 55
Too low; Try again.
Enter guess: 65
Too high; Try again.
Enter number: 60
Too high; Try again.
Enter number: 58
You won in a 4 guess!

Play agin? (Y/N) y

A new number has been chosen.
Enter guess: 78
Too high; try again.
Enter guess: 34
You won in 2 guess!

Player again? (Y/N) n

功能实现:

外部变量版本:
guess.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAX_NUMBER 100

//外部变量
int secret_number;

//函数原型
void initialize_number_generator(void);

void choose_secret_number(void);

void read_guess(void);

int main(void)
{
    char command;

    printf("Guess the secret number between 1 and %d.\n\n", MAX_NUMBER);
    initialize_number_generator();
    do
    {
        choose_secret_number();
        printf("A new number has been choosen.\n");
        read_guess();
        printf("Player again? (Y/N) ");
        scanf(" %c", &command);
        printf("\n");
    }while(command == 'y'|| command == 'y');

    return 0;
}

void initialize_number_generator(void)
{
    srand((unsigned)time(NULL));
}

void choose_secret_number(void)
{
    secret_number = rand()%MAX_NUMBER + 1;
}

void read_guess(void)
{
    int guess, number_guess = 0;

    for (;;){
        number_guess++;
        printf("Enter guess: ");
        scanf("%d", &guess);
        if (guess == secret_number) {
            printf("You won in %d guess!\n\n", number_guess);
        } else if (guess < secret_number) {
            printf("Too low; try again.\n");
        } else {
            printf("Too high; try again\n");
        }
    }

}

不使用外部变量实现:
guess2.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAX_NUMBER 100

//函数原型
void initialize_number_generator(void);

int new_secret_number(void);

void read_guess(int secret_number);

int main(void)
{
    char command;
    int secret_number;

    printf("Guess the secret number between 1 and %d.\n\n", MAX_NUMBER);
    initialize_number_generator();
    do
    {
        secret_number = new_secret_number();
        printf("A new number has been choosen.\n");
        read_guess(secret_number);
        printf("Player again? (Y/N) ");
        scanf(" %c", &command);
        printf("\n");
    }while(command == 'y'|| command == 'y');

    return 0;
}

void initialize_number_generator(void)
{
    srand((unsigned)time(NULL));
}

int new_secret_number(void)
{
    return rand()%MAX_NUMBER + 1;
}

void read_guess(int secret_number)
{
    int guess, number_guess = 0;

    for (;;){
        number_guess++;
        printf("Enter guess: ");
        scanf("%d", &guess);
        if (guess == secret_number) {
            printf("You won in %d guess!\n\n", number_guess);
        } else if (guess < secret_number) {
            printf("Too low; try again.\n");
        } else {
            printf("Too high; try again\n");
        }
    }

}

10.3 程序块

程序块和复合语句

C语言里的程序块的一般格式为:

{多条声明 多条语句}

程序块都用于哪些场景?

程序块都有哪些性质?
默认情况下,

例1 程序块

if (i > j){
  int temp = i;
  i = j;
  j = temp;
}

10.4 作用域

在C语言中,相同的标识符可能有若干种不同的含义。

C语言的作用域规则使得程序员和编译器能判断在程序中的某个点该使用同名标识符的哪一种含义

最重要的作用域规则:
当程序块内的某条声明命名了一个标识符,如果该标识符已经是已可见时,则新的声明将临时隐藏旧的声明,直到程序块的末尾处。出了程序块,该标识符就恢复原来的含义

例1 作用域规则示例

int i;//声明1:i是一个全局变量,有文件作用域

void f(int i)//声明2:i是一个形式参数,具有块作用域
{
  i = 1;//这里的i使用的是声明2里的i的含义
}

void g(void)
{
  int i = 2;//声明3:i是一个局部非静态变量,具有块作用域;
  if (i > 0) {//这里的i使用的是声明3的i的含义
    int i;//声明4:i是一个局部非静态变量,具有块作用域

    i = 3; //这里的i使用的是声明4里的i的含义
  }
  
  i = 4;//这里的i使用的是声明3里的i的含义
}

void h(void)
{
  i = 5;//这里的i使用的是声明1里的i的含义
}

解释:

上一篇 下一篇

猜你喜欢

热点阅读