mirror of
https://github.com/hairrrrr/C-CrashCourse.git
synced 2026-05-08 23:12:30 +08:00
4-22
This commit is contained in:
70
Coding/Examples/06 循环/01 账簿计算/main.c
Normal file
70
Coding/Examples/06 循环/01 账簿计算/main.c
Normal 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");
|
||||
|
||||
// 题目中已经规定了这些功能用 0,1,2,3,4 代替,其实是想让我们用 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
Coding/Examples/06 循环/01 账簿计算/readme.md
Normal file
22
Coding/Examples/06 循环/01 账簿计算/readme.md
Normal file
@@ -0,0 +1,22 @@
|
||||
#### 程序:账薄结算
|
||||
|
||||
这个程序帮你理解一种简单的交互式程序设计,我们可以通过这种方式设计菜单。
|
||||
|
||||
题目:
|
||||
|
||||
开发一个程序用来维护账簿的余额。程序将为用户提供选择菜单:清空余额账户,向账户存钱,从账户取钱,显示当前余额,退出程序。选项用 0,1,2,3,4表示。程序的会话类似这样:
|
||||
|
||||
```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~
|
||||
```
|
||||
|
||||
35
Coding/Examples/08 数组/01 计算利息/main.c
Normal file
35
Coding/Examples/08 数组/01 计算利息/main.c
Normal 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;
|
||||
}
|
||||
18
Coding/Examples/08 数组/01 计算利息/readme.md
Normal file
18
Coding/Examples/08 数组/01 计算利息/readme.md
Normal 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 语句,一个控制年份,一个控制不同的利率。
|
||||
37
Coding/Examples/08 数组/02 发牌/main.c
Normal file
37
Coding/Examples/08 数组/02 发牌/main.c
Normal 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;
|
||||
}
|
||||
32
Coding/Examples/08 数组/02 发牌/readme.md
Normal file
32
Coding/Examples/08 数组/02 发牌/readme.md
Normal 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 然后将此牌“发”给用户;否则,重新生成随机数。
|
||||
33
Coding/Examples/09 函数/01 判定素数/main.c
Normal file
33
Coding/Examples/09 函数/01 判定素数/main.c
Normal 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;
|
||||
}
|
||||
10
Coding/Examples/09 函数/01 判定素数/readme.md
Normal file
10
Coding/Examples/09 函数/01 判定素数/readme.md
Normal file
@@ -0,0 +1,10 @@
|
||||
#### 程序:判断素数
|
||||
|
||||
编写程序提示用户录入数,然后给出一条信息说明此数是否为素数。
|
||||
|
||||
```c
|
||||
Enter a number: 24
|
||||
Not prime
|
||||
```
|
||||
|
||||
把判断素数的实现写到另外一个函数中,此函数返回值为 true 就表示是素数,返回 false 表示不是素数。
|
||||
74
Coding/Examples/10 程序结构/01 猜数/global_variable.c
Normal file
74
Coding/Examples/10 程序结构/01 猜数/global_variable.c
Normal 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_guesses:Repeatedly 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;
|
||||
}
|
||||
}
|
||||
59
Coding/Examples/10 程序结构/01 猜数/no_global_variable.c
Normal file
59
Coding/Examples/10 程序结构/01 猜数/no_global_variable.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
Coding/Examples/10 程序结构/01 猜数/readme.md
Normal file
19
Coding/Examples/10 程序结构/01 猜数/readme.md
Normal 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
|
||||
```
|
||||
|
||||
使用两种方法:使用全局变量/不使用全局变量
|
||||
209
Coding/Examples/10 程序结构/02 手牌分类/main.c
Normal file
209
Coding/Examples/10 程序结构/02 手牌分类/main.c
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 程序难点思路:
|
||||
* 1)为了判定手中的牌是否重复,我们需要一个布尔类型数组存储整副牌,初始化整个数组为 false。如果一张牌已经在我们手上,那么我们将数组对应的元素置为 true
|
||||
* 2)用两个分别数组来存储每个点数和花色的个数,这样方便我们后面判断牌的类型
|
||||
* 3)8 种牌的类型,我们可拆成 同花,顺子,4张,3张,对子(值为 0,1,2)这五种基础类型的组合。
|
||||
*
|
||||
* 程序结构:
|
||||
* 通过上面的分析,我们发现:这个程序需要 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");
|
||||
}
|
||||
56
Coding/Examples/10 程序结构/02 手牌分类/readme.md
Normal file
56
Coding/Examples/10 程序结构/02 手牌分类/readme.md
Normal file
@@ -0,0 +1,56 @@
|
||||
#### 程序:手牌分类
|
||||
|
||||
编写程序对手牌进行读取和分类。手中的每张牌都有花色(方块,梅花,红桃和黑桃)和等级(2,3,4,5,6,7,8,9,T,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 而不是一张牌,就会导致程序终止。
|
||||
|
||||
与程序的会话如下:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
|
||||
|
||||
45
Coding/Examples/11 指针/01 数组的最大元素和最小元素/main.c
Normal file
45
Coding/Examples/11 指针/01 数组的最大元素和最小元素/main.c
Normal 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];
|
||||
//}
|
||||
}
|
||||
10
Coding/Examples/11 指针/01 数组的最大元素和最小元素/readme.md
Normal file
10
Coding/Examples/11 指针/01 数组的最大元素和最小元素/readme.md
Normal file
@@ -0,0 +1,10 @@
|
||||
#### 程序:找出数组中的最大元素和最小元素
|
||||
|
||||
与程序的交互如下:
|
||||
|
||||
```c
|
||||
Enter 5 numbers:9 5 2 7 8
|
||||
Largest: 9
|
||||
Smallest: 2
|
||||
```
|
||||
|
||||
16
Coding/Examples/13 字符串/01 设计字符串读取函数/read_line.c
Normal file
16
Coding/Examples/13 字符串/01 设计字符串读取函数/read_line.c
Normal 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 类型的值返回。
|
||||
25
Coding/Examples/13 字符串/01 设计字符串读取函数/readme.md
Normal file
25
Coding/Examples/13 字符串/01 设计字符串读取函数/readme.md
Normal file
@@ -0,0 +1,25 @@
|
||||
#### 3. 逐个字符读取字符串
|
||||
|
||||
因为对许多程序而言,scanf 函数和 gets 函数都有风险而且不够灵活,C 程序员经常会自己编写输入函数。通过每次读一个字符的方式读取字符串。
|
||||
|
||||
如果决定自己设计输入函数,那么需要考虑以下问题:
|
||||
|
||||
- 在开始存储字符串之前,函数应该跳过空白字符吗?
|
||||
- 什么字符导致函数停止读取:换行符,任意空白字符,还是其他某种字符?需要存储这些字符还是忽略掉?
|
||||
- 如果输入的字符串太长以至于无法存储,那么函数应该忽略额外的字符还是把它们留给下一次输入操作?
|
||||
|
||||
示例中,我们选择:不跳过空白字符,换行符结束,不存储换行符,忽略掉额外字符。
|
||||
|
||||
函数原型如下:
|
||||
|
||||
```c
|
||||
int read_line(char str[], int read_num);
|
||||
```
|
||||
|
||||
参数:str 表示存储输入的数组,read_num 表示读入字符的最大数量。
|
||||
|
||||
返回值:返回读入字符的个数。
|
||||
|
||||
使用 getchar 实现按字符读入。
|
||||
|
||||
|
||||
20
Coding/Examples/13 字符串/02 显示一个月的提醒列表/readme.md
Normal file
20
Coding/Examples/13 字符串/02 显示一个月的提醒列表/readme.md
Normal 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 函数
|
||||
- 将提醒存放在二维数组中,数组的每一行看作一个字符串。日期和提示消息都要放进去 。
|
||||
- 日期我们用整型输入,然后转换为字符串放入二维数组的前面。
|
||||
- 每次读入新的日期和提示消息后,将转为字符串的当前日期和二维数组每行前面表示日期的部分比较。如果当前日期字符串小于二维数组当前行的字符串,说明当前日期较小,应当插入到当前数组的行前一行。我们可以将二维数组从当前行到存放提示的最后一行每行依次向后移动一行,从而使得当前日期和提示可以插入二维数组的当前行。
|
||||
- 打印二维数组
|
||||
76
Coding/Examples/13 字符串/02 显示一个月的提醒列表/remind.c
Normal file
76
Coding/Examples/13 字符串/02 显示一个月的提醒列表/remind.c
Normal 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;
|
||||
}
|
||||
27
Coding/Examples/13 字符串/03 核对行星名字/planets.c
Normal file
27
Coding/Examples/13 字符串/03 核对行星名字/planets.c
Normal 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 中的字符串进行比较,直到找到匹配的名字或到了数组末尾才停止。
|
||||
21
Coding/Examples/13 字符串/03 核对行星名字/readme.md
Normal file
21
Coding/Examples/13 字符串/03 核对行星名字/readme.md
Normal 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)
|
||||
37
Coding/Examples/15 编写大型程序/01 文本格式化/justify.c
Normal file
37
Coding/Examples/15 编写大型程序/01 文本格式化/justify.c
Normal 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;
|
||||
}
|
||||
63
Coding/Examples/15 编写大型程序/01 文本格式化/line.c
Normal file
63
Coding/Examples/15 编写大型程序/01 文本格式化/line.c
Normal 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);
|
||||
}
|
||||
48
Coding/Examples/15 编写大型程序/01 文本格式化/line.h
Normal file
48
Coding/Examples/15 编写大型程序/01 文本格式化/line.h
Normal 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
|
||||
10
Coding/Examples/15 编写大型程序/01 文本格式化/quote.txt
Normal file
10
Coding/Examples/15 编写大型程序/01 文本格式化/quote.txt
Normal 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
|
||||
43
Coding/Examples/15 编写大型程序/01 文本格式化/readme.md
Normal file
43
Coding/Examples/15 编写大型程序/01 文本格式化/readme.md
Normal 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 函数
|
||||
|
||||

|
||||
33
Coding/Examples/15 编写大型程序/01 文本格式化/word.c
Normal file
33
Coding/Examples/15 编写大型程序/01 文本格式化/word.c
Normal 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';
|
||||
}
|
||||
14
Coding/Examples/15 编写大型程序/01 文本格式化/word.h
Normal file
14
Coding/Examples/15 编写大型程序/01 文本格式化/word.h
Normal 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
|
||||
BIN
Coding/Examples/15 编写大型程序/01 文本格式化/重定向.png
Normal file
BIN
Coding/Examples/15 编写大型程序/01 文本格式化/重定向.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 65 KiB |
194
Coding/Examples/16 结构&联合&枚举/01 维护零件数据库/inventory.c
Normal file
194
Coding/Examples/16 结构&联合&枚举/01 维护零件数据库/inventory.c
Normal 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);
|
||||
}
|
||||
}
|
||||
24
Coding/Examples/16 结构&联合&枚举/01 维护零件数据库/readline.c
Normal file
24
Coding/Examples/16 结构&联合&枚举/01 维护零件数据库/readline.c
Normal 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;
|
||||
}
|
||||
|
||||
16
Coding/Examples/16 结构&联合&枚举/01 维护零件数据库/readline.h
Normal file
16
Coding/Examples/16 结构&联合&枚举/01 维护零件数据库/readline.h
Normal 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
|
||||
51
Coding/Examples/16 结构&联合&枚举/01 维护零件数据库/readme.md
Normal file
51
Coding/Examples/16 结构&联合&枚举/01 维护零件数据库/readme.md
Normal 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 函数和这个程序的主干没有太大关系,我们用单独的头文件和源文件包含它。
|
||||
5
Coding/Examples/17 指针的高级应用/01 显示一个月的提醒列表/readme.md
Normal file
5
Coding/Examples/17 指针的高级应用/01 显示一个月的提醒列表/readme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
#### 程序:显示一个月的提醒列表
|
||||
|
||||
前面我们把字符串存储在二维数组中,但是这可能会浪费空间。后面的教学中我们设想使用指针数组存储字符串,让一维数组的每个元素都指向一个字符串字面量。如果数组元素是指向动态分配的字符串的指针,那么是可以实现我们的设想的。
|
||||
|
||||
下面的程序对之前的程序作了小部分修改,修改的地方后面用注释注明了。
|
||||
86
Coding/Examples/17 指针的高级应用/01 显示一个月的提醒列表/reminder.c
Normal file
86
Coding/Examples/17 指针的高级应用/01 显示一个月的提醒列表/reminder.c
Normal 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;
|
||||
}
|
||||
234
Coding/Examples/17 指针的高级应用/02 维护零件数据库(链表)/inventory2.c
Normal file
234
Coding/Examples/17 指针的高级应用/02 维护零件数据库(链表)/inventory2.c
Normal 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);
|
||||
}
|
||||
}
|
||||
6
Coding/Examples/17 指针的高级应用/02 维护零件数据库(链表)/readme.md
Normal file
6
Coding/Examples/17 指针的高级应用/02 维护零件数据库(链表)/readme.md
Normal file
@@ -0,0 +1,6 @@
|
||||
#### 程序:维护零件数据库
|
||||
|
||||
下面重做前面的程序,这次把数据库存储在链表中。链表代替数组主要有两个好处:
|
||||
|
||||
1. 不需要事先限制数据库的大小
|
||||
2. 可以很容易地按零件编号对数据库排序(本程序采用默认升序排序)
|
||||
40
Coding/Examples/19 程序设计/01 栈的实现/01 栈模块/array_stack.c
Normal file
40
Coding/Examples/19 程序设计/01 栈的实现/01 栈模块/array_stack.c
Normal 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];
|
||||
}
|
||||
|
||||
57
Coding/Examples/19 程序设计/01 栈的实现/01 栈模块/linkedlist_stack.c
Normal file
57
Coding/Examples/19 程序设计/01 栈的实现/01 栈模块/linkedlist_stack.c
Normal 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;
|
||||
}
|
||||
12
Coding/Examples/19 程序设计/01 栈的实现/01 栈模块/stack.h
Normal file
12
Coding/Examples/19 程序设计/01 栈的实现/01 栈模块/stack.h
Normal 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
|
||||
@@ -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];
|
||||
//}
|
||||
@@ -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
|
||||
@@ -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];
|
||||
//}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
19
Coding/Examples/19 程序设计/01 栈的实现/02 栈抽象数据类型/readme.md
Normal file
19
Coding/Examples/19 程序设计/01 栈的实现/02 栈抽象数据类型/readme.md
Normal 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);
|
||||
```
|
||||
23
Coding/Examples/19 程序设计/01 栈的实现/02 栈抽象数据类型/stackclient.c
Normal file
23
Coding/Examples/19 程序设计/01 栈的实现/02 栈抽象数据类型/stackclient.c
Normal 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;
|
||||
}
|
||||
4
Coding/Examples/20 底层程序设计/01 XOR加密/msg.txt
Normal file
4
Coding/Examples/20 底层程序设计/01 XOR加密/msg.txt
Normal 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
|
||||
4
Coding/Examples/20 底层程序设计/01 XOR加密/newmsg.txt
Normal file
4
Coding/Examples/20 底层程序设计/01 XOR加密/newmsg.txt
Normal 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
|
||||
51
Coding/Examples/20 底层程序设计/01 XOR加密/readme.md
Normal file
51
Coding/Examples/20 底层程序设计/01 XOR加密/readme.md
Normal 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`函数来确保原始字符和新字符都是可打印字符(即不是控制字符)。如果不满足,让程序写原始字符,而不是新字符。
|
||||
21
Coding/Examples/20 底层程序设计/01 XOR加密/xor.c
Normal file
21
Coding/Examples/20 底层程序设计/01 XOR加密/xor.c
Normal 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;
|
||||
}
|
||||
49
Coding/Examples/20 底层程序设计/02 查看内存单元/readme.md
Normal file
49
Coding/Examples/20 底层程序设计/02 查看内存单元/readme.md
Normal 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 个字节是我们输入的表示地址的整数,注意它的每个字节存储顺序)
|
||||
49
Coding/Examples/20 底层程序设计/02 查看内存单元/view_memory.c
Normal file
49
Coding/Examples/20 底层程序设计/02 查看内存单元/view_memory.c
Normal 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;
|
||||
}
|
||||
|
||||
1
Coding/Exercises/CMOOC/readme.md
Normal file
1
Coding/Exercises/CMOOC/readme.md
Normal file
@@ -0,0 +1 @@
|
||||
新建文件夹
|
||||
1
Coding/Exercises/C_Crash_Couse/readme.md
Normal file
1
Coding/Exercises/C_Crash_Couse/readme.md
Normal file
@@ -0,0 +1 @@
|
||||
新建文件夹
|
||||
1
Coding/Exercises/advanced C/readme.md
Normal file
1
Coding/Exercises/advanced C/readme.md
Normal file
@@ -0,0 +1 @@
|
||||
新建文件夹
|
||||
29
Coding/Exercises/advanced C/test/01/01 打印杨辉三角/test1.c
Normal file
29
Coding/Exercises/advanced C/test/01/01 打印杨辉三角/test1.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#include<stdio.h>
|
||||
|
||||
#define HEIGHT 10 //假设杨辉三角的高度是10
|
||||
|
||||
int main(void) {
|
||||
|
||||
int YHtrangle[HEIGHT][HEIGHT] = { 0 };
|
||||
int i, j;
|
||||
|
||||
//计算杨辉三角
|
||||
for (i = 0; i < HEIGHT; i++) {
|
||||
|
||||
YHtrangle[i][0] = 1;//每行第一个元素为 1
|
||||
YHtrangle[i][i] = 1;//每行最后一个元素为 1
|
||||
//需要计算的是从第三行开始,i == 2
|
||||
for (j = 1; j < i; j++)
|
||||
YHtrangle[i][j] = YHtrangle[i - 1][j - 1] + YHtrangle[i - 1][j];// 比如第三行的 2 是 第二行 1 + 1的和,这是杨辉三角的规律
|
||||
}
|
||||
|
||||
//输出杨辉三角
|
||||
for (i = 0; i < HEIGHT; i++) {
|
||||
for (j = 0; j <= i; j++) {
|
||||
printf("%d ", YHtrangle[i][j]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
37
Coding/Exercises/advanced C/test/01/01 打印杨辉三角/test2.c
Normal file
37
Coding/Exercises/advanced C/test/01/01 打印杨辉三角/test2.c
Normal file
@@ -0,0 +1,37 @@
|
||||
#include<stdio.h>
|
||||
|
||||
#define HEIGHT 10 //如果要修改杨辉三角的高度修改这个参数即可
|
||||
|
||||
#define ARRLEN HEIGHT + 1
|
||||
|
||||
void printYH(int arr[][ARRLEN]) {
|
||||
|
||||
int row, col;
|
||||
|
||||
//这个程序比较方便惯性思维理解,数组的首行首列不用(下标从 1 开始)
|
||||
for (row = 1; row <= HEIGHT; row++) {
|
||||
|
||||
for (col = 1; col <= row; col++) {
|
||||
|
||||
if (col == 1 || col == row)
|
||||
arr[row][col] = 1;
|
||||
else
|
||||
arr[row][col] = arr[row - 1][col - 1] + arr[row - 1][col];
|
||||
|
||||
//直接输出计算结果不需要再用另一个循环输出
|
||||
printf("%d ", arr[row][col]);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
//当然这个程序也有缺点,如果你在程序中使用这个数组,你需要时刻记得下标为 0 是不存任何有意义的数据的
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
||||
int YHtrangle[ARRLEN][ARRLEN] = { 0 };
|
||||
|
||||
printYH(YHtrangle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
12
Coding/Exercises/advanced C/test/01/01 打印杨辉三角/题目.md
Normal file
12
Coding/Exercises/advanced C/test/01/01 打印杨辉三角/题目.md
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
#### 1.打印杨辉三角
|
||||
|
||||
```c
|
||||
1
|
||||
1 1
|
||||
1 2 1
|
||||
1 3 3 1
|
||||
1 4 6 4 1
|
||||
...
|
||||
```
|
||||
|
||||
63
Coding/Exercises/advanced C/test/01/02 字符串旋转/test1.c
Normal file
63
Coding/Exercises/advanced C/test/01/02 字符串旋转/test1.c
Normal file
@@ -0,0 +1,63 @@
|
||||
#include<stdio.h>
|
||||
#include<string.h>
|
||||
#include<assert.h>
|
||||
|
||||
int strJudge(const char* str1, const char* str2) {
|
||||
|
||||
char* str2_start = (char*)str2;
|
||||
|
||||
//str1 和 str2 不能是空指针
|
||||
assert(str1 != NULL && str2 != NULL);
|
||||
|
||||
//如果字符串长度是 1 ,这个判断就没有意义了
|
||||
assert(strlen(str1) != 1);
|
||||
|
||||
//如果连个字符串长度都不一样,肯定不是
|
||||
if (!(strlen(str1) == strlen(str2)))
|
||||
return 0;
|
||||
|
||||
//在 str2 中寻找 str1 的第一个元素
|
||||
while (*str2) {
|
||||
if (*str2 == *str1) {
|
||||
++str1;// str1 中的第一个元素已经找到,在下一个循环寻找 str1 中的下一个
|
||||
break;
|
||||
}
|
||||
|
||||
++str2;
|
||||
}
|
||||
//没有找到,直接返回 0
|
||||
if (*str2 == '\0')
|
||||
return 0;
|
||||
|
||||
while (*str1) {
|
||||
|
||||
++str2;
|
||||
|
||||
if (*str2 == '\0')
|
||||
str2 = str2_start;
|
||||
|
||||
if (!(*str2 == *str1))
|
||||
return 0;
|
||||
|
||||
++str1;
|
||||
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
||||
char str1[100];
|
||||
char str2[100];
|
||||
|
||||
printf("Enter two strings: ");
|
||||
scanf("%s %s", str1, str2);
|
||||
|
||||
if (strJudge(str1, str2))
|
||||
printf("Yes\n");
|
||||
else
|
||||
printf("No\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
12
Coding/Exercises/advanced C/test/01/02 字符串旋转/题目.md
Normal file
12
Coding/Exercises/advanced C/test/01/02 字符串旋转/题目.md
Normal file
@@ -0,0 +1,12 @@
|
||||
#### 2. 字符串旋转
|
||||
|
||||
|
||||
|
||||
写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。
|
||||
|
||||
例如:给定s1 =AABCD和s2 = BCDAA,返回1
|
||||
给定s1=abcd和s2=ACBD,返回0.
|
||||
|
||||
AABCD左旋一个字符得到ABCDA
|
||||
AABCD左旋两个字符得到BCDAA
|
||||
AABCD右旋一个字符得到DAABC
|
||||
56
Coding/Exercises/advanced C/test/01/03 字符串左旋/test1.c
Normal file
56
Coding/Exercises/advanced C/test/01/03 字符串左旋/test1.c
Normal file
@@ -0,0 +1,56 @@
|
||||
//实现一个函数,可以左旋字符串中的k个字符。
|
||||
//
|
||||
//例如:
|
||||
//ABCD左旋一个字符得到BCDA
|
||||
//ABCD左旋两个字符得到CDAB
|
||||
|
||||
#include<stdio.h>
|
||||
#include<string.h>
|
||||
#include<assert.h>
|
||||
|
||||
char* strRightReverse(char* str, int num) {
|
||||
|
||||
char strAfterReverse[100] = {0};
|
||||
char* strReverse = str;
|
||||
char* strNotReverse = str;
|
||||
int i;
|
||||
int length = strlen(str);
|
||||
|
||||
if (num < 0 || num > strlen(str)) {
|
||||
printf("Illegal input!\n");
|
||||
return str;
|
||||
}
|
||||
|
||||
//我们先将不左旋的数放进 strReverse 数组中
|
||||
strNotReverse += num;
|
||||
|
||||
for (i = 0; i < length - num; i++)
|
||||
strAfterReverse[i] = *strNotReverse++;
|
||||
|
||||
//再将要左旋的数依次放入数组尾
|
||||
for (i = length - num; i < length; i++) {
|
||||
strAfterReverse[i] = *strReverse++;
|
||||
}
|
||||
strAfterReverse[length] = '\0';
|
||||
|
||||
strcpy(str, strAfterReverse);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
||||
char str[100];
|
||||
int num = 0;
|
||||
|
||||
printf("Enter a string: ");
|
||||
scanf("%s", str);
|
||||
printf("Enter a number: ");
|
||||
scanf("%d", &num);
|
||||
|
||||
strRightReverse(str, num);
|
||||
|
||||
printf("output: %s\n", str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
85
Coding/Exercises/advanced C/test/01/04 杨氏矩阵/test1.c
Normal file
85
Coding/Exercises/advanced C/test/01/04 杨氏矩阵/test1.c
Normal file
@@ -0,0 +1,85 @@
|
||||
//#### 4. 杨氏矩阵
|
||||
//
|
||||
//有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在。
|
||||
//
|
||||
//
|
||||
//
|
||||
//要求:时间复杂度小于O(N);
|
||||
//
|
||||
//*可以先不去管复杂度问题,这里给出一种方便理解的算法。[参考文章](https://blog.csdn.net/sgbfblog/article/details/7745450?depth_1-utm_source=distribute.pc_relevant_right.none-task&utm_source=distribute.pc_relevant_right.none-task)*
|
||||
|
||||
|
||||
#include<stdio.h>
|
||||
#include<assert.h>
|
||||
|
||||
#define ROW 3
|
||||
#define COL 3
|
||||
|
||||
void printArr(int arr[ROW][COL]);
|
||||
int searchYangMatri(int arr[ROW][COL], int row, int col, int target, int position[2]);
|
||||
|
||||
int main(void) {
|
||||
|
||||
int target;
|
||||
int position[2] = {-1, -1};//目标数在矩阵中的位置
|
||||
|
||||
//注意:杨氏矩阵并不是有序数列
|
||||
int arr[ROW][COL] = {
|
||||
{1, 3, 5},
|
||||
{2, 4, 6},
|
||||
{7, 8, 9},
|
||||
};
|
||||
|
||||
printArr(arr);
|
||||
|
||||
printf("Enter a target: ");
|
||||
scanf("%d", &target);
|
||||
|
||||
if (searchYangMatrix(arr, ROW, COL, target, position))
|
||||
printf("%d is found!The position in the matrix is as follow:(%d, %d)\n", target, position[0], position[1]);
|
||||
else
|
||||
printf("Not Found!\n");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void printArr(int arr[ROW][COL]) {
|
||||
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < ROW; i++) {
|
||||
for (j = 0; j < COL; j++)
|
||||
printf("%d ", arr[i][j]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int searchYangMatrix(int arr[ROW][COL], int row, int col, int target, int position[2]) {
|
||||
|
||||
assert(arr != NULL);
|
||||
|
||||
//从矩阵的右上角开始搜寻
|
||||
int i = 0;
|
||||
int j = col - 1;
|
||||
|
||||
while (i < ROW && j >= 0) {
|
||||
|
||||
//如果目标数比右上角的数大,则去掉这一行(因为右上角的数是这一行中最大的)
|
||||
if (target > arr[i][j])
|
||||
i++;
|
||||
//如果目标数比右上角的小,则去掉这一列(因为右上角的数是这一列中最小的)
|
||||
else if (target < arr[i][j])
|
||||
j--;
|
||||
else {
|
||||
position[0] = j;//如果将数组右下角的元素放在二位坐标系的原点上, x 和数组的 列是对应的:x = col(元素所在位置的 col)
|
||||
position[1] = row - 1 - i;// y 与 数组行的关系是 y = ROW - 1 - row(元素所在位置的 row)
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
52
Coding/Exercises/advanced C/test/01/05 qsort实现/test1.c
Normal file
52
Coding/Exercises/advanced C/test/01/05 qsort实现/test1.c
Normal file
@@ -0,0 +1,52 @@
|
||||
#include<stdio.h>
|
||||
|
||||
//定义一个 Cmp 类型的函数指针,函数指针返回值是 int,参数是(int, int)
|
||||
typedef int(*Cmp)(int, int);
|
||||
|
||||
void bubbleSort(int arr[], int size, Cmp cmp);
|
||||
int Asc(int x, int y);
|
||||
int Desc(int x, int y);
|
||||
|
||||
int main(void) {
|
||||
|
||||
int arr[] = { 9, 5, 2, 7 };
|
||||
int size = sizeof(arr) / sizeof(arr[0]);
|
||||
int i = 0;
|
||||
|
||||
bubbleSort(arr, size, Desc);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
printf("%d ", arr[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bubbleSort(int arr[], int size, Cmp cmp) {
|
||||
|
||||
int i, j;
|
||||
int tmp;
|
||||
|
||||
for (i = 0; i < size - 1; i++) {
|
||||
for (j = 0; j < size - 1 - i; j++) {
|
||||
//根据 cmp 函数的规则,如果返回 1,进行 if 语句中的位置互换
|
||||
if (cmp(arr[j], arr[j + 1]) == 1) {
|
||||
tmp = arr[j];
|
||||
arr[j] = arr[j + 1];
|
||||
arr[j + 1] = tmp;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//升序
|
||||
int Asc(int x, int y) {
|
||||
//如果第一个参数比第二个参数大返回 1
|
||||
return (x > y ? 1 : 0);
|
||||
}
|
||||
|
||||
//降序
|
||||
int Desc(int x, int y) {
|
||||
return (x < y ? 1 : 0);
|
||||
}
|
||||
37
Coding/Exercises/advanced C/test/01/06 猜凶手/test1.c
Normal file
37
Coding/Exercises/advanced C/test/01/06 猜凶手/test1.c
Normal file
@@ -0,0 +1,37 @@
|
||||
//#### 6. 猜凶手日本某地发生了一件谋杀案,警察通过排查确定杀人凶手必为4个嫌疑犯的一个。
|
||||
//
|
||||
//以下为4个嫌疑犯的供词:
|
||||
//
|
||||
//
|
||||
//
|
||||
//A说:不是我。
|
||||
//
|
||||
//B说:是C。
|
||||
//
|
||||
//C说:是D。
|
||||
//
|
||||
//D说:C在胡说
|
||||
//
|
||||
//已知3个人说了真话,1个人说的是假话。
|
||||
//
|
||||
//
|
||||
//
|
||||
//现在请根据这些信息,写一个程序来确定到底谁是凶手。
|
||||
|
||||
#include<stdio.h>
|
||||
|
||||
int main(void) {
|
||||
|
||||
int i, murder;
|
||||
|
||||
//这种类型的问题思路是穷举——即遍历所有可能
|
||||
|
||||
for (murder = 'A'; murder <= 'D'; murder++) {
|
||||
// 3 人说了真话, 1 人说了假话,说明这四个语句 3 真 1 假 ,相加为 3
|
||||
if (((murder != 'A') + (murder == 'C') + (murder == 'D') + (murder != 'D')) == 3 )
|
||||
|
||||
printf("murder is %c\n", murder);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
48
Coding/Exercises/advanced C/test/01/07 猜名次/test1.c
Normal file
48
Coding/Exercises/advanced C/test/01/07 猜名次/test1.c
Normal file
@@ -0,0 +1,48 @@
|
||||
//#### 7. 猜名次
|
||||
//
|
||||
//5位运动员参加了10米台跳水比赛,有人让他们预测比赛结果:
|
||||
//
|
||||
//A选手说:B第二,我第三;
|
||||
//
|
||||
//B选手说:我第二,E第四;
|
||||
//
|
||||
//C选手说:我第一,D第二;
|
||||
//
|
||||
//D选手说:C最后,我第三;
|
||||
//
|
||||
//E选手说:我第四,A第一;
|
||||
//
|
||||
//比赛结束后,每位选手都说对了一半,请编程确定比赛的名次。
|
||||
|
||||
#include<stdio.h>
|
||||
|
||||
int main(void) {
|
||||
|
||||
int a, b, c, d, e;
|
||||
|
||||
for (a = 1; a <= 5; a++) {
|
||||
for (b = 1; b <= 5; b++) {
|
||||
for (c = 1; c <= 5; c++) {
|
||||
for (d = 1; d <= 5; d++) {
|
||||
for (e = 1; e <= 5; e++) {
|
||||
// 每位选手都说对了一半
|
||||
if (((b == 2) + (a == 3)) == 1
|
||||
&& ((b == 2) + (e == 4)) == 1
|
||||
&& ((c == 1) + (d == 2)) == 1
|
||||
&& ((c == 5) + (d == 3)) == 1
|
||||
&& ((e == 4) + (a == 1)) == 1) {
|
||||
|
||||
//名次不能重复
|
||||
if (a * b * c * d * e == 120)
|
||||
printf("A 的名次:%d\nB 的名次:%d\nC 的名次:%d\nD 的名次:%d\nE 的名次:%d\n", a, b, c, d, e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
77
Coding/Exercises/advanced C/test/02/01 实现atoi/MyAtoi.c
Normal file
77
Coding/Exercises/advanced C/test/02/01 实现atoi/MyAtoi.c
Normal file
@@ -0,0 +1,77 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
// atoi 实现
|
||||
// ElementType atoi( const char *str );
|
||||
//转译 str 所指的字节字符串中的整数值。
|
||||
|
||||
//舍弃任何空白符,直至找到首个非空白符,然后接收尽可能多的字符以组成合法的整数表示,并转换之为整数值。合法的整数值含下列部分:
|
||||
|
||||
//(可选) 正或负号
|
||||
//数位
|
||||
|
||||
#include<stdio.h>
|
||||
#include<stdlib.h>
|
||||
|
||||
#define MAXSIZE 100
|
||||
|
||||
typedef long long ElementType;
|
||||
|
||||
ElementType MyAtoi(const char* str);
|
||||
ElementType StrToNum(int numbers[], int size);
|
||||
|
||||
int main(void) {
|
||||
|
||||
char str[MAXSIZE];
|
||||
ElementType NumInStr;
|
||||
|
||||
scanf("%s", str);
|
||||
|
||||
NumInStr = MyAtoi(str);
|
||||
|
||||
printf("%lld\n", NumInStr);// 转换成大的类型需要改变 printf 的转换说明
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ElementType MyAtoi(const char* str) {
|
||||
|
||||
ElementType ret = 0;
|
||||
int isFirstNumber = 1;
|
||||
int flg = 1;
|
||||
int numbers[MAXSIZE];
|
||||
int size = 0;
|
||||
|
||||
while (*str != '\0') {
|
||||
|
||||
if (*str >= '0' && *str <= '9') {
|
||||
// 判断这个数是否为第一个出现的数字,如果是,我们要判断前一个字符是否是 - 号
|
||||
if (isFirstNumber) {
|
||||
if (*(str - 1) == '-')
|
||||
flg = -1;
|
||||
isFirstNumber = 0;
|
||||
}
|
||||
// 直接将字符转化为数字放入数组内,数组放置的是目标数字的每一位(不带权重)。
|
||||
numbers[size++] = *str - '0';// 数字对应的 ACSII 码值减去 ‘0’对应的 ASCII 值就是实际上数组表示的值
|
||||
}
|
||||
++str;
|
||||
}
|
||||
|
||||
ret = StrToNum(numbers, size);
|
||||
|
||||
return (flg * ret);
|
||||
}
|
||||
|
||||
ElementType StrToNum(int numbers[], int size) {
|
||||
|
||||
ElementType ret = 0;
|
||||
ElementType weight = 1;
|
||||
int i;
|
||||
|
||||
for (i = size - 1; i >= 0; i--) {
|
||||
ret += numbers[i] * weight;
|
||||
weight *= 10;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
82
Coding/Exercises/advanced C/test/02/01 实现atoi/MyAtoi2.c
Normal file
82
Coding/Exercises/advanced C/test/02/01 实现atoi/MyAtoi2.c
Normal file
@@ -0,0 +1,82 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
// 这是 atoi 实现的另一个版本
|
||||
// 上一个版本其实和库里面的 atoi 实现有的地方不是一致的
|
||||
// 比如库函数 atoi 处理字符串 "abc123", 返回的结果是 0 ,而上一个我实现的 MyAtoi 会返回 123
|
||||
// "+-123", 返回的结果是 0, 而MyAtoi 会返回 -123
|
||||
// 其实我觉得我都 atoi 函数更强大哈哈
|
||||
// 但是我们也应该规范一点,毕竟是库函数
|
||||
|
||||
#include<stdio.h>
|
||||
#include<stdlib.h>
|
||||
#include<ctype.h>
|
||||
|
||||
int MyAtoi_2(const char* str) {
|
||||
|
||||
int flg = 1;
|
||||
int ret = 0;
|
||||
|
||||
if (str == NULL || str == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 判断是否为空白字符(空格,换行,制表,翻页 ...)
|
||||
while (isspace(*str)) {
|
||||
str++;
|
||||
}
|
||||
|
||||
// 判断是否为正负号
|
||||
// 用库函数 atoi 试一下就知道 "+-123" 这种字符串是无法转成数字的,所以这是一种要处理的情况
|
||||
// 如果符号位的下一个不是数字,则返回 0
|
||||
if (*str == '+' || *str == '-' ) {
|
||||
|
||||
if (*(str + 1) == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!isdigit(*(str + 1))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*str == '-') {
|
||||
flg = -1;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
|
||||
// 处理数字字符
|
||||
while (*str != '\0') {
|
||||
if (isdigit(*str)) {
|
||||
|
||||
// 判断一下会不会超过 int 的范围
|
||||
if (((unsigned long)ret * 10 + (*str - '0')) > INT_MAX) {
|
||||
return INT_MAX;
|
||||
}
|
||||
ret = ret * 10 + (*str - '0');
|
||||
|
||||
// 在 MyAtoi 中,我选择用数组存储每一位,然后从后向前依次乘以每一位的权重
|
||||
// 但是这样直接加一个数之前先乘以 10 的做法更为简洁
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
|
||||
return ret * flg;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
||||
char str[100];
|
||||
int ret;
|
||||
|
||||
scanf("%s", str);
|
||||
|
||||
ret = MyAtoi_2(str);
|
||||
|
||||
printf("%d\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
// strncat 实现
|
||||
|
||||
#include<stdio.h>
|
||||
#include<assert.h>
|
||||
|
||||
char* MyStrncat(char* dest, const char* src, int count);
|
||||
|
||||
int main(void) {
|
||||
|
||||
char dest[100];
|
||||
char src[50];
|
||||
int count = 0;
|
||||
|
||||
printf("Enter string dest: ");
|
||||
scanf("%s", dest);
|
||||
printf("Enter string src: ");
|
||||
scanf("%s", src);
|
||||
printf("Enter number of character(s) copy from src to dest: ");
|
||||
scanf("%d", &count);
|
||||
|
||||
MyStrncat(dest, src, count);
|
||||
|
||||
printf("%s\n", dest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 其实我觉得这取实现程序不如不写。这种写法你上网一查都是一大片一大片的,
|
||||
// 乍一看感觉是对的,但是自己想一想,这是很危险的写法。当让你也可以归结于
|
||||
// C语言本身的语法不好,但是其实是我们的水品还没到罢了,我们想不到更好的写法,
|
||||
// 我们根本没学精 C!但是这糟糕的函数是 C 的创始人写出来的啊!似乎可以为我们找一些接口。
|
||||
|
||||
// 现在我们来说一下这样的写法有什么漏洞,首先,我们看一看 cppreference 上给的定义:
|
||||
|
||||
//后附来自 src 所指向的字符数组的至多 count 个字符,到 dest 所指向的空终止字节字符串的末尾,
|
||||
//若找到空字符则停止。字符 src[0] 替换位于 dest 末尾的空终止符。
|
||||
//始终后附终止空字符到末尾(故函数可写入的最大字节数是 count + 1 )。
|
||||
|
||||
//若目标数组没有对于 dest 和 src 的首 count 个字符加上终止空字符的足够空间,
|
||||
//则行为未定义。若源与目标对象重叠,则行为未定义。若 dest 不是指向空终止字节字符串的指针,
|
||||
//或 src 不是指向字符数组的指针,则行为未定义。
|
||||
|
||||
// 现在我来说一下我认为这个程序可能存在的问题:
|
||||
// 1.dest 函数本身加上 count 个字符是否会造成数组越界?
|
||||
// 2.dest 函数有没有 '\0' 我没有检测。如果没有,则很有可能造成越界,并且将造成非法内存访问和改写。
|
||||
// 3.如果 src 没有 '\0' 而 count 又大于 src字符数组有意义内容的大小,或者 src字符数组本身的大小,
|
||||
// 这时也会造成非法内存访问。
|
||||
// 4.src 是不是指向字符数组的指针?
|
||||
|
||||
// 如果还有问题欢迎补充,如果你有更好的解法(C语言)欢迎告诉我,我会加上你的真知灼见!(私信我即可)
|
||||
|
||||
char* MyStrncat(char* dest, const char* src, int count) {
|
||||
|
||||
assert(dest && src);
|
||||
|
||||
char* NewStr = dest;
|
||||
|
||||
// 循环结束 NewStr 指向 dest 字符串 '\0' 所在位置
|
||||
while (*NewStr != '\0') {
|
||||
++NewStr;
|
||||
}
|
||||
while (count-- && (*NewStr++ = *src++));
|
||||
|
||||
*NewStr = '\0';
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
#include<stdio.h>
|
||||
#include<assert.h>
|
||||
|
||||
char* MyStrncpy(char* dest, char* src, int count);
|
||||
|
||||
int main(void) {
|
||||
|
||||
char dest[100];
|
||||
char src[50];
|
||||
int count = 0;
|
||||
|
||||
printf("Enter string dest: ");
|
||||
scanf("%s", dest);
|
||||
printf("Enter string src: ");
|
||||
scanf("%s", src);
|
||||
printf("Enter number of character(s) copy from src to dest: ");
|
||||
scanf("%d", &count);
|
||||
|
||||
MyStrncpy(dest, src, count);
|
||||
|
||||
printf("%s\n", dest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 这个函数同样是考虑不周全的:
|
||||
// 1. count 如果大于 字符数组dest 怎么办?
|
||||
// 2. count 大于 src 而 src 没有空字符怎么办?
|
||||
|
||||
char* MyStrncpy(char* dest,const char* src, int count) {
|
||||
|
||||
assert(dest && src);
|
||||
char* begin = dest;
|
||||
|
||||
while (count-- && (*begin++ = *src++));
|
||||
|
||||
// count 如果小于 src 的长度,那么得到的字符串是不含空字符的。
|
||||
// 加上 \0 是保险的。
|
||||
if (count + 1 == 0) {
|
||||
*begin = '\0';
|
||||
}
|
||||
else {
|
||||
while (count--) {
|
||||
*begin++ = *src++;
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
77
Coding/Exercises/advanced C/test/02/03 单身狗问题/SingleDog.c
Normal file
77
Coding/Exercises/advanced C/test/02/03 单身狗问题/SingleDog.c
Normal file
@@ -0,0 +1,77 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
// 找单身狗问题:
|
||||
// 1. 现有一个数组,只有一个元素只出现了一次,其他元素都出现了两次,请找出这个元素。
|
||||
// 2. 如果数组中有两个元素只出现了一次,其他都出现了两次,请找出这两个元素。
|
||||
|
||||
// 这两个问题都应用到了按位异或
|
||||
// 如果两个相同的数按位异或结果为零
|
||||
// 我们在前面的 两数平均值求法 中用异或来求出两数 二进制位 中 不同的位 表示的数的 和
|
||||
|
||||
|
||||
#include<stdio.h>
|
||||
|
||||
int FindOneSingleDog(int arr[], int size) {
|
||||
|
||||
int Dog = arr[0];
|
||||
|
||||
for (int i = 1; i < size; i++) {
|
||||
Dog = Dog ^ arr[i];
|
||||
}
|
||||
|
||||
return Dog;
|
||||
}
|
||||
|
||||
void FindTwoSingleDog(int arr[], int size, int* Dog1, int* Dog2) {
|
||||
|
||||
int rst = arr[0];
|
||||
int leftPos = 0;
|
||||
|
||||
for (int i = 1; i < size; i++) {
|
||||
rst = rst ^ arr[i];
|
||||
}
|
||||
//此时 rst 等价于 Dog1 ^ Dog2, 这个结果的比特位一定有一个是 1
|
||||
|
||||
// 找到第一个是 1 的比特位
|
||||
for (leftPos = 0; leftPos < 32; leftPos++) {
|
||||
if ((rst & (1 << leftPos)) == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 将数组元素分成两部分,一部分 leftPos 位为 1,另一部分不为 1
|
||||
// 那么 Dog1 和 Dog2 就在不同的组内了。再次按位异或,结果就是 Dog1 和 Dog2 的值
|
||||
|
||||
*Dog1 = 0;
|
||||
*Dog2 = 0;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
// leftPos 为 1 的组
|
||||
if ((arr[i] & (1 << leftPos)) == 1) {
|
||||
*Dog1 = *Dog1 ^ arr[i];
|
||||
}
|
||||
else {
|
||||
*Dog2 = *Dog2 ^ arr[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
||||
int arr1[] = { 1, 2, 3, 4, 1, 2, 3 };
|
||||
int size1 = sizeof(arr1) / sizeof(arr1[0]);
|
||||
int Dog;
|
||||
|
||||
int arr2[] = { 1, 1, 2, 2, 3, 4 };
|
||||
int size2 = sizeof(arr2) / sizeof(arr2[0]);
|
||||
int Dog1, Dog2;
|
||||
|
||||
Dog = FindOneSingleDog(arr1, size1);
|
||||
|
||||
FindTwoSingleDog(arr2, size2, &Dog1, &Dog2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
51
Coding/Exercises/advanced C/test/02/04 两数平均值/main.c
Normal file
51
Coding/Exercises/advanced C/test/02/04 两数平均值/main.c
Normal file
@@ -0,0 +1,51 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
// 今天在听课的时候听到一个老师讲 平均数求法,一时之间我竟无从下手,其实这个问题很早之前我们老师就说过了。
|
||||
// 虽然这种题确实是一种考验计算机专业的人的专业度的问题,但是大多老师讲完之后恐怕根本也不会去给你布置
|
||||
// 练习来强化你的记忆,所以你可能会很快忘记这种特殊的解法(也许是我太卑微了,大家都过目不忘??)。
|
||||
//所以,大多时候,讲到这个的老师无非是为了炫技,让你觉得他很专业。
|
||||
|
||||
// 但是我忘记了是真的,我没能想出来也是真的,我真菜啊,所以,我决定动手写一下
|
||||
|
||||
#include<stdio.h>
|
||||
|
||||
int main(void) {
|
||||
|
||||
int a = 10;
|
||||
int b = 11;
|
||||
double aver;
|
||||
int Aver;
|
||||
|
||||
// 方法1:直接求和除以2 这种方法的问题是:如果 a + b 会超过 int 最大能表示的范围,那结果就不是你想要的了
|
||||
|
||||
|
||||
|
||||
// 方法2:
|
||||
|
||||
// 确定两个的大小
|
||||
int max = a > b ? a : b;
|
||||
int min = a < b ? a : b;
|
||||
|
||||
// 平均值等于:大 - 后面这一坨 或者 小 + 后面这一坨
|
||||
aver = max - (max - min) / 2.0;
|
||||
|
||||
// 比较僵硬的是,整数的平均值要用浮点数来表示,不然可能会丧失精度
|
||||
printf("%.2f\n", aver);
|
||||
|
||||
|
||||
|
||||
// 方法3:这个方法可以说就是炫技的地方所在了,但是位运算确实效率是比较高的。
|
||||
|
||||
Aver = (a & b) + ((a ^ b) >> 1);
|
||||
printf("%d\n", Aver);
|
||||
|
||||
// a & b 得到的是 a,b 相同二进制位所表示的数
|
||||
// 比如 2 和 2 相同,那他们的平均值就是 2
|
||||
// a ^ b 表示 a 异或 b,得到的是 a,b 不同二进制位的和表示的数,除以 2,就是这部分两数的平均值。
|
||||
// 其实这个思想挺像 方法2 的思想,就将数分为两部分,依靠 2 进制的性质进行计算。
|
||||
// 位运算会向下取整,如果要求平均值有小数部分,那这个方法也就僵硬了
|
||||
// 参考博客:https://blog.csdn.net/weixin_43214609/article/details/83547313?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
// 字符串的左旋问题包含两点:
|
||||
// 1. 左旋字符串
|
||||
// 2. 判断字符串是否是另一个字符串左旋得到的
|
||||
// 这次我们一起处理这两个问题。
|
||||
|
||||
#include<stdio.h>
|
||||
#include<string.h>
|
||||
|
||||
// 让字符串左旋,我们可以先实现一个函数左旋一个字符,然后用另一个函数去调用这个函数,实现旋转多个字符
|
||||
|
||||
void LeftRotateOne(char str[], int size) {
|
||||
|
||||
char tmp;
|
||||
int i;
|
||||
|
||||
if (str == NULL)
|
||||
return;
|
||||
|
||||
if (size == 1)
|
||||
return;
|
||||
|
||||
// 先备份字符串第一个元素
|
||||
tmp = str[0];
|
||||
|
||||
// 将字符串从第二个元素开始依次向前移动一位
|
||||
for (i = 1; i < size; i++) {
|
||||
str[i - 1] = str[i];
|
||||
}
|
||||
|
||||
str[size - 1] = tmp;
|
||||
|
||||
}
|
||||
|
||||
// 这样的算法,如果 size 比较大,效率是很低的,因为数组每次移动复杂度都是 O(n)
|
||||
void LeftRotateN(char str[], int size, int n) {
|
||||
|
||||
if (str == NULL)
|
||||
return;
|
||||
|
||||
// 求模是因为:比如 “ABCD” 左旋 4 次 等于没左旋
|
||||
for (int i = 0; i < n % size; i++) {
|
||||
LeftRotateOne(str, size);
|
||||
}
|
||||
}
|
||||
// 判断 str1 str2 是否互为左旋
|
||||
int LeftRotateCmp(char str1[], char str2[]) {
|
||||
|
||||
int len1 = strlen(str1);
|
||||
int len2 = strlen(str2);
|
||||
|
||||
if (str1 == NULL || str2 == NULL)
|
||||
return -1;
|
||||
|
||||
if (len1 != len2)
|
||||
return 0;
|
||||
|
||||
// 将 str1 用 for 循环左旋 len1 次,每次左旋后与 str2 比较
|
||||
// 其实最多只用循环 len1 - 1 次即可,但是如果 写成 i < len1 - 1
|
||||
// 最后依次旋转的结果不会被比较!!而且即使多旋转了一次,相当于判断结束将 str1 归位了,一举两得!
|
||||
for (int i = 0; i < len1; i++) {
|
||||
if (strcmp(str1, str2) == 0) {
|
||||
return 1;
|
||||
}
|
||||
LeftRotateOne(str1, len1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
||||
char str[] = "ABCD";
|
||||
char str1[] = "DABC";
|
||||
|
||||
//LeftRotateOne(str, strlen(str));
|
||||
//LeftRotateN(str, strlen(str), 4);
|
||||
int a = LeftRotateCmp(str, str1);
|
||||
|
||||
printf("%d", a);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
3
Coding/Exercises/advanced C/test/02/05 字符串左旋问题/readme.md
Normal file
3
Coding/Exercises/advanced C/test/02/05 字符串左旋问题/readme.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 字符串左旋
|
||||
|
||||
这个问题我们在【习题一】也就是 上一级 【01】目录中讨论过,此次给出另一个中解法
|
||||
Reference in New Issue
Block a user