主程序
// entry.c
// 主函数所在的文件。
// 作者:黄铎彦,福州大学软件学院软件工程系 2022 级(4)班,学号 222200428,
// 邮箱 HuangDY2016@outlook.com,编写完成于 2023/1/29。Copyright (C) 2023。
// 本程序使用基于链表的栈和队列,先将用户输入的中缀表达式转化成后缀表达式,
// 再计算转化后的后缀表达式,最后输出计算结果。支持加减乘除、乘方和小括号运算。
// 开发环境:Microsoft Visual Studio Enterprise 2022(64位)版本 17.4.4,
// 工作负荷为 使用 C++ 的桌面开发,C 语言标准为 ISO C17(2018)/std:c17;
// Microsoft Windows 10 家庭版 22H2(内部版本 19045.2486)64位。
// 运行环境:64 位的 Windows,建议为 Windows 10 或更高版本。
// 参考文献:《数据结构与算法分析(C语言描述)》第 2 版,Mark Allen Weiss 著
// 修改记录:
// 2023/2/12 新增了一个错误类型:非法的数字。当用户输入形如 1e 的数字时报错。
// 已知问题:
// 2023/1/30 不能通过 (-0.125)^(1/3) 来计算立方根,它会输出 -nan(ind);
// 按 F7 选择之前输入的表达式再按下回车之后,没反应,再按下回车之后,
// 报告错误:操作数太多。这可能是 Windows 控制台的 Bug。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include "calc_expr.h"
static inline void show_usage(void);
int main(void)
{
Queue q;
int err = init_q(&q);
if (err)
{
SHOW_ERR("初始化队列失败!\n");
return NO_MEM;
}
show_usage();
while (1)
{
err = in_to_post(&q);
if (err) SHOW_ERR("中缀转后缀失败,代码 %d。\n\n", err);
else
{
#ifndef NDEBUG // 此宏仅在 Release 模式下有定义
pri_q(&q); // Debug 模式下用于调试的代码
#endif // !NDEBUG
double res;
err = calc_post(&q, &res);
if (err) SHOW_ERR("计算后缀失败,代码 %d。\n\n", err);
else printf("表达式的值为 %g。\n\n", res);
}
empty_q(&q);
}
return 0;
}
// 提示用户程序的用法
inline void show_usage(void)
{
puts("欢迎使用表达式计算程序!版本:1.0,作者:黄铎彦。\n"
"本程序可以计算包含加(+)、减或相反数(-)、乘(*)、除(/)、"
"乘方(^)和小括号运算符的算式。\n\n您每次只需在英文模式下"
"输入待计算的表达式(注意:乘号不可省略),\n"
"按下回车,计算结果就会显示在输入行的下方。\n"
"您可以一直输入你想计算的表达式,直到您点击关闭按钮退出本程序。\n\n"
"示例输入:\n1/(2^(1/2)+1)\n\n"
"示例输出:\n表达式的值为 0.414214。\n\n"
"如果您在使用过程遇到问题,请联系我,"
"邮箱:HuangDY2016@outlook.com。祝您使用愉快!\n");
}
依赖的各个文件
头文件
calc_expr.h
// calc_expr.h
// 计算表达式的值
#pragma once
#ifndef _INC_CALC_EXPR
#define _INC_CALC_EXPR
#include "queue.h"
#include <stdio.h>
/* 各种错误代码 */
#define INVALID_OP -1 // 非法的运算符
#define MISSING_BRACKET_R -2 // 缺少右括号
#define MISSING_BRACKET_L -3 // 缺少左括号
#define MISSING_NUM -4 // 缺少操作数
#define TOO_MANY_NUM -5 // 过多的操作数
#define INVALID_NUM -6 // 非法的数字,比如 1e,2.e.。2023/2/12
#define OP_SIZE sizeof(int) // 运算符的大小
#define NUM_SIZE sizeof(double) // 数的大小
/* 各种运算符 */
#define ADD_OP '+' // 加法运算符
#define SUB_OP '-' // 减法运算符
#define NEG_OP '_' // 负号(与减法运算符区别)
#define MUL_OP '*' // 乘法运算符
#define DIV_OP '/' // 除法运算符
#define POW_OP '^' // 乘方运算符
#define LBRKT '(' // 左括号
#define RBRKT ')' // 右括号
/// <summary>
/// 在标准错误流(stderr)中输出错误信息
/// </summary>
/// <param name="__VA_ARGS__">可变参数,包含两部分:
/// <para>1.(必选)存储错误信息的格式字符串(需要自行添加换行符)。</para>
/// <para>2.(可选)与格式字符串中的转换说明相对应的一个或多个参数。</para>
/// <para>当两个部分同时存在时,必须用逗号隔开。</para>
/// </param>
#define SHOW_ERR(...) fprintf(stderr, __VA_ARGS__)
/// <summary>
/// 从标准输入流中读取中缀表达式,将它转化为后缀表达式后存放在队列中
/// <para>
/// 不能保证生成的后缀表达式不会出现缺操作数、操作数过多的错误。但可以识别出
/// 中缀表达式中括号不匹配、运算符非法的错误。出现错误时表达式队列可能非空。
/// </para>
/// <para>函数定义在 in_to_post.c 中</para>
/// </summary>
/// <param name="pq">
/// 存储后缀表达式的队列(已初始化,主调函数中的自动变量)的地址
/// </param>
/// <returns>如果转化过程中没有错误,返回 0;否则返回非 0。</returns>
int in_to_post(Queue* const pq);
/// <summary>
/// 计算后缀表达式的值。空表达式的值为 0。出现错误时表达式队列可能非空。
/// </summary>
/// <para>函数定义在 calc_post.c 中</para>
/// <param name="pq">
/// 存储后缀表达式的队列(已初始化,主调函数中的自动变量)的地址
/// </param>
/// <param name="pval">参数输出:后缀表达式的值</param>
/// <returns>如果计算成功,返回 0;否则说明表达式非法,返回非 0。</returns>
int calc_post(Queue* const pq, double* const pval);
#endif // !_INC_CALC_EXPR
linked_list.h
// linked_list.h
// 带哨兵结点的单向链表。所有非内联函数的定义都在 linked_list.c 中。
#pragma once
#ifndef _INC_LINKED_LIST
#define _INC_LINKED_LIST
#define NO_MEM 1 // 内存申请失败导致的内存不足
#include <stddef.h>
#include <assert.h>
/// <summary>
/// 链表的数据域。
/// <para>
/// 因为所存储的数据类型不明确,所以需要根据数据的大小进行动态内存分配。
/// </para>
/// </summary>
typedef struct Data
{
/// <summary>
/// 指向数据的指针
/// <para>除哨兵结点数据域的这个指针指向空之外,
/// 其余的都需要动态内存分配</para>
/// </summary>
void* pointer;
size_t size; // 数据的大小
}Data;
/// <summary>
/// 链表的结点。
/// <para>对链表的操作,除初始化需要用指向哨兵结点的指针的指针作参数外,
/// 其余需要用指向哨兵结点的指针作参数。</para>
/// </summary>
typedef struct Node
{
Data data;
struct Node* next;
}Node;
/// <summary>
/// 初始化链表
/// </summary>
/// <param name="pphead">指向哨兵的指针的地址(非空)</param>
/// <returns>如果成功,返回 0;否则返回 NO_MEM。</returns>
int init_list(Node** const pphead);
/// <summary>
/// 释放链表
/// </summary>
/// <param name="phead">指向哨兵的指针</param>
void free_list(Node* const phead);
/// <summary>
/// 检查链表(已初始化)是否为空
/// </summary>
/// <param name="phead">指向哨兵的指针</param>
/// <returns>如果链表为空,返回真(1);否则返回假(0)。</returns>
inline int is_empty_list(const Node* const phead)
{
assert(phead != NULL);
return phead->next == NULL;
}
/// <summary>
/// 删除链表(非空)的第一个结点
/// </summary>
/// <param name="phead">指向哨兵的指针</param>
void del_first(Node* const phead);
/// <summary>
/// 拷贝数据到新结点中
/// </summary>
/// <param name="pnew">使用 malloc 函数新建的结点(非空)</param>
/// <param name="pdata">指向数据的指针(非空)</param>
/// <param name="data_size">数据的大小</param>
/// <returns>如果操作成功,返回 0;否则说明空间不够,返回 NO_MEM。</returns>
int cpy_data(Node* pnew, const void* const pdata, const size_t data_size);
/// <summary>
/// 如果链表非空,清空链表中除哨兵结点外的所有结点及内容;否则什么也不做
/// </summary>
/// <param name="phead">指向哨兵的指针</param>
void empty_list(Node* const phead);
#endif // !_INC_LINKED_LIST
queue.h
// queue.h
// 基于链表的队列。所有非内联函数的定义都在 queue.c 中。
#pragma once
#ifndef _INC_QUEUE
#define _INC_QUEUE
#include "linked_list.h"
#include <string.h>
/// <summary>
/// 队列类型。
/// <para>将此类型作为函数参数时,应传指针。</para>
/// </summary>
typedef struct Queue
{
Node* pqlist; // 将链表中指向哨兵结点的指针抽象为队列中的元素列表
Node* front, * rear;
}Queue;
/// <summary>
/// 初始化队列
/// </summary>
/// <param name="pq">队列(主调函数中的自动变量)的地址</param>
/// <returns>如果成功,返回 0;否则返回 NO_MEM。</returns>
int init_q(Queue* const pq);
/ <summary>
/ 释放队列
/ </summary>
/ <param name="pq">队列(主调函数中的自动变量)的地址</param>
//void free_q(Queue* const pq);
/// <summary>
/// 入队
/// </summary>
/// <param name="pq">队列(已初始化,主调函数中的自动变量)的地址</param>
/// <param name="pdata">指向数据的指针(非空)</param>
/// <param name="data_size">数据的大小</param>
/// <returns>如果操作成功,返回 0;否则说明空间不够,返回 NO_MEM。</returns>
int en_q(Queue* const pq, const void* const pdata, const size_t data_size);
/// <summary>
/// 检查指针 <paramref name="pq"/> 指向的队列(已初始化)是否为空队列
/// </summary>
/// <returns>如果为空队列,返回真(1);否则返回假(0)。</returns>
inline int is_empty_q(const Queue* const pq)
{
return pq->front == NULL;
}
/// <summary>
/// 获取队列头部结点的数据的大小
/// </summary>
/// <param name="pq">队列(非空,主调函数中的自动变量)的地址</param>
/// <returns>队头第一个结点的数据大小</returns>
inline size_t get_first_size(const Queue* const pq)
{
assert(!is_empty_q(pq));
return pq->front->data.size;
}
/// <summary>
/// 读取队头数据。警告:本函数不检查缓冲区的大小!
/// </summary>
/// <param name="pq">队列(非空,主调函数中的自动变量)的地址</param>
/// <param name="buf">待将队头数据写入的缓冲区</param>
inline void get_front(const Queue* const pq, void* const buf)
{
assert(!is_empty_q(pq));
#define FRONT_DATA (pq->front->data)
memcpy(buf, FRONT_DATA.pointer, FRONT_DATA.size);
#undef FRONT_DATA
}
/// <summary>
/// 删除队头元素
/// </summary>
/// <param name="pq">队列(非空,主调函数中的自动变量)的地址</param>
void de_q(Queue* const pq);
#ifndef NDEBUG
/// <summary>
/// 打印队列内容
/// </summary>
/// <param name="pq">队列(非空,主调函数中的自动变量)的地址</param>
void pri_q(Queue* pq);
#endif // !NDEBUG
/// <summary>
/// 如果队列非空,则清空队列中的内容;否则什么也不做。
/// </summary>
/// <param name="pq">队列(已初始化,主调函数中的自动变量)的地址</param>
void empty_q(Queue* pq);
#endif // !_INC_QUEUE
stack.h
// stack.h
// 基于链表的栈。所有非内联函数的定义都在 stack.c 中。
#pragma once
#ifndef _INC_STACK
#define _INC_STACK
#include "linked_list.h"
#include <string.h>
/// <summary>
/// 栈的类型。
/// <para>作为函数参数时,初始化时应传指针,其他时候直接传变量。</para>
/// </summary>
typedef Node* Stack;
/// <summary>
/// 初始化栈
/// </summary>
/// <param name="pstack">指向栈的指针</param>
/// <returns>如果成功,返回 0;否则返回 NO_MEM。</returns>
inline int init_stack(Stack* const pstack)
{
return init_list(pstack);
}
/// <summary>
/// 释放栈 <paramref name="stack"/> 的所有空间
/// </summary>
inline void free_stack(const Stack stack)
{
free_list(stack);
}
/// <summary>
/// 检查栈 <paramref name="stack"/>(已初始化)是否为空
/// </summary>
/// <returns>如果为空,返回真(1);否则返回假(0)。</returns>
inline int is_empty_stack(const Stack stack)
{
return is_empty_list(stack);
}
/// <summary>
/// 进栈
/// </summary>
/// <param name="stack">栈(已初始化)</param>
/// <param name="pdata">指向数据的指针(非空)</param>
/// <param name="data_size">数据的大小</param>
/// <returns>如果操作成功,返回 0;否则说明空间不够,返回 NO_MEM。</returns>
int push(const Stack stack, const void* const pdata, const size_t data_size);
/// <summary>
/// 读取栈顶数据。警告:本函数不检查缓冲区的大小!
/// </summary>
/// <param name="stack">栈(非空)</param>
/// <param name="buf">待将栈顶数据写入的缓冲区</param>
inline void get_top(const Stack stack, void* const buf)
{
assert(!is_empty_stack(stack));
#define TOP_DATA (stack->next->data)
memcpy(buf, TOP_DATA.pointer, TOP_DATA.size);
#undef TOP_DATA
}
/// <summary>
/// 删除栈顶元素
/// </summary>
/// <param name="stack">栈(非空)</param>
inline void pop(const Stack stack)
{
del_first(stack);
}
#endif // !_INC_STACK
源文件
calc_post.c
// calc_post.c
#include "calc_expr.h"
#include "stack.h"
#include <math.h>
#include <stdlib.h>
/// <summary>
/// 检查错误代码是否非零。如果非零则结束函数,返回该错误代码。
/// </summary>
/// <param name="err_code">错误代码</param>
#define CHKERR(err_code) if (err_code) return err_code
#pragma region 计算后缀表达式所需的内部函数原型
/// <summary>
/// 如果队头是数字,就调用本函数将它压入栈中。
/// </summary>
/// <param name="pq">
/// 保存后缀表达式的队列(非空,主调函数中的自动变量)的地址
/// </param>
/// <param name="s">处理和保存操作数用的栈(已初始化)</param>
/// <returns>操作成功返回 0,出错返回非 0 值。</returns>
static int num_proc(Queue* const pq, Stack s);
/// <summary>
/// 如果队头是运算符,就调用本函数,从栈中弹出两个数来运算,再将结果压入栈中。
/// </summary>
/// <param name="pq">
/// 保存后缀表达式的队列(非空,主调函数中的自动变量)的地址
/// </param>
/// <param name="s">处理和保存操作数用的栈(已初始化)</param>
/// <returns>操作成功返回 0,出错返回非 0 值。</returns>
static int op_proc(Queue* const pq, Stack s);
/// <summary>
/// 从操作数栈的栈顶弹出一个操作数
/// </summary>
/// <param name="s">操作数栈</param>
/// <param name="pnum">参数输出:操作数的地址</param>
/// <returns>操作成功返回 0;栈为空时说明缺少操作数,返回非 0。</returns>
static int pop_num(Stack s, double* pnum);
#pragma endregion
int calc_post(Queue* const pq, double* const pval)
{
assert(pval != NULL);
*pval = 0;
Stack s; // 存放操作数用的栈,元素都是 double 型
int err = init_stack(&s);
while (!err && !is_empty_q(pq))
{
size_t fs = get_first_size(pq); // 队头元素数据的大小
if (fs == NUM_SIZE) err = num_proc(pq, s);
else if (fs == OP_SIZE) err = op_proc(pq, s);
else assert(0);
}
if (!err && !is_empty_stack(s))
{
pop_num(s, pval);
if (!is_empty_stack(s))
{
SHOW_ERR("操作数太多!\n");
err = TOO_MANY_NUM;
}
}
free_stack(s);
return err;
}
#pragma region 计算后缀表达式所需的内部函数定义
int num_proc(Queue* const pq, Stack s)
{
double n;
get_front(pq, &n);
de_q(pq);
return push(s, &n, NUM_SIZE);
}
int op_proc(Queue* const pq, Stack s)
{
int op;
get_front(pq, &op);
de_q(pq);
double right; // 右操作数
int err = pop_num(s, &right);
CHKERR(err);
double res;
if (op == NEG_OP) res = -right;
else
{
double left; // 左操作数
err = pop_num(s, &left);
CHKERR(err);
switch (op)
{
case ADD_OP: res = left + right; break;
case SUB_OP: res = left - right; break;
case MUL_OP: res = left * right; break;
case DIV_OP: res = left / right; break;
case POW_OP: res = pow(left, right); break;
default : assert(0);
}
}
if (!err) push(s, &res, NUM_SIZE);
return err;
}
int pop_num(Stack s, double* pnum)
{
if (!is_empty_stack(s))
{
get_top(s, pnum);
pop(s);
return 0;
}
SHOW_ERR("缺少操作数!\n");
return MISSING_NUM;
}
#pragma endregion
in_to_post.c
// in_to_post.c
#define _CRT_SECURE_NO_WARNINGS
#include <string.h>
#include <stdbool.h>
#include <ctype.h>
#include "calc_expr.h"
#include "stack.h"
#pragma region 计算中缀表达式所需的类型和宏定义
typedef enum Op_rank // 运算符的优先级,值越大,优先级越高
{
add_sub, mul_div,
neg, // 负号的优先级
power, bracket
}Op_rank;
/// <summary>
/// 在栈非空且栈顶不为左括号并且附加条件成立的情况下,
/// 将栈顶运算符不断弹出到后缀表达式队列中。
/// <para>
/// 在此宏中,已弹出的栈顶运算符记为 top_op,可以在附加条件中引用。
/// </para>
/// </summary>
/// <param name="stack">处理和保存运算符用的栈(已初始化)</param>
/// <param name="pq">队列(已初始化,主调函数中的自动变量)的地址</param>
/// <param name="additional_condition">
/// 附加条件,不做要求应该填 true(1)。
/// </param>
#define POP_UNTIL_LEFT_BRACKET(pq, stack, additional_condition) \
{ \
int top_op = 0; \
while (!is_empty_stack(stack) && \
(get_top(stack, &top_op), top_op != '(') && (additional_condition)) \
{ \
pop(stack); \
en_q(pq, &top_op, OP_SIZE); \
} \
}
// 如果当前输入行中还有字符,则使用此宏将包括回车在内的剩余字符读掉。
#define EAT_LINE while (getchar() != '\n')
#pragma endregion
#pragma region 计算中缀表达式所需的内部函数原型
/// <summary>
/// 获取运算符的优先级
/// </summary>
/// <param name="op_ch">
/// 用字符表示的运算符,必须是加、减、乘、除、负、乘方和圆括号中的一个
/// </param>
/// <returns>
/// Op_rank 枚举值,值越大,优先级越高。
/// <para>如果运算符非法,则断言失败,返回 INVALID_OP。</para>
/// </returns>
static Op_rank get_op_rank(const int op_ch);
/// <summary>
/// 比较 <paramref name="op1"/> 和 <paramref name="op2"/> 这两个运算符的优先级
/// </summary>
/// <returns>
/// 如果 <paramref name="op1"/> 的优先级比 <paramref name="op2"/> 高,返回正数;
/// 如果两个运算符的优先级相等,返回 0;
/// 如果 <paramref name="op1"/> 的优先级比 <paramref name="op2"/> 低,返回负数。
/// </returns>
static inline int cmp_rank(const int op1, const int op2);
/// <summary>
/// 判断运算符是否合法
/// </summary>
/// <param name="op">用字符存储的运算符</param>
/// <returns>
/// 如果运算符是加(+)减(-)乘(*)除(/)乘方(^)和圆括号中的任何一个,
/// 返回真;否则返回假。
/// </returns>
static inline int is_valid_op(const int op);
/// <summary>
/// 当前输入的一行没有字符可读时的处理程序。
/// 如果栈非空,则将栈中元素依次弹出,放入队列。
/// </summary>
/// <param name="pq">队列(已初始化,主调函数中的自动变量)的地址</param>
/// <param name="s">处理和保存运算符用的栈(已初始化)</param>
/// <returns>仅在括号未成对出现时返回非 0 值,正常情况下返回 0。</returns>
static int no_more_ch_proc(Queue* const pq, Stack s);
/// <summary>
/// 读到右括号时的处理程序。不断弹出栈顶的元素,直到弹出一个左括号。
/// </summary>
/// <param name="pq">队列(已初始化,主调函数中的自动变量)的地址</param>
/// <param name="s">处理和保存运算符用的栈(已初始化)</param>
/// <returns>仅在括号未成对出现时返回非 0 值,正常情况下返回 0。</returns>
static int rbracket_proc(Queue* const pq, Stack s);
/// <summary>
/// 除乘方和右括号之外的运算符处理程序。
/// 不断弹出栈顶的运算符并放入队列,直到遇到优先级更低的运算符或者左括号,
/// 最后将新运算符入栈。
/// </summary>
/// <param name="pq">队列(已初始化,主调函数中的自动变量)的地址</param>
/// <param name="s">处理和保存运算符用的栈(已初始化)</param>
/// <param name="new_op">新遇到的的运算符,非乘方或右括号</param>
/// <returns>仅在正常情况下返回 0。</returns>
static int other_op_proc(Queue* const pq, Stack s, const int new_op);
// 跳过输入中的空格或水平制表符,
// 读取并返回下一个既不是空格也不是水平制表符的字符。
static int ch_after_blank(void);
/// <summary>
/// 从输入中读取 double 型数字,并放入队列中。
/// </summary>
/// <param name="pq">队列(已初始化,主调函数中的自动变量)的地址</param>
/// <returns>仅在正常情况下返回 0。</returns>
static int num_proc(Queue* pq);
#pragma endregion
int in_to_post(Queue* const pq)
{
Stack s; // 存放运算符用的栈,元素都是整型
int err = init_stack(&s);
int unary = true; // 读取到的加号或减号是否为一元的正/负号
int ch;
while (!err && (ch = ch_after_blank()) != '\n')
{
if (isdigit(ch))
{
ungetc(ch, stdin);
unary = false; // 数字后面的加号或减号一定是二元的
err = num_proc(pq);
}
else if (is_valid_op(ch))
{
// 在下面对各种运算符的处理中,
// 由于乘方和负号是右结合的,所以必须直接压栈。
if (ch == POW_OP)
{
unary = true;
err = push(s, &ch, OP_SIZE);
}
else if (ch == RBRKT)
{
unary = false;
err = rbracket_proc(pq, s);
}
else if ((ch == SUB_OP || ch == ADD_OP) && !unary
|| ch == MUL_OP || ch == DIV_OP || ch == LBRKT)
{
unary = true;
err = other_op_proc(pq, s, ch);
}
else if (ch == SUB_OP && unary)
{
ch = NEG_OP;
err = push(s, &ch, OP_SIZE);
}
else assert(ch == ADD_OP && unary); // 忽略正号
}
else
{
SHOW_ERR("%c 不是合法的运算符!\n", (char)ch);
EAT_LINE;
err = INVALID_OP;
}
}
if (!err) err = no_more_ch_proc(pq, s);
free_stack(s);
return err;
}
#pragma region 计算中缀表达式所需的内部函数定义
Op_rank get_op_rank(const int op_ch)
{
switch (op_ch)
{
case ADD_OP: case SUB_OP: return add_sub;
case NEG_OP: return neg;
case MUL_OP: case DIV_OP: return mul_div;
case POW_OP: return power;
case LBRKT : case RBRKT : return bracket;
default: assert(0); return INVALID_OP;
}
}
inline int cmp_rank(const int op1, const int op2)
{
return get_op_rank(op1) - get_op_rank(op2);
}
inline int is_valid_op(const int op)
{
return strchr("+-*/^()", op) != NULL;
}
int no_more_ch_proc(Queue* const pq, Stack s)
{
POP_UNTIL_LEFT_BRACKET(pq, s, true);
if (!is_empty_stack(s))
{
SHOW_ERR("缺少右括号!\n");
return MISSING_BRACKET_R;
}
return 0;
}
int rbracket_proc(Queue* const pq, Stack s)
{
POP_UNTIL_LEFT_BRACKET(pq, s, true);
if (is_empty_stack(s))
{
SHOW_ERR("缺少左括号!\n");
EAT_LINE;
return MISSING_BRACKET_L;
}
pop(s); // 弹出左括号
return 0;
}
int other_op_proc(Queue* const pq, Stack s, const int new_op)
{
assert(new_op != POW_OP && new_op != RBRKT);
POP_UNTIL_LEFT_BRACKET(pq, s, cmp_rank(new_op, top_op) <= 0);
return push(s, &new_op, OP_SIZE);
}
int ch_after_blank(void)
{
int ch;
while (isblank(ch = getchar()));
return ch;
}
int num_proc(Queue* pq)
{
double num;
if (scanf("%lf", &num) != 1) // 2023/2/12
{ // 增加此 if 语句判断块
SHOW_ERR("非法的数字!\n");
EAT_LINE;
return INVALID_NUM;
}
return en_q(pq, &num, NUM_SIZE);
}
#pragma endregion
linked_list.c
// linked_list.c
#include "linked_list.h"
#include <stdlib.h>
#include <string.h>
int init_list(Node** const pphead)
{
assert(pphead != NULL);
*pphead = malloc(sizeof(Node));
if (*pphead == NULL) return NO_MEM;
(*pphead)->data.pointer = (*pphead)->next = NULL;
(*pphead)->data.size = 0;
return 0;
}
void free_list(Node* const phead)
{
empty_list(phead);
free(phead);
}
void del_first(Node* const phead)
{
assert(!is_empty_list(phead));
Node* tmp = phead->next;
phead->next = tmp->next;
free(tmp->data.pointer);
free(tmp);
}
int cpy_data(Node* pnew, const void* const pdata, const size_t data_size)
{
assert(pnew != NULL && pdata != NULL);
pnew->data.size = data_size;
pnew->data.pointer = malloc(data_size);
if (pnew->data.pointer == NULL) return NO_MEM;
memcpy(pnew->data.pointer, pdata, data_size);
return 0;
}
void empty_list(Node* const phead)
{
Node* next;
for (Node* p = phead->next; p != NULL; p = next)
{
next = p->next;
free(p->data.pointer);
free(p);
}
phead->next = NULL;
}
queue.c
// queue.c
#include "queue.h"
#include <stdio.h>
#include <stdlib.h>
int init_q(Queue* const pq)
{
assert(pq != NULL);
pq->front = pq->rear = NULL;
return init_list(&pq->pqlist);
}
//void free_q(Queue* const pq)
//{
// assert(pq != NULL);
// free_list(pq->pqlist);
// pq->front = pq->rear = NULL;
//}
int en_q(Queue* const pq, const void* const pdata, const size_t data_size)
{
assert(pq != NULL && pq->pqlist != NULL && pdata != NULL);
Node* pnew = malloc(sizeof(Node));
if (pnew == NULL) return NO_MEM;
pnew->next = NULL;
if (is_empty_q(pq))
pq->front = pq->rear = pq->pqlist->next = pnew;
else
{
pq->rear->next = pnew;
pq->rear = pnew;
}
return cpy_data(pnew, pdata, data_size);
}
void de_q(Queue* const pq)
{
pq->front = pq->front->next;
if (pq->front == NULL) pq->rear = NULL;
del_first(pq->pqlist);
}
void empty_q(Queue* pq)
{
assert(pq != NULL);
empty_list(pq->pqlist);
pq->front = pq->rear = NULL;
}
#ifndef NDEBUG
void pri_q(Queue* pq)
{
for (Node* p = pq->front; p != NULL; p = p->next)
{
if (p->data.size == sizeof(int))
printf("%c ", *(char*)p->data.pointer);
else
printf("%g ", *(double*)p->data.pointer);
}
putchar('\n');
}
#endif // !NDEBUG
stack.c
// stack.c
#include "stack.h"
#include <stdlib.h>
#include <assert.h>
int push(const Stack stack, const void* const pdata, const size_t data_size)
{
assert(stack != NULL && pdata != NULL);
Node* pnew = malloc(sizeof(Node));
if (pnew == NULL) return NO_MEM;
#define PHEAD stack
pnew->next = PHEAD->next;
PHEAD->next = pnew;
#undef PHEAD
return cpy_data(pnew, pdata, data_size);
}