This commit is contained in:
hairrrrr
2020-04-22 16:12:33 +08:00
parent d3fcf08122
commit 6d8ed4e21e
57 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
#include<stdio.h>
int main(void) {
double balance = 0; // 余额
double credit, debit;
// 菜单,形式可以自己设计,尽量美观一点嘛,不过不用纠结这种界面,不要舍本逐末。
printf("**** ACME checkbook-balancing program ****\n");
printf(" Comands: \n");
printf(" 0 = clear \n");
printf(" 1 = credit \n");
printf(" 2 = debit \n");
printf(" 3 = balance \n");
printf(" 4 = exit \n");
// 题目中已经规定了这些功能用 01234 代替,其实是想让我们用 switch
// 如果你想用 if else 也完全 ok
// 死循环让用户可以重复选择
while (1) {
int choice;
printf("Enter command: ");
scanf("%d", &choice);
switch (choice) {
// 清除账户是一种很“危险”的举动,可以让用户确认一次
case 0: printf("Are you sure to clear your balance?\n");
printf("1 = yes, 0 = no\n");
int isClear;
scanf("%d", &isClear);
if (isClear == 1) {
balance = 0;
printf("clear successfully!\n");
}
break;
case 1: printf("Enter amount of credit: ");
scanf("%lf", &credit);
balance += credit;
break;
case 2: printf("Enter amount of debit : ");
scanf("%lf", &debit);
balance -= debit;
break;
case 3: printf("Current balance: %.2f\n", balance);
break;
case 4: printf("Are you sure to quit?\n");
printf("1 = yes, 0 = no\n");
int isQuit;
scanf("%d", &isQuit);
if (isQuit == 1) {
printf("Goodbye~\n");
return 0;
}
else {
break;
}
default: printf("Illeagl option!\n");
break;
}
}
}

View File

@@ -0,0 +1,22 @@
#### 程序:账薄结算
这个程序帮你理解一种简单的交互式程序设计,我们可以通过这种方式设计菜单。
题目:
开发一个程序用来维护账簿的余额。程序将为用户提供选择菜单:清空余额账户,向账户存钱,从账户取钱,显示当前余额,退出程序。选项用 01234表示。程序的会话类似这样
```c
**** ACME checkbook-balancing program ****
Comands: 0 = clear, 1 = credit, 2 = debit, 3 = balance, 4 = exit
Enter command: 1
Enter amount of credit: 1042.56
Enter command: 2
Enter amount of debit : 133.56
Enter command: 3
Current balance: 909
Ener command: 4
Goodbye~
```

View File

@@ -0,0 +1,35 @@
#include<stdio.h>
#define NUM_RATES (int)sizeof(value) / sizeof(value[0])
#define INITIAL_BALANCE 100.00
int main(void) {
int rate;
int year;
double value[5];
printf("Enter intrest rate: ");
scanf("%d", &rate);
printf("Enter number of years: ");
scanf("%d", &year);
printf("\nYears");
for (int i = 0; i < NUM_RATES; i++) {
printf("%7d%%", rate + i);
value[i] = INITIAL_BALANCE; // 初始化
}
printf("\n");
for (int i = 0; i < year; i++) {
printf("%3d ", i); // 补空格,让第一行和下面的行对齐
for (int j = 0; j < NUM_RATES; j++) {
value[j] += value[j] * (rate + j) / 100; // 注意这里不要写错
printf("%8.2f", value[j]);
}
printf("\n");
}
return 0;
}

View File

@@ -0,0 +1,18 @@
#### 程序:计算利息
编写一个程序显示一个表格。这个表格显示了几年时间内 100 美元投资在不同利率下的价值。用户输入利率和要投资的年数。投资总价值一年算一次,表格将显示输入的利率和紧随其后的 4 个更高的利率下投资的总价值。程序会话如下:
```c
Enter intrest rate: 6
Enter number of years: 2
Years 6% 7% 8% 9% 10%
1 106.00 107.00 108.00 109.00 110.00
2 112.36 114.49 116.64 118.81 121.00
```
第一行用一个 for 语句来显示。
我们在计算第一年的价值的时候将结果存放到数组中,然后使用数组中的结果继续计算下一年的价值。
在这一过程中我们将需要两个 for 语句,一个控制年份,一个控制不同的利率。

View File

@@ -0,0 +1,37 @@
#include<stdio.h>
#include<time.h>
#include<stdbool.h>
#include<stdlib.h>
#define NUM_SUIT 4
#define NUM_RANK 13
int main(void) {
int suit, rank, num_cards;
const char suit_code[] = {'H', 'D', 'C', 'S'}; // heartºìÌÒ diamand·½Æ¬ club÷»¨ spadeºÚÌÒ
const char rank_code[] = { '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A' };
bool in_hand[NUM_SUIT][NUM_RANK] = { false };
srand((unsigned)time(NULL));
printf("Enter number of cards in hand: ");
scanf("%d", &num_cards);
printf("Your card(s): ");
while (num_cards > 0) {
suit = rand() % NUM_SUIT;
rank = rand() % NUM_RANK;
if (!in_hand[suit][rank]) {
in_hand[suit][rank] = true;
num_cards--;
printf("%c%c ", suit_code[suit], rank_code[rank]);
}
}
printf("\n");
return 0;
}

View File

@@ -0,0 +1,32 @@
#### 程序:发牌
下面这个程序说明了二维数组和常量数组的用法。
**要求:**
程序负责发一副标准纸牌。每张标准指派都有一个花色梅花方块红桃黑桃和一个点数2 ~ 10, J, Q, K, A。用户需要指明发多少张牌
```c
Enter number of cards in hand: 5
Your card(s): S8 SA D7 H8 SK
```
**程序说明: **
- 创建两个常量数组,分别放置 4 中花色 和 13 个点数
- 程序要可以生成 随机数 。我们需要三个函数:
time <time.h>
srand <stdlib.h>
rand <stdlib.h>
这三个函数组合就可以完成这一功能,原理在我另一篇文章:【随机数发生器】 中讲解过。
- 生成的随机数必须在0 ~ 3 和 0 ~ 13 之间:
只需要让 `rand() % 4` 那么随机数就在 0 ~ 3 之间,另一个同理。
- 两次拿到的牌不能是一样的。创建一个 bool 类型的数组,开始时全部初始化 false。每次拿到两个随机数后如果数组对应的值为 false 那么将该元素置为 true 然后将此牌“发”给用户;否则,重新生成随机数。

View File

@@ -0,0 +1,33 @@
#include<stdio.h>
#include<stdbool.h>
bool is_prime(int n) {
int divisor;
if (n <= 1)
return false;
for (divisor = 2; divisor * divisor <= n; divisor++) {
if (n % divisor == 0)
return false;
}
return true;
}
int main(void) {
int n;
printf("Enter a number: ");
scanf("%d", &n);
if (is_prime(n))
printf("Prime\n");
else
printf("Not Prime\n");
return 0;
}

View File

@@ -0,0 +1,10 @@
#### 程序:判断素数
编写程序提示用户录入数,然后给出一条信息说明此数是否为素数。
```c
Enter a number: 24
Not prime
```
把判断素数的实现写到另外一个函数中,此函数返回值为 true 就表示是素数,返回 false 表示不是素数。

View File

@@ -0,0 +1,74 @@
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define MAX_NUMBER 100
int secret_number;// 要猜的数
void generate_secret_number();// 随机数生成
void read_guesses(); // 猜的实现
int main(void) {
char command;
printf("Guess the secret number between 1 and 100.\n");
do {
generate_secret_number();
printf("A new number has been chosen.\n");
read_guesses();
printf("Play again?(Y/N)");
scanf(" %c", &command);// 注意 %c 前的空格,这很重要
printf("\n");
} while (command == 'y' || command == 'Y');
return 0;
}
// 可以用这样的注释将函数的功能写在函数的定义的上方
// 我个人比较喜欢将简单的注释写在函数原型处
/****************************************************************************
*
* generate_secret_number: Initilizes the random number generator using the
* time of day.Randomly selects a number between
* 1 and MAX_NUMBER and stores it in secret_number
*
*****************************************************************************/
void generate_secret_number() {
srand((unsigned)time(NULL));
secret_number = rand() % MAX_NUMBER + 1;
}
/*****************************************************************
*
* read_guessesRepeatedly reads user guesses and gives hints
* When guess is right,prints the total number of
* guesses and returns
*
******************************************************************/
void read_guesses() {
int guess, count = 0;
for (;;) {
printf("Enter guess: ");
scanf("%d", &guess);
count++;
if (guess > secret_number) {
printf("Too high; try again\n");
}
else if (guess < secret_number) {
printf("Too low; try again.\n");
}
else {
printf("You won in %d guesses!\n\n", count);
return;
}
}

View File

@@ -0,0 +1,59 @@
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define MAX_NUMBER 100
int generate_secret_number();// Ëæ»úÊýÉú³É
void read_guesses(int secret_number); // ²ÂµÄʵÏÖ
int main(void) {
char command;
int secret_number;
printf("Guess the secret number between 1 and 100.\n");
do {
secret_number = generate_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;
}
int generate_secret_number() {
srand((unsigned)time(NULL));
int secret_number = rand() % MAX_NUMBER + 1;
return secret_number;
}
void read_guesses(int secret_number) {
int guess, count = 0;
for (;;) {
printf("Enter guess: ");
scanf("%d", &guess);
count++;
if (guess > secret_number) {
printf("Too high; try again\n");
}
else if (guess < secret_number) {
printf("Too low; try again.\n");
}
else {
printf("You won in %d guesses!\n\n", count);
return;
}
}
}

View File

@@ -0,0 +1,19 @@
#### 程序:猜数
程序产生一个 1 ~ 100 的随机数,用户尝试用尽可能少的次数猜出这个数。程序运行如下:
```c
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
You won in 3 guesses!
Play again?(Y/N) n
```
使用两种方法:使用全局变量/不使用全局变量

View File

@@ -0,0 +1,209 @@
/*
* 程序难点思路:
* 1为了判定手中的牌是否重复我们需要一个布尔类型数组存储整副牌初始化整个数组为 false。如果一张牌已经在我们手上那么我们将数组对应的元素置为 true
* 2用两个分别数组来存储每个点数和花色的个数这样方便我们后面判断牌的类型
* 38 种牌的类型,我们可拆成 同花顺子4张3张对子值为 012这五种基础类型的组合。
*
* 程序结构:
* 通过上面的分析,我们发现:这个程序需要 3 个数组和 5 个变量,如果都作为函数参数传参,显得有些笨。
* 而且,前面我们说过,函数只能返回一个值,那如果要将函数分离, 5 种基础类型就得放进数组;或者使用指针,而指针我们没有学习,而且指针还是逃不开传参
* 这样一分析,貌似使用全局变量是最好的做法了。对于初学者来说,这样可能确实是最好的。
* 但是,使用大量的全局变量是很不好的习惯,我不能让自己去写这样的代码。我认为:宁可这道题不做,也不能有坏的代码风格去写!
* 后面我们会学习自定义类型:结构体,它可能是这种问题最好的解决方法。
*
* 下面是这个问题的 4 种解决方法:
* 1)应用全局变量
* 2应用指针作为函数参数
* 3将判断卡牌类型的函数与打印函数合并
* 4使用结构体
*
* 在这里,我坚持使用结构体来解决这类问题。全局变量大家只要知道概念即可,对于这道题来说,比起方法,可能设计程序的模块化思路更值得学习。
* 即使使用结构体,程序的主要逻辑也不会变。如果你非要用全局变量写,那你可以改写一下。
*/
#include<stdio.h>
#include<stdbool.h>
#define RANK 13
#define SUIT 4
#define CARD 5
typedef struct CardType {
bool flush; //同花
bool straight; //顺子
bool four; //四张
bool three; //三张
int pair; // 对子
// 0 表示不是 1 表示 1个对子 2 表示两个对子
bool cardInHand[SUIT][RANK]; // 判断此牌是否已在手中
int numRank[RANK]; // 每个点数的个数
int numSuit[SUIT]; // 每个花色的个数
}CardType;
void initCardType(CardType* card); // 初始化
void readCard(CardType* card); // 读取输入
void analyseCard(CardType* card); // 分析手牌
void printResult(CardType* card); //打印结果
int main(void) {
CardType card;
for (; ;) {
initCardType(&card);
readCard(&card);
analyseCard(&card);
printResult(&card);
}
return 0;
}
void initCardType(CardType* card) {
card->flush = false;
card->straight = false;
card->four = false;
card->three = false;
card->pair = 0;
int i, j;
for (i = 0; i < SUIT; i++) {
card->numSuit[i] = 0;
for (j = 0; j < RANK; j++) {
card->cardInHand[i][j] = false;
}
}
for (i = 0; i < RANK; i++) {
card->numRank[i] = 0;
}
}
void readCard(CardType* card) {
int card_read = CARD, rank, suit;
bool bad_card;
char ch;
while (card_read) {
bad_card = false; // 不要忘记重置坏牌的标记
printf("Enter a card : ");
// 判断点数
ch = getchar();
switch (ch) {
case '0': exit(0); break;
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; break;
}
ch = getchar();
switch (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; break;
}
// 精华 1
// 检测输入是否多于两个字符
while ((ch = getchar()) != '\n') {
if (ch != ' ')
bad_card = true;
}
if (bad_card)
printf("Bad card; ignored.\n");
else if (card->cardInHand[suit][rank])
printf("Duplicated card; ignored.\n");
else {
++card->numRank[rank];
++card->numSuit[suit];
card->cardInHand[suit][rank] = true;
card_read--;
}
}
}
void analyseCard(CardType* card) {
int i, count;
// 同花是五张牌相同花色
for (i = 0; i < SUIT; i++) {
if (card->numSuit[i] == 5)
card->flush = true;
}
// 精华 2
// 顺子是五张连续的牌,中间不能隔断
i = 0;
// 找到数组种第一张存在的牌
while (card->numRank[i] == 0)
i++;
count = 0;
for (; i < RANK && card->numRank[i] != 0; i++) {
count++;
}
// 顺子必须是五张
if (count == CARD) {
card->straight = true;
return; // 顺子肯定不是对子
}
for (i = 0; i < RANK; i++) {
if (card->numRank[i] == 4)
card->four = true;
if (card->numRank[i] == 3)
card->three = true;
if (card->numRank[i] == 2)
++card->pair;
}
}
void printResult(CardType* card) {
if (card->flush && card->straight)
printf("Stright flush\n");
else if (card->four)
printf("Four of a kind\n");
else if (card->three && card->pair == 1)
printf("Full house\n");
else if (card->flush)
printf("flush\n");
else if (card->straight)
printf("straight\n");
else if (card->three)
printf("Three of a kind\n");
else if (card->pair == 2)
printf("Two pairs\n");
else if (card->pair == 1)
printf("pair\n");
else
printf("High card\n");
printf("\n\n");
}

View File

@@ -0,0 +1,56 @@
#### 程序:手牌分类
编写程序对手牌进行读取和分类。手中的每张牌都有花色方块梅花红桃和黑桃和等级23456789TJQK 和 A。不允许使用王牌并且假设 A 是最高等级的。一手 5 张牌,然后把手中的牌分为下列某一类(列出的顺序从好到坏)。
- 同花顺(顺序连续且同花色)
- 四张4 张牌等级相同)
- 葫芦3 张牌等级一样另外2 张等级一样)
- 同花5 张牌同花色)
- 顺子5 张牌等级顺序连续)
- 三张3 张牌等级连续)
- 两对
- 一对2 张牌等级一样)
- 其他牌
如果一手牌可以分为两种或多种类别,程序将选择最好的一种。
为了便于输入,将牌的等级和花色简化如下:
- 等级: 23456789TJQK A
- 花色c d h s
如果用户输入非法牌或者输入同一张牌两次,程序将此牌忽略掉,产生错误信息,然后要求输入另一张牌。如果输入为 0 而不是一张牌,就会导致程序终止。
与程序的会话如下:
```c
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
Duplicated 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
```

View File

@@ -0,0 +1,45 @@
#include<stdio.h>
#define SIZE 5
void max_min(int a[], int len, int* max, int* min);
int main(void) {
int a[SIZE];
int max, min, i;
printf("Enter 5 numbers: ");
for (i = 0; i < SIZE; i++)
scanf("%d", &a[i]);
max_min(a, SIZE, &max, &min);
printf("Largest: %d\n", max);
printf("Smallest: %d\n", min);
return 0;
}
void max_min(int a[], int len, int* max, int* min) {
int i;
*max = *min = a[0];
for (i = 1; i < len; i++) {
// a[i] 如果比 *max 大 那肯定不会比 *min 小,反之也成立
if (a[i] > * max)
*max = a[i];
else if (a[i] < *min)
*min = a[i];
}
// 我自己写的,因为我这个算法每一次循环都要判断两次 if效率肯定不如上面的高
//for (i = 1; i < len; i++) {
// if (*max < a[i])
// *max = a[i];
// if (*min > a[i])
// *min = a[i];
//}
}

View File

@@ -0,0 +1,10 @@
#### 程序:找出数组中的最大元素和最小元素
与程序的交互如下:
```c
Enter 5 numbers:9 5 2 7 8
Largest: 9
Smallest: 2
```

View File

@@ -0,0 +1,16 @@
int read_line(char str[], int read_num) {
int ch, i = 0;
while ((ch = getchar()) != '\n' && ch != EOF) {
// i 大于 read_num 不执行操作,跳过后面的字符
if (i < read_num)
str[i++] = ch;
}
str[i] = '\0';
return i;
}
// ch 的类型是 int 而不是 char ,只是因为 getchar 把它读入的字符作为 int 类型的值返回。

View File

@@ -0,0 +1,25 @@
#### 3. 逐个字符读取字符串
因为对许多程序而言scanf 函数和 gets 函数都有风险而且不够灵活C 程序员经常会自己编写输入函数。通过每次读一个字符的方式读取字符串。
如果决定自己设计输入函数,那么需要考虑以下问题:
- 在开始存储字符串之前,函数应该跳过空白字符吗?
- 什么字符导致函数停止读取:换行符,任意空白字符,还是其他某种字符?需要存储这些字符还是忽略掉?
- 如果输入的字符串太长以至于无法存储,那么函数应该忽略额外的字符还是把它们留给下一次输入操作?
示例中,我们选择:不跳过空白字符,换行符结束,不存储换行符,忽略掉额外字符。
函数原型如下:
```c
int read_line(char str[], int read_num);
```
参数str 表示存储输入的数组read_num 表示读入字符的最大数量。
返回值:返回读入字符的个数。
使用 getchar 实现按字符读入。

View File

@@ -0,0 +1,20 @@
#### 程序:显示一个月的提醒列表
此程序会显示每一个月的每日提醒列表。用户需要输入一系列提醒,每条提醒都要有一个前缀来说明是那一个月中的那一天。当用户输入的是 0 而不是有效日期时,程序会显示出录入的全部提醒列表(按日期排序)。下面是会话示例:
```c
Enter day and reminder: 24 Suan's birstday
Enter day and reminder: 5 6:00 - Dinner with Marge
Enter day and reminder: 7 10:30 - Movie - "Chinatown"
Enter day and reminder: 0
Day Reminder:
5 6:00 - Dinner with Marge
7 10:30 - Movie - "Chinatown"
24 Suan's birstday
```
- 读入提醒使用我们写的 read_line 函数
- 将提醒存放在二维数组中,数组的每一行看作一个字符串。日期和提示消息都要放进去 。
- 日期我们用整型输入,然后转换为字符串放入二维数组的前面。
- 每次读入新的日期和提示消息后,将转为字符串的当前日期和二维数组每行前面表示日期的部分比较。如果当前日期字符串小于二维数组当前行的字符串,说明当前日期较小,应当插入到当前数组的行前一行。我们可以将二维数组从当前行到存放提示的最后一行每行依次向后移动一行,从而使得当前日期和提示可以插入二维数组的当前行。
- 打印二维数组

View File

@@ -0,0 +1,76 @@
#include<stdio.h>
#include<string.h>
#define MAX_REMIND 50
#define MSG_LEN 100
int read_line(char str[], int read_num);
int main(void) {
char reminders[MAX_REMIND][MSG_LEN + 3]; // 存放提示的数组
char day_str[3];//当前日期转换为字符串
char msg_str[MSG_LEN + 1]; //当前输入的提示消息
int day, num_remind = 0; // 日期和当前提示数
int i, j;
for (;;) {
if (num_remind == MAX_REMIND) {
printf("-- No space left --\n");
break;
}
printf("Enter day and reminder:");
scanf("%2d", &day); //每月的日期只用两个数表示即可,只读 2 个字段
if (day == 0)
break;
sprintf(day_str, "%2d", day); // 将 day 以 "%2d" 的格式写入 day_str 字符数组中。"%2d" 保证小于10的天占两位右对齐
read_line(msg_str, MSG_LEN);
// 寻找当前输入的提示应该放到提示数组的那个位置
for (i = 0; i < num_remind; i++) {
// 说明当前输入的日期应该排在此行前
if(strcmp(day_str, reminders[i]) < 0)
break;
}
// 将当前输入的提示插入到正确的位置
for (j = num_remind; j > i; j--) {
strcpy(reminders[j], reminders[j - 1]);
}
strcpy(reminders[i], day_str);
strcat(reminders[i], msg_str);// 刚好将 day_str 复制进去的空字符覆盖掉了
num_remind++;
}
printf("Day Reminder: \n");
for (i = 0; i < num_remind; i++)
printf("%s\n", reminders[i]);
return 0;
}
int read_line(char str[], int read_num) {
int ch, count = 0;
while ((ch = getchar()) != '\n') {
if (count < read_num) {
str[count++] = ch;
}
}
str[count] = '\0';
return count;
}

View File

@@ -0,0 +1,27 @@
#include<stdio.h>
#include<string.h>
#define NUM_PLANETS 9
int main(int argc, char* argv[]) {
int i, j;
char* planets[NUM_PLANETS] = {
"Mercury", "Venus", "Earth",
"Mars", "Jupiter", "Saturn",
"Uranus", "Neptune", "Pluto"
};
for (i = 1; i < argc; i++) {
for (j = 0; j < NUM_PLANETS; j++)
if (strcmp(argv[i], planets[j]) == 0) {
printf("%s is a planet %d\n", argv[i], j + 1);
break;
}
if (j == NUM_PLANETS)
printf("%s is not a planet\n", argv[i]);
}
return 0;
}
// 程序会依次访问每个命令行参数,把它与 planets 中的字符串进行比较,直到找到匹配的名字或到了数组末尾才停止。

View File

@@ -0,0 +1,21 @@
#### 程序:核对行星的名字
设计一个程序检查一系列字符串,从而找出那些字符串是行星的名字。执行程序时,用户把待测试的字符串放置在命令行中:
```c
planet Mercury Aotoman Pluto Thebug Earth
```
程序会指出每个字符串是否为行星名。如果是,程序还将显示行星的编号:
```c
Mercury is a planet 1
Aotoman is not a planet
Pluto is a planet 9
Thebug is not a planet
Earth is a planet 3
```
**注意:**命令行输入的第一个参数 planet 是 c 程序编译出的可执行程序名。一般一个叫 x.c 的程序编译后的可执行程序就叫做 x 。
我们命名这个 c 程序为 planet.c 所以编译后的可执行文件应该叫做 planet (在 Windows 上后缀应该为 .exe

View File

@@ -0,0 +1,37 @@
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include"word.h"
#include"line.h"
#define MAX_WORD_LEN 20 //每个单词的最大长度
int main(void) {
char word[MAX_WORD_LEN + 2];
int word_len;
clear_line();
for (;;) {
// 允许 read_word 函数多读 1 个字符,多读则代表单词长度超过 20需要截断
read_word(word, MAX_WORD_LEN + 1);
word_len = strlen(word);
if (word_len == 0) {
flush_line();
return 0;
}
// 截断超过 20 个字符的单词
if (word_len > MAX_WORD_LEN)
word[MAX_WORD_LEN] = '*';
// + 1 是因为需要在上一个单词后添加空格。
// 如果满足条件,则需要输出当前行并清空当前行
if (word_len + 1 > space_remaining()) {
write_line();
clear_line();
}
add_word(word);
}
return 0;
}

View File

@@ -0,0 +1,63 @@
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include"line.h"
#define MAX_LINE_LEN 60 // 每行的最大字符数
char line[MAX_LINE_LEN + 1];
int line_len = 0; // 当前行长度
int num_words = 0; // 当前行的单词数
void clear_line() {
line[0] = '\0';
line_len = 0;
num_words = 0;
}
void add_word(const char* word) {
// 非首个单词,需要在上一个单词后添加空格
if (num_words > 0) {
line[line_len] = ' ';
line[line_len + 1] = '\0';
line_len++;
}
strcat(line, word);
line_len += strlen(word);
num_words++;
}
int space_remaining() {
return MAX_LINE_LEN - line_len;
}
void write_line() {
int extra_space, spaces_to_insert, i, j;
extra_space = MAX_LINE_LEN - line_len; // 当前行未被填满的字符数
for (i = 0; i < line_len; i++) {
if (line[i] != ' ')
putchar(line[i]);
else {
spaces_to_insert = extra_space / num_words; // 遵循这个公式来增加空格
for (j = 0; j <= spaces_to_insert; j++) // 使用 = 确保至少打印一个
putchar(' ');
extra_space -= spaces_to_insert;
num_words--;
}
}
putchar('\n');
}
void flush_line(void) {
if (line_len > 0)
puts(line);
}

View File

@@ -0,0 +1,48 @@
#ifndef LINE_H
#define LINE_H
/********************************************************
*
* clear_line: Clears the current line.
*
*********************************************************/
void clear_line();
/********************************************************
*
* add_word: Adds word to the end of current line.
* If this is not the first word on the line,
* puts one space before word.
*
*********************************************************/
void add_word(const char* word);
/********************************************************
*
* space_remaining: Returns the number of characters left
* in the current line.
*
*********************************************************/
int space_remaining();
/********************************************************
*
* write_line: Writes the current line with justification.
*
*********************************************************/
void write_line();
/********************************************************
*
* flush_line: Write the current line without
* justification.If the line is empty,
* does nothing.
*
*********************************************************/
void flush_line(void);
#endif

View File

@@ -0,0 +1,10 @@
C is quirky, flawed, and an
enormous success. Although accidents of history
surely helped, it evidently satisfied a need
for a system implementation language efficient
enough to displace assembly language,
yet sufficiently abstract and fluent to describe
algorithms and interactions in a wide variety
of environments.
-- Dennis M. Ritchie

View File

@@ -0,0 +1,43 @@
### 二 把程序划分成多个文件
#### 程序:文本格式化
输入未格式化的引语:来自 Dennis M. Ritchie 写的"The Development of the C programming language" 一文:
```c
C is quirky, flawed, and an
enormous success. Although accidents of history
surely helped, it evidently satisfied a need
for a system implementation language efficient
enough to displace assembly language,
yet sufficiently abstract and fluent to describe
algorithms and interactions in a wide variety
of environments.
-- Dennis M. Ritchie
```
程序完成对这段文字的调整:
```c
C is quirky, flawed, and an enormous success. Although
accidents of history surely helped, it evidently satisfied a
need for a system implementation language efficient enough
to displace assembly language, yet sufficiently abstract and
fluent to describe algorithms and interactions in a wide
variety of environments. -- Dennis M. Ritchie
```
程序分析:
完成这个程序需要两步:读入和输出。
读入我们选择按单词读入到当前行中,然后按当前行输出。注意输出的每一行最后“对”的很齐,我们 write_line 函数对这种格式做了特殊处理。
按单词读入我们创建 word.h 和 word.c
按行输出我们创建 line.h 和 line.c
最后用 justify.c 包含 main 函数
![](./重定向.png)

View File

@@ -0,0 +1,33 @@
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"word.h"
// 解决换行符和制表符问题
int read_char(void) {
int ch = getchar();
if (ch == '\n' || ch == '\t')
return ' ';
return ch;
}
void read_word(char* word, int len) {
int ch, i;
while ((ch = read_char()) == ' ')
;
i = 0;
while (ch != ' ' && ch != EOF) {
if (i < len)
word[i++] = ch;
ch = read_char();
}
word[i] = '\0';
}

View File

@@ -0,0 +1,14 @@
#ifndef WORD_H
#define WORD_H
/***********************************************************************
*
* read_word: Read the next word from the input and stores it in word.
* Make word empty if no word could be read because of EOF.
* Truncates the word if its length exceeds len.
*
************************************************************************/
void read_word(char* word, int len);
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View File

@@ -0,0 +1,194 @@
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"readline.h"
#define NAME_LEN 20
#define MAX_PARTS 100
struct part {
int number;
char name[NAME_LEN + 1];
int on_hand;
}inventory[MAX_PARTS];
int num_parts = 0; //number of parts current stored
void menu();
int find_part(int number);
void insert();
void search();
void update();
void print();
int main(void) {
char code = 'a';
menu();
for (;;) {
printf("Enter operation code: ");
scanf(" %c", &code);
while (getchar() != '\n') // ships to end of line
;
switch (code) {
case 'i': insert(); break;
case 's': search(); break;
case 'u': update(); break;
case 'p': print(); break;
case 'q': return 0;
default: printf("Illegal code.\n"); break;
}
}
return 0;
}
void menu() {
printf(" ==================================\n");
printf(" * *\n");
printf(" * i: insert *\n");
printf(" * s: search *\n");
printf(" * u: undate *\n");
printf(" * p: print *\n");
printf(" * q: quit *\n");
printf(" * *\n");
printf(" ==================================\n");
}
/**********************************************************
*
* find_part: Looks up a part number in the inventory
* array.Returns the array index if the part
* number is found;otherwise,return -1
*
***********************************************************/
int find_part(int number) {
int i;
for (i = 0; i < num_parts; i++) {
if (inventory[i].number == number)
return i;
}
return -1;
}
/**********************************************************
*
* insert: Inserts the part into the database.Prints
* an error message and returns prematurely
* if the part already exists or the database
* is full.
*
***********************************************************/
void insert() {
int part_number;
if (num_parts == MAX_PARTS) {
printf("Database is full; can't add more parts.\n");
return;
}
printf("Enter part number: ");
scanf("%d", &part_number);
if (find_part(part_number) >= 0) {
printf("Part already exists.\n");
return;
}
inventory[num_parts].number = part_number;
printf("Enter part name: ");
read_line(inventory[num_parts].name, NAME_LEN);
printf("Enter quantity on hand: ");
scanf("%d", &inventory[num_parts].on_hand);
num_parts++;
}
/************************************************************
*
* search: Look up a part by the number user enters.
* If the part exists, prints the name and quantity
* on hand;if not, print an error message.
*
************************************************************/
void search() {
int index, number;
printf("Enter part number: ");
scanf("%d", &number);
index = find_part(number);
if (index == -1) {
printf("Part not found.\n");
return;
}
printf("Part name: %s\n", inventory[index].name);
printf("Quantity on hand: %d\n", inventory[index].on_hand);
}
/************************************************************
*
* update: Prompts user to enter a number.
* Print an error message if the part doesn't exist;
* otherwise,prompts the user to enter change in
* quantity on hand and updates the database.
*
************************************************************/
void update() {
int number, index, change;
printf("Enter part number: ");
scanf("%d", &number);
index = find_part(number);
if (index == -1) {
printf("Part not found.\n");
return;
}
printf("Enter change in quantity on hand(- means minus): ");
scanf("%d", &change);
inventory[index].on_hand += change;
}
/************************************************************
*
* print: Print a listing of all parts in the database,
* showing the part number,part name and quantity
* on hand.Parts are printed in the order in which
* they were entered into the database.
*
************************************************************/
void print() {
int i;
printf("Part Number Part Name Quantity on Hand\n");
for (i = 0; i < num_parts; i++) {
printf("%6d%20s%15d\n", inventory[i].number, inventory[i].name, inventory[i].on_hand);
}
}

View File

@@ -0,0 +1,24 @@
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<ctype.h>
#include"readline.h"
int read_line(char str[], int n) {
int ch, i = 0;
while (isspace(ch = getchar()))
;
while (ch != '\n' && ch != EOF) {
if (i < n)
str[i++] = ch;
ch = getchar();
}
str[i] = '\0';
return i;
}

View File

@@ -0,0 +1,16 @@
#ifndef READLINE_H
#define READLINE_H
/***********************************************************
*
* read_line: Skips leading white-space characters, then
* reads the remainder of the input line and
* stores it in str. Truncates the line if its
* length exceeds n. Return the number of
* characters stores.
*
***********************************************************/
int read_line(char str[], int n);
#endif

View File

@@ -0,0 +1,51 @@
#### 程序:维护零件数据库
此程序用来维护仓库存储的零件信息的数据库。程序围绕一个结构数组构建,且每个结构包含以下信息:零件编号,名称和数量。程序将支持下列操作:
- **添加新零件信息**。如果零件已经存在,或数据库已满,显示出错信息。
- **给定零件编号,显示零件的名称,数量信息**。如果零件编号不存在,那么给出出错信息。
- **给定零件编号,改变零件的数量**。如果零件编号不存在,给出出错消息。
- **显示列出数据库中的全部信息**。零件必须按照录入顺序显示。
- **终止程序的执行**
使用:
- `i`:插入
- `s`:搜索
- `u`:更新
- `p`:显示
- `q`:退出
分表表示这种操作,与程序得到会话如下:
```c
Enter operation code: i
Enter part number: 833
Enter part name: Disk Drive
Enter quantity on hand: 90
Enter operation code: i
Enter part number: 788
Enter part name: USB 3.0
Enter quantity on hand: 67
Enter operation code: s
Enter part number: 832
Part not found.
Enter operation code: 833
Illegal code.
Enter operation code: s
Enter part number: 833
Part name: Disk Drive
Quantity on hand: 90
Enter operation code: u
Enter part number: 788
Enter change in quantity on hand(- means minus): 3
Enter operation code: p
Part Number Part Name Quantity on Hand
833 Disk Drive 90
788 USB 3.0 70
Enter operation code: q
```
注意:菜单可以没有
因为 readline 函数和这个程序的主干没有太大关系,我们用单独的头文件和源文件包含它。

View File

@@ -0,0 +1,5 @@
#### 程序:显示一个月的提醒列表
前面我们把字符串存储在二维数组中,但是这可能会浪费空间。后面的教学中我们设想使用指针数组存储字符串,让一维数组的每个元素都指向一个字符串字面量。如果数组元素是指向动态分配的字符串的指针,那么是可以实现我们的设想的。
下面的程序对之前的程序作了小部分修改,修改的地方后面用注释注明了。

View File

@@ -0,0 +1,86 @@
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAX_REMIND 50
#define MSG_LEN 100
int read_line(char str[], int read_num);
int main(void) {
char* reminders[MAX_REMIND]; // 存放提示的数组 // change
char day_str[3];//当前日期转换为字符串
char msg_str[MSG_LEN + 1]; //当前输入的提示消息
int day, num_remind = 0; // 日期和当前提示数
int i, j;
for (;;) {
if (num_remind == MAX_REMIND) {
printf("-- No space left --\n");
break;
}
printf("Enter day and reminder:");
scanf("%2d", &day); //每月的日期只用两个数表示即可,只读 2 个字段
if (day == 0)
break;
sprintf(day_str, "%2d", day); // 将 day 以 "%2d" 的格式写入 day_str 字符数组中。"%2d" 保证小于10的天占两位右对齐
read_line(msg_str, MSG_LEN);
// 寻找当前输入的提示应该放到提示数组的那个位置
for (i = 0; i < num_remind; i++) {
// 说明当前输入的日期应该排在此行前
if (strcmp(day_str, reminders[i]) < 0)
break;
}
// 将当前输入的提示插入到正确的位置
for (j = num_remind; j > i; j--) {
reminders[j] = reminders[j - 1]; // change
}
reminders[i] = (char*)malloc(sizeof(msg_str) + sizeof(day_str) + 1); // change
// change
if (reminders[i] == NULL) {
printf("-- No space left --\n");
break;
}
strcpy(reminders[i], day_str);
strcat(reminders[i], msg_str);// 刚好将 day_str 复制进去的空字符覆盖掉了
num_remind++;
}
printf("Day Reminder: \n");
for (i = 0; i < num_remind; i++)
printf("%s\n", reminders[i]);
return 0;
}
int read_line(char str[], int read_num) {
int ch, count = 0;
while ((ch = getchar()) != '\n') {
if (count < read_num) {
str[count++] = ch;
}
}
str[count] = '\0';
return count;
}

View File

@@ -0,0 +1,234 @@
#include<stdio.h>
#include<stdlib.h>
#include"readline.h"
#define NAME_LEN 20
typedef struct part {
int number;
char name[NAME_LEN + 1];
int on_hand;
struct part* next;
}part;
void menu();
part* find_part(part* head, int number);
void insert(part* head);
void search(part* head);
void update(part* head);
void print(part* head);
int main(void) {
char code = 'a';
part* head = (part*)malloc(sizeof(part));
head->next = NULL;
if (head == NULL) {
printf("Database establish failed\n");
exit(EXIT_SUCCESS);
}
menu();
for (;;) {
printf("Enter operation code: ");
scanf(" %c", &code);
while (getchar() != '\n') // skips to end of line
;
switch (code) {
case 'i': insert(head); break;
case 's': search(head); break;
case 'u': update(head); break;
case 'p': print(head); break;
case 'q': return 0;
default: printf("Illegal code.\n"); break;
}
}
return 0;
}
void menu() {
printf(" ==================================\n");
printf(" * *\n");
printf(" * i: insert *\n");
printf(" * s: search *\n");
printf(" * u: undate *\n");
printf(" * p: print *\n");
printf(" * q: quit *\n");
printf(" * *\n");
printf(" ==================================\n");
}
/**********************************************************
*
* find_part: Looks up a part number in the inventory
* array.Returns the array index if the part
* number is found;otherwise,return -1
*
***********************************************************/
part* find_part(part* head, int number) {
part* cur;
// 链表是按照编号升序排序的
for (cur = head->next; cur != NULL && cur->number > number;
cur = cur->next)
;
if (cur == NULL)
return NULL;
if (cur->number == number)
return cur;
}
/**********************************************************
*
* insert: Inserts the part into the database.Prints
* an error message and returns prematurely
* if the part already exists or the database
* is full.
*
***********************************************************/
void insert(part* head) {
int part_number;
part* cur, * prev, *new_part;
printf("Enter part number: ");
scanf("%d", &part_number);
// 寻找 part_number 所应插入的位置,我们需要 cur 遍历链表,但是应该保留 cur 前面的结点 prev
// 退出循环条件cur == NULL 说明是头插或尾插
// cur->number > part_number 说明 输入的编号重复
// 应该在 cur 和 prev 之间插入新的零件 或 头插
for (cur = head->next, prev = NULL;cur != NULL && cur->number < part_number ;
prev = cur, cur = cur->next)
;
// 判断输入的编号是否于数据库中的现有重复
if (cur != NULL && cur->number == part_number) {
printf("Part already exists.\n");
return;
}
// 申请新结点
new_part = (part*)malloc(sizeof(part));
// 判断申请是否成功
if (new_part == NULL) {
printf("Database is full; can't add more parts.\n");
return;
}
new_part->number = part_number;
printf("Enter part name: ");
read_line(new_part->name, NAME_LEN);
printf("Enter quantity on hand: ");
scanf("%d", &new_part->on_hand);
// 插入的方式:
// 链表为空时:对 head 进行操作prev == NULL, cur == NULL
// 链表不为空:
// 头插:对 head 操作 (prev == NULL, cur != NULL)
// 尾插:对 prev 操作 (prev != NULL, cur == NULL)
// 普通位置插入:对 prev 操作(prev,cur 都不为 NULL)
new_part->next = cur;
if (prev == NULL)
head->next = new_part;
else
prev->next = new_part;
}
/************************************************************
*
* search: Look up a part by the number user enters.
* If the part exists, prints the name and quantity
* on hand;if not, print an error message.
*
************************************************************/
void search(part* head) {
int number;
part* trg;
printf("Enter part number: ");
scanf("%d", &number);
trg = find_part(head, number);
if (trg == NULL) {
printf("Part not found.\n");
return;
}
printf("Part name: %s\n", trg->name);
printf("Quantity on hand: %d\n", trg->on_hand);
}
/************************************************************
*
* update: Prompts user to enter a number.
* Print an error message if the part doesn't exist;
* otherwise,prompts the user to enter change in
* quantity on hand and updates the database.
*
************************************************************/
void update(part* head) {
int number, change;
part* trg;
printf("Enter part number: ");
scanf("%d", &number);
trg = find_part(head, number);
if (trg == NULL) {
printf("Part not found.\n");
return;
}
printf("Enter change in quantity on hand(- means minus): ");
scanf("%d", &change);
trg->on_hand += change;
}
/************************************************************
*
* print: Print a listing of all parts in the database,
* showing the part number,part name and quantity
* on hand.Parts are printed in the order in which
* they were entered into the database.
*
************************************************************/
void print(part* head) {
printf("Part Number Part Name Quantity on Hand\n");
for (part* cur = head->next; cur != NULL; cur = cur->next) {
printf("%6d%20s%15d\n", cur->number, cur->name, cur->on_hand);
}
}

View File

@@ -0,0 +1,6 @@
#### 程序:维护零件数据库
下面重做前面的程序,这次把数据库存储在链表中。链表代替数组主要有两个好处:
1. 不需要事先限制数据库的大小
2. 可以很容易地按零件编号对数据库排序(本程序采用默认升序排序)

View File

@@ -0,0 +1,40 @@
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include"stack.h"
#define STACK_SIZE 100
static int contents[STACK_SIZE];
static int top = 0;
static void terminate(const char* message) {
printf("%s\n", message);
exit(EXIT_FAILURE);
}
void make_empty() {
top = 0;
}
bool is_empty() {
return top == 0;
}
bool is_full() {
return top == STACK_SIZE;
}
void push(int i) {
if (is_full())
terminate("Error in push: stack is full\n");
contents[top++] = i;
}
int pop() {
if (is_empty())
printf("Error in pop: stack is empty\n");
return contents[--top];
}

View File

@@ -0,0 +1,57 @@
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include"stack.h"
typedef struct node {
int data;
struct node* next;
}node;
static node* top = NULL;
static void terminate(char* message) {
printf("%s\n", message);
exit(EXIT_FAILURE);
}
void make_empty() {
while (!is_empty())
pop();
}
bool is_empty() {
return top == NULL;
}
bool is_full() {
return false;
}
void push(int i) {
node* new_node = (node*)malloc(sizeof(node));
if (new_node == NULL) {
terminate("Error in push: stack is full.\n");
exit(EXIT_FAILURE);
}
new_node->data = i;
new_node->next = top;
top = new_node;
}
int pop() {
if (is_empty())
terminate("Error in pop: stack is empty.\n");
int data = top->data;
node* del = top;
top = top->next;
free(del);
return data;
}

View File

@@ -0,0 +1,12 @@
#ifndef STACK_H
#define STACK_H
#include<stdbool.h> //C99 only
void make_empty();
bool is_empty();
bool is_full();
void push(int i);
int pop();
#endif

View File

@@ -0,0 +1,71 @@
//#define _CRT_SECURE_NO_WARNINGS 1
//
//#include<stdio.h>
//#include<stdlib.h>
//#include"stackADT.h"
//
//#define STACK_SIZE 100
//
//typedef struct stack_type {
// int contents[STACK_SIZE];
// int top;
//}stack_type;
//
//
//
//static void terminate(char* message) {
// printf("%s\n", message);
// exit(EXIT_FAILURE);
//}
//
//Stack create() {
//
// Stack s = (Stack)malloc(sizeof(stack_type));
// if (s == NULL) {
// terminate("Error in create: stack could not be created.\n");
// exit(EXIT_FAILURE);
// }
// s->top = 0;
//
// return s;
//}
//
//
//void destory(Stack s) {
//
// free(s);
//}
//
//
//void make_empty(Stack s) {
//
// s->top = 0;
//}
//
//bool is_empty(Stack s) {
// return s->top == 0;
//}
//
//bool is_full(Stack s) {
// return s->top == STACK_SIZE;
//}
//
//void push(Stack s, int i) {
//
// if (is_full(s)) {
// terminate("Error in push: stack is full.\n");
// exit(EXIT_FAILURE);
// }
//
// s->contents[s->top++] = i;
//}
//
//int pop(Stack s) {
//
// if (is_empty(s)) {
// terminate("Error in pop: stack is empty.\n");
// exit(EXIT_FAILURE);
// }
//
// return s->contents[--s->top];
//}

View File

@@ -0,0 +1,16 @@
#ifndef STACKADT_H
#define STACKADT_H
#include<stdbool.h>
typedef struct stack_type* Stack;
Stack create();
void destory(Stack s);
void make_empty(Stack s);
bool is_empty(const Stack s);
bool is_full(const Stack s);
void push(Stack s, int i);
int pop(Stack s);
#endif

View File

@@ -0,0 +1,73 @@
//#define _CRT_SECURE_NO_WARNINGS 1
//
//#include<stdio.h>
//#include<stdlib.h>
//#include"stackADT3.h"
//
//
//typedef struct stack_type {
// int top;
// int size;
// Item contents[]; // 柔性数组
//}stack_type;
//
//
//
//static void terminate(char* message) {
// printf("%s\n", message);
// exit(EXIT_FAILURE);
//}
//
//Stack create(int size) {
//
// // sizeof(stack_type) 的大小不含有柔性数组
// Stack s = (Stack)malloc(sizeof(stack_type) + sizeof(Item) * size);
// if (s == NULL) {
// terminate("Error in create: stack could not be created.\n");
// exit(EXIT_FAILURE);
// }
// s->top = 0;
// s->size = size;
//
// return s;
//}
//
//
//void destory(Stack s) {
//
// free(s); // 柔性数组只需要释放一次
//}
//
//
//void make_empty(Stack s) {
//
// s->top = 0;
//}
//
//bool is_empty(Stack s) {
// return s->top == 0;
//}
//
//bool is_full(Stack s) {
// return s->top == s->size;
//}
//
//void push(Stack s, Item i) {
//
// if (is_full(s)) {
// terminate("Error in push: stack is full.\n");
// exit(EXIT_FAILURE);
// }
//
// s->contents[s->top++] = i;
//}
//
//Item pop(Stack s) {
//
// if (is_empty(s)) {
// terminate("Error in pop: stack is empty.\n");
// exit(EXIT_FAILURE);
// }
//
// return s->contents[--s->top];
//}

View File

@@ -0,0 +1,18 @@
#ifndef STACKADT3_H
#define STACKADT3_H
#include<stdbool.h>
typedef int Item;
typedef struct stack_type* Stack;
Stack create(int size);
void destory(Stack s);
void make_empty(Stack s);
bool is_empty(const Stack s);
bool is_full(const Stack s);
void push(Stack s, Item i);
Item pop(Stack s);
#endif

View File

@@ -0,0 +1,79 @@
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include"stackADT4.h"
typedef struct node{
Item data;
struct node* next;
}node;
typedef struct stack_type {
node* top;
}stack_type;
static void terminate(char* message) {
printf("%s\n", message);
exit(EXIT_FAILURE);
}
Stack create() {
Stack s = (Stack)malloc(sizeof(stack_type));
if (s == NULL) {
terminate("Error in create: stack could not be created.\n");
exit(EXIT_FAILURE);
}
s->top = NULL;
return s;
}
void destory(Stack s) {
make_empty(s);
free(s);
}
void make_empty(Stack s) {
while (!is_empty(s))
pop(s);
}
bool is_empty(Stack s) {
return s->top == NULL;
}
bool is_full(Stack s) {
return false;
}
void push(Stack s, Item i) {
node* new_node = (node*)malloc(sizeof(node));
if (new_node == NULL) {
terminate("Error in push: stack is full.\n");
exit(EXIT_FAILURE);
}
new_node->data = i;
new_node->next = s->top;
s->top = new_node;
}
Item pop(Stack s) {
if (is_empty(s))
terminate("Error in pop: stack is empty.\n");
int data = s->top->data;
node* del = s->top;
s->top = s->top->next;
free(del);
return data;
}

View File

@@ -0,0 +1,18 @@
#ifndef STACKADT4_H
#define STACKADT4_H
#include<stdbool.h>
typedef int Item;
typedef struct stack_type* Stack;
Stack create();
void destory(Stack s);
void make_empty(Stack s);
bool is_empty(const Stack s);
bool is_full(const Stack s);
void push(Stack s, Item i);
Item pop(Stack s);
#endif

View File

@@ -0,0 +1,79 @@
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include"final_stackADT.h"
#define PUBLIC
#define PRIVATE static
typedef struct node{
Item data;
struct node* next;
}node;
typedef struct stack_type {
node* top;
Item pop_val;
}stack_type;
PUBLIC Stack stack_create() {
Stack s = (Stack)malloc(sizeof(stack_type));
assert(s != NULL);
s->top = NULL;
return s;
}
PUBLIC void stack_destory(Stack s) {
stack_make_empty(s);
free(s);
}
PUBLIC void stack_make_empty(Stack s) {
while (!stack_is_empty(s))
stack_pop(s);
}
PUBLIC bool stack_is_empty(Stack s) {
return s->top == NULL;
}
PUBLIC bool stack_is_full(Stack s) {
return false;
}
PUBLIC bool stack_push(Stack s, Item i) {
node* new_node = (node*)malloc(sizeof(node));
if (new_node == NULL)
return false;
new_node->data = i;
new_node->next = s->top;
s->top = new_node;
return true;
}
PUBLIC Item* stack_pop(Stack s) {
if (stack_is_empty(s))
return NULL;
node* del = s->top;
s->pop_val = del->data;
s->top = s->top->next;
free(del);
return &s->pop_val;
}

View File

@@ -0,0 +1,18 @@
#ifndef FINAL_STACKADT_H
#define FINAL_STACKADT_H
#include<stdbool.h>
typedef int Item;
typedef struct stack_type* Stack;
Stack stack_create();
void stack_destory(Stack s);
void stack_make_empty(Stack s);
bool stack_is_empty(const Stack s);
bool stack_is_full(const Stack s);
bool stack_push(Stack s, Item i);
Item* stack_pop(Stack s);
#endif

View File

@@ -0,0 +1,26 @@
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"final_stackADT.h"
int main(void) {
Stack s1, s2;
s1 = stack_create();
s2 = stack_create();
stack_push(s1, 1);
stack_push(s1, 2);
printf("%d\n", *stack_pop(s1));
printf("%d\n", *stack_pop(s1));
stack_destory(s1);
stack_destory(s2);
return 0;
}

View File

@@ -0,0 +1,19 @@
不同的实现方式中 接口头文件和客户包含main函数的文件改变的很少
我们一共使用了三种方式实现栈抽象数据类型:
1. 使用定长数组
2. 用动态数组
3. 使用链表
“最终形态”是基于链表的一些“改进”:
- 基于“不完整类型”的封装
- 使用宏来定义 PUBLIC 和 PRIVATE
- 使用不容易冲突的命名
- 提供错误处理的可能push 和 pop 函数返回值)
最后,本想通过改变 push 和 pop 来实现“通用抽象数据类型”,在修改程序时发现类型名还是不是很会处理,作罢。
可以参考下面的函数声明来完成它:
```c
bool stack_push(Stack s, void* i);
void* stack_pop(Stack s);
```

View File

@@ -0,0 +1,23 @@
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"stackADT.h"
int main(void) {
Stack s1, s2;
s1 = create();
s2 = create();
push(s1, 1);
push(s1, 2);
printf("%d\n", pop(s1));
printf("%d\n", pop(s1));
destory(s1);
destory(s2);
return 0;
}

View File

@@ -0,0 +1,4 @@
If two people write exactly the same program, each should be put in
micro-code and then they certainly won't be the same.
-- epigrams-on-programming
Time:4/21/2020

View File

@@ -0,0 +1,4 @@
o@ RQI VCIVJC QTORC C^GERJ_ RNC UGKC VTIATGK, CGEN UNISJB DC VSR OH
KOETI-EIBC GHB RNCH RNC_ ECTRGOHJ_ QIH'R DC RNC UGKC.
-- CVOATGKU-IH-VTIATGKKOHA
rOKC:4/21/2020

View File

@@ -0,0 +1,51 @@
#### 程序XOR 加密
对数据加密的一种最简单的方法就是将每个字符与一个密钥进行异或XOR运算。假设密钥时一个 & 字符。如果将它与字符 z 异或,我们会得到 \ 字符(假定字符集位 ACSII 字符集)。具体计算如下:
```c
00100110 (& ASCII )
XOR 01111010 (z ASCII )
01011100 (\ ASCII )
```
要将消息解密,只需要采用相同的算法。例如,如果将 & 与 \ 异或就可以得到 &
```c
00100110 (& ASCII )
XOR 01011100 (\ ASCII )
01111010 (z ASCII )
```
下面的程序 xor.c 通过每个字符于 & 字符进行异或来加密消息。原始消息可以由用户输入也可以输入重定向从文件读入。加密后的消息可以在屏幕上显示也可以通过输出重定向存入到文件中。例如 msg 文件包含以下内容:
```
If two people write exactly the same program, each should be put in
micro-code and then they certainly won't be the same.
-- epigrams-on-programming
Time:4/21/2020
```
为了对文件 msg 加密并将加密后的消息存入文件 newmsg 中,输入以下命令:
```c
xor <msg >newmsg
```
文件 newmsg 将包含下面的内容:
```
o@ RQI VCIVJC QTORC C^GERJ_ RNC UGKC VTIATGK, CGEN UNISJB DC VSR OH
KOETI-EIBC GHB RNCH RNC_ ECTRGOHJ_ QIH'R DC RNC UGKC.
-- CVOATGKU-IH-VTIATGKKOHA
rOKC:4/21/2020
```
要恢复原始消息,需要命令:
```c
xor <newmsg
```
将原始消息显示在屏幕上。
正如例子中看到的那样,程序不会改变一些字符,包括数字。将这些字符于 & 异或会产生不可见的控制字符,这在一些操作系统中会引发问题。在这里,为了安全起见,我们使用 `isprint`函数来确保原始字符和新字符都是可打印字符(即不是控制字符)。如果不满足,让程序写原始字符,而不是新字符。

View File

@@ -0,0 +1,21 @@
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<ctype.h>
#define KEY '&'
int main(void) {
int orig_ch, new_ch;
while ((orig_ch = getchar()) != EOF) {
new_ch = orig_ch ^ KEY;
if (isprint(orig_ch) && isprint(new_ch))
putchar(new_ch);
else
putchar(orig_ch);
}
return 0;
}

View File

@@ -0,0 +1,49 @@
#### 程序:查看内存单元
这个程序允许用户查看计算机内存段,这主要得益于 C 允许把整数用作指针。大多数 CPU 执行程序时都是处于“保护模式”,这就意味着程序只能访问那些分配给它的内存。这种方式还可以阻止对其他应用程序和操作系统本身所占用的内存的访问。因此我们只能看到程序本身分配到的内存,如果要对其他内存地址进行访问将导致程序崩溃。
程序 veiw_memory.c 先显示了该程序主函数和主函数中第一个变量的地址这样可以给用户一个线索去了解那个内存可以被探测。程序接下来提示用户输入地址16 进制格式)和需要查看的字节数,然后从指定地址开始显示指定字节内存块的内容。
字节按 10 个一组的方式显示(最后一组可能达不到 10 个。每组字节的首地址显示在一行的开头然后是该组的字节16 进制格式),再后面为该组字节的字符显示。只有打印字符(使用 `isprint`函数判断)会被显示,其余的被显示为 `.`
假设 int 类型大小为 32 位,地址也是 32 位长。
格式如下:
```
Address of main function: 5712bc
Address of addr variable: bcf784
Enter a (hex)address: 5712bc
Enter number of bytes to view: 40
Address Bytes Characters
----------------------------------------------------
5712BC E9 6F 06 00 00 E9 EA 04 00 00 .o........
5712C6 E9 45 22 00 00 E9 50 3F 00 00 .E"...P?..
5712D0 E9 FB 0C 00 00 E9 A6 27 00 00 .......'..
5712DA E9 14 3E 00 00 E9 AC 1E 00 00 ..>.......
```
.
```
Address of main function: 5712bc
Address of addr variable: effbc8
Enter a (hex)address: effbc8
Enter number of bytes to view: 64
Address Bytes Characters
----------------------------------------------------
EFFBC8 C8 FB EF 00 CC CC CC CC 99 76 .........v
EFFBD2 90 86 F4 FB EF 00 63 24 57 00 ......c$W.
EFFBDC 01 00 00 00 F8 4F 2E 01 B0 70 .....O...p
EFFBE6 2E 01 01 00 00 00 F8 4F 2E 01 .......O..
EFFBF0 B0 70 2E 01 50 FC EF 00 B7 22 .p..P...."
EFFBFA 57 00 1D 71 90 86 48 13 57 00 W..q..H.W.
EFFC04 48 13 57 00 H.W.
```
(前 4 个字节是我们输入的表示地址的整数,注意它的每个字节存储顺序)

View File

@@ -0,0 +1,49 @@
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<ctype.h>
typedef unsigned char BYTE;
int main(void) {
unsigned int addr;
int i, n;
BYTE* ptr;
printf("Address of main function: %x\n", (unsigned int)main);
printf("Address of addr variable: %x\n", (unsigned int)&addr);
printf("\nEnter a (hex)address: ");
scanf("%x", &addr);
printf("Enter number of bytes to view: ");
scanf("%d", &n);
printf("\n");
printf(" Address Bytes Characters\n");
printf("----------------------------------------------------\n");
ptr = (BYTE*)addr;
for (; n > 0; n -= 10) {
printf("%8X ", (unsigned int)ptr);
// 考虑到最后一组不满 10 个字节
for (i = 0; i < 10 && i < n; i++) {
printf("%.2X ", *(ptr + i)); // 转换说明:%.2X 相当于 %02hhX
}
// 最后一组不够 10 用空格凑满
for (; i < 10; i++) {
printf(" ");
}
printf(" ");
for (i = 0; i < 10 && i < n; i++) {
BYTE ch = *(ptr + i);
if (!(isprint(ch)))
ch = '.';
printf("%c", ch);
}
printf("\n");
ptr += 10;
}
return 0;
}