如何制作apple pencil外链seo服务
- 作者: 五速梦信息网
- 时间: 2026年03月21日 09:49
当前位置: 首页 > news >正文
如何制作apple pencil,外链seo服务,网站 一级域名 二级域名,哪个网站做ppt好文章目录 第10章 程序结构10.1 局部变量10.1.1 静态局部变量10.1.2 形式参数 10.2 外部变量10.2.1 示例#xff1a;用外部变量实现栈10.2.2 外部变量的利与弊 10.3 程序块10.4 作用域10.5 构建C程序10.5.1 复杂程序#xff1a;给一手牌分类 问与答写在最后 第10章 程序结构
… 文章目录 第10章 程序结构10.1 局部变量10.1.1 静态局部变量10.1.2 形式参数 10.2 外部变量10.2.1 示例用外部变量实现栈10.2.2 外部变量的利与弊 10.3 程序块10.4 作用域10.5 构建C程序10.5.1 复杂程序给一手牌分类 问与答写在最后 第10章 程序结构
——Will Rogers也一定会说“没有自由变量这种东西。”
第9章已经介绍过函数了因此本章就来讨论一个程序包含多个函数时所产生的几个问题。本章的前两节讨论局部变量10.1节和外部变量10.2节之间的差异10.3节考虑程序块含有声明的复合语句问题10.4节解决用于局部名、外部名和在程序块中声明的名字的作用域规则问题10.5节介绍用来组织函数原型、函数定义、变量声明和程序其他部分的方法。
10.1 局部变量
我们把在函数体内声明的变量称为该函数的局部变量。在下面的函数中sum是局部变量:
int sum_digits(int n)
{ int sum 0; /* local variable / while (n 0) { sum n % 10; n / 10; } return sum;
}默认情况下局部变量具有下列性质:
自动存储期。变量的存储期storage duration也称为延续是程序执行时能够确保变量的存储空间必定存在的那一部分时间。通常来说局部变量的存储空间是在包含该变量的函数被调用时“自动”分配的函数返回时收回分配所以称这种变量具有自动存储期。包含局部变量的函数返回时局部变量的值无法保留。当再次调用该函数时无法保证变量仍拥有原先的值。块作用域。变量的作用域是可以引用该变量的那一部分程序文本。局部变量拥有块作用域从变量声明的点开始一直到所在函数体的末尾。因为局部变量的作用域不能延伸到其所属函数之外所以其他函数可以把同名变量用于别的用途。 C99不要求在函数一开始就进行变量声明所以局部变量的作用域可能非常小。 10.1.1 静态局部变量
在局部变量声明中放置单词static可以使变量具有静态存储期而不再是自动存储期。因为具有静态存储期的变量拥有永久的存储单元所以在整个程序执行期间都会保留变量的值。思考下面的函数
void f(void)
{ static int i; / static local variable / …
}因为局部变量i已经声明为static所以在程序执行期间它所占据的内存单元是不变的。在f返回时变量i不会丢失其值。 静态局部变量始终有块作用域所以它对其他函数是不可见的。概括来说静态变量是对其他函数隐藏数据的地方但是它会为将来同一个函数的再调用保留这些数据。 10.1.2 形式参数
形式参数拥有和局部变量一样的性质即自动存储期和块作用域。事实上形式参数和局部变量唯一真正的区别是在每次函数调用时对形式参数自动进行初始化调用中通过赋值获得相应实际参数的值。
10.2 外部变量
传递参数是给函数传送信息的一种方法。函数还可以通过外部变量external variable进行通信。外部变量是声明在任何函数体外的。
外部变量有时称为全局变量的性质不同于局部变量的性质。
静态存储期。就如同声明为static的局部变量一样外部变量拥有静态存储期。存储在外部变量中的值将永久保留下来。文件作用域。外部变量拥有文件作用域从变量被声明的点开始一直到所在文件的末尾。因此跟随在外部变量声明之后的所有函数都可以访问并修改它。
10.2.1 示例用外部变量实现栈 为了说明外部变量的使用方法一起来看看称为栈stack的数据结构。栈是抽象的概念它不是C语言的特性。大多数编程语言都可以实现栈。像数组一样栈可以存储具有相同数据类型的多个数据项。然而栈操作是受限制的只可以往栈中压入数据项把数据项加在一端——“栈顶”或者从栈中弹出数据项从同一端移走数据项。禁止测试或修改不在栈顶的数据项。 C语言中实现栈的一种方法是把元素存储在数组中我们称这个数组为contents。命名为top的一个整型变量用来标记栈顶的位置。栈为空时top的值为0。为了往栈中压入数据项可以把数据项简单存储在contents中由top指定的位置上然后自增top。弹出数据项则要求自减top然后用它作为contents的索引取回弹出的数据项。 基于上述这些概要这里有一段代码不是完整的程序为栈声明了变量contents和top并且提供了一组函数来表示对栈的操作。全部5个函数都需要访问变量top而且其中2个函数还都需要访问contents所以接下来把contents和top设为外部变量。 #include stdbool.h / C99 only /
#define STACK_SIZE 100 / external variables /
int contents[STACK_SIZE];
int top 0; void make_empty(void)
{ 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_underflow(); elsereturn contents[–top];
}10.2.2 外部变量的利与弊 在多个函数必须共享一个变量时或者少数几个函数共享大量变量时外部变量是很有用的。然而在大多数情况下对函数而言通过形式参数进行通信比通过共享变量的方法更好原因列举如下: 在程序维护期间如果改变外部变量比方说改变它的类型那么将需要检查同一文件中的每个函数以确认该变化如何对函数产生影响。如果外部变量被赋了错误的值可能很难确定出错的函数就好像侦察大型聚会上的谋杀案时很难缩小嫌疑人范围一样。很难在其他程序中复用依赖外部变量的函数。依赖外部变量的函数不是“独立的”。为了在另一个程序中使用该函数必须带上此函数需要的外部变量。 许多C程序员过于依赖外部变量。一个普遍的陋习是在不同的函数中为不同的目的使用同一个外部变量。假设几个函数都需要变量i来控制for语句。一些程序员不是在使用变量i的每个函数中都声明它而是在程序的顶部声明它从而使得该变量对所有函数都是可见的。这种方式除了前面提到的几个缺点外还会产生误导以后阅读程序的人可能认为变量的使用彼此关联而实际并非如此。 使用外部变量时要确保它们都拥有有意义的名字。局部变量不是总需要有意义的名字的因为往往很难为for循环中的控制变量起一个比i更好的名字。如果你发现为外部变量使用的名字就像i和temp一样这可能意味着这些变量其实应该是局部变量。 请注意把原本应该是局部变量的变量声明为外部变量可能导致一些令人厌烦的错误请看下面这个例子
//我们希望显示一个由星号组成的10×10的图形int i; void print_one_row(void)
{ for (i 1; i 10; i) printf();
} void print_all_rows(void)
{ for (i 1; i 10; i) { print_one_row(); printf(\n); }
}/*
print_all_rows函数不是显示10行星号而是只显示1行。
在第一次调用print_one_row函数后返回时i的值将为11。
然后print_all_rows函数中的for语句对变量i进行自增并判定它是否小于或等于10。
因为判定条件不满足所以循环终止函数返回。
/为了获得更多关于外部变量的经验现在编写一个简单的游戏程序。这个程序产生一个1~100的随机数用户尝试用尽可能少的次数猜出这个数。
运行效果如下所示
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 guess: 60
Too high; try again.
Enter guess: 58
You won in 4 guesses! Play again? (Y/N) y A new number has been chosen.
Enter guess: 78
Too high; try again.
Enter guess: 34
You won in 2 guesses! Play again? (Y/N) n这个程序需要完成几个任务初始化随机数生成器选择神秘数以及与用户交互直到选出正确数为止。如果编写独立的函数来处理每个任务那么可能会得到下面的程序。
/
guess.c(外部变量版本)
–Asks user to guess a hidden number
/#include stdio.h
#include stdlib.h
#include time.h #define MAX_NUMBER 100 / external variable /
int secret_number; / prototypes */
void initialize_number_generator(void);
void choose_new_secret_number(void);
void read_guesses(void); int main(void)
{ char command; printf(Guess the secret number between 1 and %d.\n\n, MAX_NUMBER); initialize_number_generator(); do { choose_new_secret_number(); printf(A new number has been chosen.\n); read_guesses(); printf(Play again? (Y/N) ); scanf( %c, command); printf(\n); } while (command y || command Y); return 0;
} /************************************************************ * initialize_number_generator: Initializes the random * * number generator using * * the time of day. * *********************************************************/
void initialize_number_generator(void)
{ srand((unsigned)time(NULL));
} /********************************************************* * choose_new_secret_number: Randomly selects a number * * between 1 and MAX_NUMBER and * * stores it in secret_number. * *********************************************************/
void choose_new_secret_number(void)
{ secret_number rand() % MAX_NUMBER 1;
}/********************************************************* * read_guesses: Repeatedly reads user guesses and tells * * the user whether each guess is too low, * * too high, or correct. When the guess is * * correct, prints the total number of * * guesses and returns. * **********************************************************/
void read_guesses(void)
{ int guess, num_guesses 0; for (;;) { num_guesses; printf(Enter guess: ); scanf(%d, guess); if(guess secret_number) { printf(You won in %d guesses!\n\n, num_guesses); return; }else if(guess secret_number) printf(Too low; try again.\n); else printf(Too high; try again.\n); }
}对于随机数的生成这次将缩放rand函数的返回值使其落在1~MAX_NUMBER范围内。虽然guess.c程序工作正常但是它依赖一个外部变量。把变量secret_number外部化以便choose_new_secret_number函数和read_guesses函数都可以访问它。 如果对choose_new_secret_number函数和read_guesses函数稍做改动应该能把变量secret_number移入main函数中。现在我们将修改choose_new_secret_number函数以便函数返回新值并将重写read_guesses函数以便变量secret_number可以作为参数传递给它具体请看下面这个程序。 /*
guess2.c(形式参数版本)
–Asks user to guess a hidden number
/#include stdio.h
#include stdlib.h
#include time.h #define MAX_NUMBER 100 / prototypes */
void initialize_number_generator(void);
int new_secret_number(void); //修改
void read_guesses(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 chosen.\n); read_guesses(secret_number); //修改printf(Play again? (Y/N) ); scanf( %c, command); printf(\n); } while (command y || command Y); return 0;
} /********************************************************** * initialize_number_generator: Initializes the random * * number generator using * * the time of day. * *********************************************************/
void initialize_number_generator(void)
{ srand((unsigned)time(NULL));
} /********************************************************* * new_secret_number: Returns a randomly chosen number * * between 1 and MAX_NUMBER. * *********************************************************/
int new_secret_number(void) //修改
{ return rand() % MAX_NUMBER 1;
} /********************************************************* * read_guesses: Repeatedly reads user guesses and tells * * the user whether each guess is too low, * * too high, or correct. When the guess is * * correct, prints the total number of * * guesses and returns. * **********************************************************/
void read_guesses(int secret_number) //修改
{ int guess, num_guesses 0; for (;;) { num_guesses; printf(Enter guess: ); scanf(%d, guess); if (guess secret_number) { printf(You won in %d guesses!\n\n, num_guesses); return; }else if(guess secret_number) printf(Too low; try again.\n); else printf(Too high; try again.\n); }
}10.3 程序块
5.2节遇到过复合语句一个复合语句也是一个块block但块并非只有复合语句这一种形式。块也叫程序块。下面是程序块的示例
if (i j) { /* swap values of i and j / int temp i; i j; j temp;
}这里整个if语句是一个程序块if语句的每一个子句也是程序块。默认情况下声明在程序块中的变量的存储期是自动的进入程序块时为变量分配存储单元退出程序块时收回分配的空间。变量具有块作用域也就是说不能在程序块外引用。 函数体是程序块。在需要临时使用变量时函数体内的程序块也是非常有用的。在上面这个例子中我们需要一个临时变量以便可以交换i和j的值。在程序块中放置临时变量有两个好处(1)避免函数体起始位置的声明与只是临时使用的变量相混淆(2)减少了名字冲突。在此例中名字temp可以根据不同的目的用于同一函数中的其他地方在程序块中声明的变量temp严格属于局部程序块。 C99允许在程序块的任何地方声明变量就像允许在函数体内的任何地方声明变量一样。 10.4 作用域
在C程序中相同的标识符可以有不同的含义。C语言的作用域规则使得程序员和编译器可以确定与程序中给定点相关的是哪种含义。
下面是最重要的作用域规则当程序块内的声明命名一个标识符时如果此标识符已经是可见的因为此标识符拥有文件作用域或者因为它已在某个程序块内声明新的声明临时“隐藏”了旧的声明标识符获得了新的含义。在程序块的末尾标识符重新获得旧的含义。
思考下面这个有点极端的例子例子中的标识符i有4种不同的含义。
在声明1中i是具有静态存储期和文件作用域的变量。在声明2中i是具有块作用域的形式参数。在声明3中i是具有块作用域的自动变量。在声明4中i也是具有块作用域的自动变量。
int i; //声明1void f(int i){ //声明2i 1;
}void g(void){int i 2; //声明3if(i 0){int i; //声明4i 3;}i 4;
}void h(void){i 5;
}一共使用了5次i。C语言的作用域规则允许确定每种情况中i的含义。
因为声明2隐藏了声明1所以赋值i 1引用了声明2中的形式参数而不是声明1中的变量。因为声明3隐藏了声明1而且声明2超出了作用域所以判定i 0引用了声明3中的变量。因为声明4隐藏了声明3所以赋值i 3引用了声明4中的变量。赋值i 4引用了声明3中的变量。声明4超出了作用域所以不能引用。赋值i 5引用了声明1中的变量。
10.5 构建C程序 我们已经看过构成C程序的主要元素现在应该为编排这些元素开发一套方法了。目前只考虑单个文件的程序第15章会说明如何组织多个文件的程序。 迄今为止已经知道程序可以包含
include和#define这样的预处理指令类型定义外部变量声明函数原型函数定义。 C语言对上述这些项的顺序要求极少 执行到预处理指令所在的代码行时预处理指令才会起作用类型名定义后才可以使用变量声明后才可以使用。虽然C语言对函数没有什么要求但是这里强烈建议在第一次调用函数前要对每个函数进行定义或声明。 至少C99要求我们这么做。 为了遵守这些规则这里有几个构建程序的方法。下面是一种可能的编排顺序 #include指令#define指令类型定义外部变量的声明除main函数之外的函数的原型main函数的定义其他函数的定义。 因为#include指令带来的信息可能在程序中的好几个地方都需要所以先放置这条指令是合理的。 #define指令创建宏对这些宏的使用通常遍布整个程序。 类型定义放置在外部变量声明的上面是合乎逻辑的因为这些外部变量的声明可能会引用刚刚定义的类型名。 接下来声明外部变量使得它们对于跟随在其后的所有函数都是可用的。在编译器看见原型之前调用函数可能会产生问题而此时声明除了main函数以外的所有函数可以避免这些问题。这种方法也使得无论用什么顺序编排函数定义都是可能的。例如根据函数名的字母顺序编排或者把相关函数组合在一起进行编排。在其他函数前定义main函数使得阅读程序的人容易定位程序的起始点。 最后的建议在每个函数定义前放盒型注释可以给出函数名、描述函数的目的、讨论每个式参数的含义、描述返回值如果有的话并罗列所有的副作用如修改了外部变量的值。
10.5.1 复杂程序给一手牌分类 为了说明构建C程序的方法下面编写一个比前面的例子更复杂的程序——给一手牌分类。这个程序会对一手牌进行读取和分类。手中的每张牌都有花色方块、梅花、红桃和黑桃和点数2、3、4、5、6、7、8、9、10、J、Q、K和A。不允许使用王牌并且假设A是最高的点数。程序将读取一手5张牌然后把手中的牌分为下列某一类列出的顺序从最好到最坏。分类如下 同花顺即顺序相连又都是同花色。四张4张牌点数相同。葫芦3张牌是同样的点数另外2张牌是同样的点数。同花5张牌是同花色的。顺子5张牌的点数顺序相连。三张3张牌的点数相同。两对。对子2张牌的点数相同。其他牌任何其他情况的牌。
如果一手牌可分为两种或多种类别程序将选择最好的一种。
为了便于输入把牌的点数和花色简化如下字母可以是大写也可以是小写。
点数2 3 4 5 6 7 8 9 t j q k a。花色c d h s。
如果用户输入非法牌或者输入同一张牌两次程序将忽略此牌产生出错消息然后要求输入另外一张牌。如果输入为0而不是一张牌就会导致程序终止。
程序运行会话如下所示
Enter a card: 2s
Enter a card: 5s
Enter a card: 4s
Enter a card: 3s
Enter a card: 6s
Straight flush Enter a card: 8c
Enter a card: as
Enter a card: 8c
Duplicate card; ignored.
Enter a card: 7c
Enter a card: ad
Enter a card: 3h
Pair Enter a card: 6s
Enter a card: d2
Bad card; ignored.
Enter a card: 2d
Enter a card: 9c
Enter a card: 4h
Enter a card: ts
High card Enter a card: 0从上述程序的描述可以看出它有3个任务
读入一手5张牌分析对子、顺子等情况显示一手牌的分类 把程序分为3个函数分别完成上述3个任务即read_cards函数、analyze_hand函数和print_result函数。main函数只负责在无限循环中调用这些函数。这些函数需要共享大量的信息所以让它们通过外部变量来进行交流。read_cards函数将与一手牌相关的信息存进几个外部变量中然后analyze_hand函数将检查这些外部变量把结果分类放在便于print_result函数显示的其他外部变量中。 /
poker.c
–Classifies a poker hand
/#include stdbool.h / C99 only /
#include stdio.h
#include stdlib.h #define NUM_RANKS 13
#define NUM_SUITS 4
#define NUM_CARDS 5 / external variables /
int num_in_rank[NUM_RANKS];
int num_in_suit[NUM_SUITS];
bool straight, flush, four, three;
int pairs; / can be 0, 1, or 2 / / prototypes */
void read_cards(void);
void analyze_hand(void);
void print_result(void); /********************************************************** * main: Calls read_cards, analyze_hand, and print_result * * repeatedly. * *********************************************************/
int main(void)
{ for (;;) { read_cards(); analyze_hand(); print_result(); }
}/********************************************************* * read_cards: Reads the cards into the external * * variables num_in_rank and num_in_suit; * * checks for bad cards and duplicate cards. * *********************************************************/
void read_cards(void)
{ bool card_exists[NUM_RANKS][NUM_SUITS]; char ch, rank_ch, suit_ch; int rank, suit; bool bad_card; int cards_read 0; for (rank 0; rank NUM_RANKS; rank) { num_in_rank[rank] 0; for (suit 0; suit NUM_SUITS; suit) card_exists[rank][suit] false; } for (suit 0; suit NUM_SUITS; suit) num_in_suit[suit] 0; while (cards_read NUM_CARDS) { bad_card false; printf(Enter a card: ); rank_ch getchar(); switch (rank_ch) { case 0: exit(EXIT_SUCCESS); case 2: rank 0; break; case 3: rank 1; break; case 4: rank 2; break; case 5: rank 3; break; case 6: rank 4; break; case 7: rank 5; break; case 8: rank 6; break; case 9: rank 7; break; case t: case T: rank 8; break; case j: case J: rank 9; break; case q: case Q: rank 10; break; case k: case K: rank 11; break; case a: case A: rank 12; break; default: bad_card true; } suit_ch getchar(); switch (suit_ch) { case c: case C: suit 0; break; case d: case D: suit 1; break; case h: case H: suit 2; break; case s: case S: suit 3; break; default: bad_card true; } while ((ch getchar()) ! \n) if (ch ! ) bad_card true; if (bad_card) printf(Bad card; ignored.\n); else if (card_exists[rank][suit])printf(Duplicate card; ignored.\n); else { num_in_rank[rank]; num_in_suit[suit]; card_exists[rank][suit] true; cards_read; } }
} /********************************************************* * analyze_hand: Determines whether the hand contains a * * straight, a flush, four-of-a-kind, * * and/or three-of-a-kind; determines the * * number of pairs; stores the results into * * the external variables straight, flush, * * four, three, and pairs. * **********************************************************/
void analyze_hand(void)
{ int num_consec 0; int rank, suit; straight false; flush false; four false; three false; pairs 0; /* check for flush / for (suit 0; suit NUM_SUITS; suit) if (num_in_suit[suit] NUM_CARDS) flush true; / check for straight / rank 0; while (num_in_rank[rank] 0) rank; for (; rank NUM_RANKS num_in_rank[rank] 0; rank) num_consec; if (num_consec NUM_CARDS) { straight true; return; } / check for 4-of-a-kind, 3-of-a-kind, and pairs */ for (rank 0; rank NUM_RANKS; rank) { if (num_in_rank[rank] 4) four true; if (num_in_rank[rank] 3) three true; if (num_in_rank[rank] 2) pairs; }
} /********************************************************** * print_result: prints the classification of the hand, * * based on the values of the external * * variables straight, flush, four, three, * * and pairs. * ************************************************************/
void print_result(void)
{ if (straight flush) printf(Straight flush);else if (four) printf(Four of a kind); else if (three pairs 1) printf(Full house); else if (flush) printf(Flush); else if (straight) printf(Straight); else if (three) printf(Three of a kind); else if (pairs 2) printf(Two pairs); else if (pairs 1) printf(Pair); else printf(High card); printf(\n\n);
}注意read_cards函数中exit函数的使用第一个switch语句的分支0。因为exit函数具有在任何地方终止程序执行的能力所以它对于此程序是十分方便的。
问与答 问1具有静态存储期的局部变量会对递归函数产生什么影响 答当函数是递归函数时每次调用它都会产生其自动变量的新副本。静态变量就不会发生这样的情况相反所有的函数调用都共享同一个静态变量。 问2在下面的例子中j初始化为和i一样的值但是有两个命名为i的变量 int i 1; void f(void)
{ int j i; int i 2; …
}这段代码是否合法如果合法j的初始值是1还是2
答代码是合法的。局部变量的作用域是从声明处开始的。因此j的声明引用了名为i的外部变量。j的初始值是1。 写在最后 本文是博主阅读《C语言程序设计现代方法第2版·修订版》时所作笔记日后会持续更新后续章节笔记。欢迎各位大佬阅读学习如有疑问请及时联系指正希望对各位有所帮助Thank you very much
- 上一篇: 如何知道自己网站租用的服务器去上海商城网站制作公司
- 下一篇: 如何制作app软件下载舟山网站建设优化
相关文章
-
如何知道自己网站租用的服务器去上海商城网站制作公司
如何知道自己网站租用的服务器去上海商城网站制作公司
- 技术栈
- 2026年03月21日
-
如何诊断网站为何被降权如何做免费的网站推广
如何诊断网站为何被降权如何做免费的网站推广
- 技术栈
- 2026年03月21日
-
如何找网站制作电子网站大全
如何找网站制作电子网站大全
- 技术栈
- 2026年03月21日
-
如何制作app软件下载舟山网站建设优化
如何制作app软件下载舟山网站建设优化
- 技术栈
- 2026年03月21日
-
如何制作h5做网站wordpress主机安装教程
如何制作h5做网站wordpress主机安装教程
- 技术栈
- 2026年03月21日
-
如何制作产品网站模板下载地址国外一个专门做配乐的网站
如何制作产品网站模板下载地址国外一个专门做配乐的网站
- 技术栈
- 2026年03月21日






