数据结构应用实例#栈&单链表#简易计算器

使用数据结构中的栈以及链表来制作一个能够处理中缀表达式的计算程序。

在该程序中输入的数字可以是任意正数、小数。(忘了添加对负数的支持,尽快补上)

输入的运算符可以是 + - * / ( )

但请确保输入的表达式合法。

中缀表达式:

形如

9+(3-1)*3+10/2

的数学表达式。特点是运算符在被运算的两个数字中间,也就是我们日常接触的算式。

后缀表达式:

以上面的中缀表达式 9+(3-1)*3+10/2 为例

转换成后缀表达式为 9 3 1-3*+ 10 2/+

特点是运算符紧跟在被运算的两个数之后,这样方便程序的理解与计算。

只要顺序读取数字并根据其后的运算符进行相应的操作即可得到结果。

对于以上的转换如有不懂可参考

将中缀表达式转化为后缀表达式

GitHub地址

程序大致分为3步实现:

1)将输入的字符串拆分为数字与运算符,用一个链表储存它们,方便接下来的计算

2)使用仅储存运算符的栈将该中缀表达式转换为后缀表达式

3)使用仅储存数字的栈来运算后缀表达式得到最终答案

为实现这3步,代码有点繁琐。。。用了多个文件来分别实现具体功能

以下是各个代码及大概作用,由于比较长,全部折叠了。

Stack.h & Stack.c

实现了一个储存运算符的栈(用于进行第2步)和一个储存数字的栈(用于第3步)

 1 //////////////
 2 // Stack.h //
 3 //////////////
 4
 5 #ifndef _STACK_H_
 6 #define _STACK_H_
 7
 8 #include<stdlib.h>
 9
10 #define STACK_INIT_SIZE 10
11 #define STACKINCREMENT 10
12
13 //优先级的定义
14 #define TopLow -1
15 #define Equal 0
16 #define TopHigh 1
17 //输入的是个括号
18 #define InputIsBrace_1 2
19 #define InputIsBrace_2 3
20 //当前栈顶是个括号
21 #define TopIsBrace 4
22
23 typedef char SymType;
24 typedef float NumType;
25
26 //第一步
27 //将中缀表达式转换为后缀表达式
28 //该栈用于储存符号
29 struct _Operator
30 {
31     SymType *Top,*Bottom;
32     int stacksize;
33 };
34 typedef struct _Operator OPERATOR;
35
36 //新建栈
37 int InitStack_O(OPERATOR *S);
38
39 //销毁栈
40 int DestroyStack_O(OPERATOR *S);
41
42 //检测栈是否为空
43 //返回1表示为空
44 int StackEmpty_O(OPERATOR S);
45
46 //获取栈顶部元素
47 SymType GetTop_O(OPERATOR S);
48
49 //与栈顶的优先级进行比较
50 int TopIsPriority_O(OPERATOR S, SymType Input,SymType Top);
51
52 //Push操作,入栈,压栈
53 int Push_O(OPERATOR *S, SymType e);
54
55 //Pop操作,出栈
56 SymType Pop_O(OPERATOR *S);
57
58 //以上为符号栈
59 //=====================================================================
60 //以下为数字栈
61
62 //第二步
63 //计算后缀表达式的值
64 //用于储存数据
65 struct _Number
66 {
67     NumType *Top, *Bottom;
68     int stacksize;
69 };
70 typedef struct _Number NUMBER;
71
72 //新建栈
73 int InitStack_N(NUMBER *S);
74
75 //销毁栈
76 int DestroyStack_N(NUMBER *S);
77
78 //获取栈顶部元素
79 NumType GetTop_N(NUMBER S);
80
81 //Push操作,入栈,压栈
82 int Push_N(NUMBER *S, NumType e);
83
84 //Pop操作,出栈
85 NumType Pop_N(NUMBER *S);
86
87 //接受运算符
88 //取栈顶的两个数字
89 //进行相应运算
90 void DoTheMath(NUMBER*,char);
91
92 #endif

Stack.h

  1 //////////////
  2 // Stack.c //
  3 /////////////
  4
  5 #include"Stack.h"
  6
  7 int InitStack_O(OPERATOR *S)
  8 {
  9     //创建出设定长度的顺序表,地址赋给bottom指针
 10     S->Bottom = (SymType*)malloc(STACK_INIT_SIZE*sizeof(SymType));
 11     if (!S->Bottom)
 12     {
 13         return 0;
 14     }
 15     S->stacksize = STACK_INIT_SIZE;
 16     S->Top = S->Bottom;
 17     return 1;
 18 }
 19
 20 int DestroyStack_O(OPERATOR *S)
 21 {
 22     S->Top = NULL;
 23     free(S->Bottom);
 24     S->Bottom = NULL;
 25     return 1;
 26 }
 27
 28 int StackEmpty_O(OPERATOR S)
 29 {
 30     if (S.Bottom == S.Top)
 31     {
 32         return 1;
 33     }
 34     else
 35     {
 36         return 0;
 37     }
 38 }
 39
 40 SymType GetTop_O(OPERATOR S)
 41 {
 42     if (S.Bottom != S.Top)
 43     {
 44         //由于top指向的是最顶上元素的下一个位置
 45         //所以取出最顶上元素的时候
 46         //要把top减去1
 47         return *(S.Top - 1);
 48     }
 49     return 0;
 50 }
 51
 52 //与栈顶的优先级进行比较
 53 int TopIsPriority_O(OPERATOR S, SymType Input,SymType top)
 54 {
 55     if (Input == ‘(‘)
 56     {
 57         return InputIsBrace_1;
 58     }
 59     if (Input == ‘)‘)
 60     {
 61         return InputIsBrace_2;
 62     }
 63     if (top == ‘(‘)
 64     {
 65         return TopIsBrace;
 66     }
 67     //-+优先级为1
 68     //*/优先级为2
 69     int p_top, p_input;
 70     if (top == ‘-‘ || top == ‘+‘)
 71     {
 72         p_top = 1;
 73     }
 74     if (top == ‘*‘ || top == ‘/‘)
 75     {
 76         p_top = 2;
 77     }
 78     if (Input == ‘-‘ || Input == ‘+‘)
 79     {
 80         p_input = 1;
 81     }
 82     if (Input == ‘*‘ || Input == ‘/‘)
 83     {
 84         p_input = 2;
 85     }
 86
 87     if (p_input > p_top)
 88     {
 89         return TopLow;
 90     }
 91     if (p_input == p_top)
 92     {
 93         return Equal;
 94     }
 95     if (p_input < p_top)
 96     {
 97         return TopHigh;
 98     }
 99     return 0;
100 }
101
102 int Push_O(OPERATOR *S, SymType e)
103 {
104     //当超出当前栈的容量时进行扩容
105     //这里应该只有等于的情况
106     //而不会大于
107     if ((S->Top - S->Bottom) >= S->stacksize)
108     {
109         //realloc函数将开辟指定大小的储存空间
110         //并将原来的数据全部移到这个新的储存空间
111         S->Bottom = (SymType*)realloc(S->Bottom, (S->stacksize + STACKINCREMENT)*sizeof(SymType));
112         if (!S->Bottom)
113         {
114             return 0;
115         }
116         //由于重新开辟了空间
117         //需要重新根据bottom指针的位置指定top
118         S->Top = S->Bottom + S->stacksize;
119         //最后增加当前栈容量
120         S->stacksize += STACKINCREMENT;
121     }
122     //再把入栈的数据存放好
123     *(S->Top++) = e;
124     return 1;
125 }
126
127 SymType Pop_O(OPERATOR *S)
128 {
129     if (S->Bottom == S->Top)
130     {
131         return 0;
132     }
133     //top指针先减1再取值
134     return *(--S->Top);
135 }
136
137 //以上为符号栈的函数
138 //========================================================================
139 //以下为数字栈的函数
140
141 int InitStack_N(NUMBER *S)
142 {
143     //创建出设定长度的顺序表,地址赋给bottom指针
144     S->Bottom = (NumType*)malloc(STACK_INIT_SIZE*sizeof(NumType));
145     if (!S->Bottom)
146     {
147         return 0;
148     }
149     S->stacksize = STACK_INIT_SIZE;
150     S->Top = S->Bottom;
151     return 1;
152 }
153
154 int DestroyStack_N(NUMBER *S)
155 {
156     S->Top = NULL;
157     free(S->Bottom);
158     S->Bottom = NULL;
159     return 1;
160 }
161
162 NumType GetTop_N(NUMBER S)
163 {
164     if (S.Bottom != S.Top)
165     {
166         //由于top指向的是最顶上元素的下一个位置
167         //所以取出最顶上元素的时候
168         //要把top减去1
169         return *(S.Top - 1);
170     }
171     return 0;
172 }
173
174 int Push_N(NUMBER *S, NumType e)
175 {
176     //当超出当前栈的容量时进行扩容
177     //这里应该只有等于的情况
178     //而不会大于
179     if ((S->Top - S->Bottom) >= S->stacksize)
180     {
181         //realloc函数将开辟指定大小的储存空间
182         //并将原来的数据全部移到这个新的储存空间
183         S->Bottom = (NumType*)realloc(S->Bottom, (S->stacksize + STACKINCREMENT)*sizeof(NumType));
184         if (!S->Bottom)
185         {
186             return 0;
187         }
188         //由于重新开辟了空间
189         //需要重新根据bottom指针的位置指定top
190         S->Top = S->Bottom + S->stacksize;
191         //最后增加当前栈容量
192         S->stacksize += STACKINCREMENT;
193     }
194     //再把入栈的数据存放好
195     *(S->Top++) = e;
196     return 1;
197 }
198
199 NumType Pop_N(NUMBER * S)
200 {
201     if (S->Bottom == S->Top)
202     {
203         return 0;
204     }
205     //top指针先减1再取值
206     return *(--S->Top);
207 }
208
209 void DoTheMath(NUMBER *S,char sym)
210 {
211     //从栈顶顺序读入两个数字
212     //进行运算
213     //并将结果压回栈内
214     float num1, num2;
215     num2 = Pop_N(S);
216     num1 = Pop_N(S);
217     switch (sym)
218     {
219     case ‘+‘:
220         Push_N(S, num1 + num2);
221         break;
222     case ‘-‘:
223         Push_N(S, num1 - num2);
224         break;
225     case ‘*‘:
226         Push_N(S, num1*num2);
227         break;
228     case ‘/‘:
229         Push_N(S, num1 / num2);
230         break;
231     default:
232         break;
233     }
234 }

Stack.c

StringToNumAndSym.h & StringToNumAndSym.c

这个文件的功能是。。。。

将输入的一大串字符串拆分成一个数字或一个运算符,并将他们按顺序储存到一个链表里。

用于进行第1步。

 1 ////////////////////////////////
 2 //StringToNumAndSym.h//
 3 ///////////////////////////////
 4
 5 #ifndef _STRINGTONUMANDSYM_H_
 6 #define _STRINGTONUMANDSYM_H_
 7
 8 #include<stdlib.h>
 9
10 //用于储存输入的数字或者符号
11 //IsNum=1 将读取数字
12 //IsNum=0 将读取字符
13 struct data_node
14 {
15     int IsNum;
16     float Num;
17     char Sym;
18     struct data_node *next;
19 };
20 typedef struct data_node EQUATION;
21
22 //该函数接受输入的字符串
23 //输出一串处理好的等式的指针
24 //用于表示数字或者运算符号加减乘除等等
25 void StringToEquation(char *,EQUATION**);
26
27 void DestroyEquation(EQUATION*);
28
29 #endif

StringToNumAndSym.h

  1 /////////////////////////////////
  2 // StringToNumAndSym.c //
  3 ///////////////////////////////
  4
  5 #include"StringToNumAndSym.h"
  6
  7 void StringToEquation(char *input, EQUATION **ptr_source)
  8 {
  9     EQUATION *ptr = NULL;
 10     int lop;
 11     //记录正数部分的temp
 12     int Ptmp;
 13     //记录小数部分的temp
 14     float Dtmp;
 15     //PNum记录正数的位数
 16     //NNum记录小数的个数
 17     float Dmulti;
 18     int Pmulti;
 19
 20     for (lop = 0; input[lop] != 0; lop++)
 21     {
 22         //接下来有个数字
 23         //除错,确保输入合法
 24         if (input[lop] >= ‘0‘ && input[lop] <= ‘9‘
 25             || input[lop] == ‘+‘ || input[lop] == ‘-‘ || input[lop] == ‘/‘ || input[lop] == ‘*‘
 26             || input[lop] == ‘(‘ || input[lop] == ‘)‘ || input[lop] == ‘.‘)
 27         {
 28             if (input[lop] >= ‘0‘ && input[lop] <= ‘9‘)
 29             {
 30                 Ptmp = 0;
 31                 Dtmp = 0;
 32                 Pmulti = 1;
 33                 Dmulti = 1;
 34
 35                 //计算整数部分
 36                 for (; input[lop] >= ‘0‘ && input[lop] <= ‘9‘; lop++)
 37                 {
 38                     Ptmp = Ptmp * 10 + (input[lop] - ‘0‘);
 39                 }
 40
 41                 //如果有小数
 42                 //计算小数部分
 43                 if (input[lop] == ‘.‘)
 44                 {
 45                     Dmulti = (float)0.1;
 46                     //计算出小数部分
 47                     for (lop += 1; input[lop] >= ‘0‘ && input[lop] <= ‘9‘; lop++)
 48                     {
 49                         Dtmp += Dmulti*(float)(input[lop] - ‘0‘);
 50                         Dmulti *= (float)0.1;
 51                     }
 52                 }
 53                 //得到数字了
 54                 //创建节点保存
 55                 if (ptr == NULL)
 56                 {
 57                     ptr = (EQUATION*)malloc(sizeof(EQUATION));
 58                     //创建第一个节点之后
 59                     //由于传入的是指针的指针
 60                     //需要更改ptr_source的值
 61                     //保存头指针
 62                     *ptr_source = ptr;
 63                 }
 64                 else
 65                 {
 66                     ptr->next = (EQUATION*)malloc(sizeof(EQUATION));
 67                     ptr = ptr->next;
 68                 }
 69                 ptr->next = NULL;
 70                 ptr->IsNum = 1;
 71                 ptr->Num = (float)Ptmp + Dtmp;
 72                 ptr->Sym = ‘0‘;
 73
 74                 lop--;
 75             }
 76             else
 77             {
 78                 //第一个节点
 79                 if (ptr == NULL)
 80                 {
 81                     ptr = (EQUATION*)malloc(sizeof(EQUATION));
 82                     *ptr_source = ptr;
 83                 }
 84                 //之后的节点
 85                 else
 86                 {
 87                     ptr->next = (EQUATION*)malloc(sizeof(EQUATION));
 88                     ptr = ptr->next;
 89                 }
 90                 ptr->next = NULL;
 91                 ptr->IsNum = 0;
 92                 ptr->Num = 0;
 93                 ptr->Sym = input[lop];
 94             }
 95         }
 96     }
 97 }
 98
 99 /*
100 //反转整数
101 int Invert(int input)
102 {
103     int output = 0;
104     while (input > 0)
105     {
106         output = output * 10 + input % 10;
107         input /= 10;
108     }
109     return output;
110 }
111 */
112
113 void DestroyEquation(EQUATION *N)
114 {
115     EQUATION *temp = NULL;
116     if (N != NULL)
117     {
118         temp = N;
119         N = N->next;
120         free(temp);
121         temp = NULL;
122
123         DestroyEquation(N);
124     }
125 }

StringToNumAndSym.c

main.c

主函数在此,实现了判断运算符运算顺序的逻辑

  1 /////////////
  2 // main.c //
  3 /////////////
  4
  5 #include<stdio.h>
  6
  7 #include"Stack.h"
  8 #include"StringToNumAndSym.h"
  9
 10 int main()
 11 {
 12     int lop = 0;
 13
 14     //新建用于储存输入数据的字符串
 15     char string[30] = { 0 };
 16     //新建用于储存处理后数据的字符串
 17     EQUATION *Eqinput;
 18
 19     //以下4行创建并初始化两个栈
 20     //分别用于存放运算符和数字
 21     OPERATOR Stack_Ope;
 22     NUMBER Stack_Num;
 23
 24     InitStack_O(&Stack_Ope);
 25     InitStack_N(&Stack_Num);
 26
 27     //输入合法的中缀表达式
 28     //不要太长
 29     printf("请输入合法的中缀表达式:\n");
 30     scanf("%s", string);
 31
 32     //将输入的字符串转化为能够处理的数据
 33     StringToEquation(string, &Eqinput);
 34
 35     //将中缀表达式转换为后缀表达式
 36     //同时包含对数据的运算
 37
 38     //如果输入的数据还没有处理完
 39     while (Eqinput != NULL)
 40     {
 41         //看看当前对象是不是数字
 42         if (Eqinput->IsNum == 1)
 43         {
 44             //是数字就直接丢到处理数字的栈里去
 45             Push_N(&Stack_Num, Eqinput->Num);
 46         }
 47         else
 48         {
 49             if (StackEmpty_O(Stack_Ope))
 50             {
 51                 Push_O(&Stack_Ope, Eqinput->Sym);
 52             }
 53
 54             //是运算符的话
 55             //将他与上一个运算符,也就是栈顶的那个运算符进行比较
 56             //然后根据两者的优先级大小来做出相应操作
 57             else
 58             {
 59                 //优先级判断
 60                 switch (TopIsPriority_O(Stack_Ope,Eqinput->Sym,GetTop_O(Stack_Ope)))
 61                 {
 62                     //如果栈顶是括号
 63                     //不进行优先级的比较
 64                     //直接入栈
 65                 case TopIsBrace:
 66                     Push_O(&Stack_Ope, Eqinput->Sym);
 67                     break;
 68
 69                     //输入了左括号
 70                 case InputIsBrace_1:
 71                     Push_O(&Stack_Ope, ‘(‘);
 72                     break;
 73
 74                     //输入了右括号
 75                     //一直出栈
 76                     //直到遇到左括号
 77                 case InputIsBrace_2:
 78                     while((GetTop_O(Stack_Ope) != ‘(‘))
 79                     {
 80                         //printf("%c ", Pop_O(&Stack_Ope));
 81                         DoTheMath(&Stack_Num, Pop_O(&Stack_Ope));
 82                     }
 83                     Pop_O(&Stack_Ope);
 84                     break;
 85
 86                     //当前运算符优先级比栈顶的低或者相等
 87                 case TopHigh:
 88                 case Equal:
 89                     while (!StackEmpty_O(Stack_Ope))
 90                     {
 91                         //printf("%c ", Pop_O(&Stack_Ope));
 92                         DoTheMath(&Stack_Num, Pop_O(&Stack_Ope));
 93                     }
 94                     Push_O(&Stack_Ope, Eqinput->Sym);
 95                     break;
 96
 97                     //当前运算符优先级比栈顶的高
 98                 case TopLow:
 99                     Push_O(&Stack_Ope, Eqinput->Sym);
100                     break;
101                 default:
102                     break;
103                 }
104             }
105         }
106         //读入下一个数据
107         Eqinput = Eqinput->next;
108     }
109
110     //如果栈里还有残留的运算符
111     //全部出栈
112     while (!StackEmpty_O(Stack_Ope))
113     {
114         //printf("%c ", Pop_O(&Stack_Ope));
115         DoTheMath(&Stack_Num, Pop_O(&Stack_Ope));
116     }
117
118     printf("\n");
119
120     //////////////
121     //运算完毕//
122     /////////////
123
124     printf("%s = %.3f\n", string, GetTop_N(Stack_Num));
125
126     //销毁输入的式子
127     DestroyEquation(Eqinput);
128     Eqinput = NULL;
129
130     DestroyStack_N(&Stack_Num);
131     DestroyStack_O(&Stack_Ope);
132
133     return 0;
134 }

main.c

时间: 2024-08-19 04:05:11

数据结构应用实例#栈&单链表#简易计算器的相关文章

数据结构上机测试2-1:单链表操作A (顺序建表+关键字删除)

数据结构上机测试2-1:单链表操作A Time Limit: 1000MS Memory limit: 4096K 题目描述 输入n个整数,先按照数据输入的顺序建立一个带头结点的单链表,再输入一个数据m,将单链表中的值为m的结点全部删除.分别输出建立的初始单链表和完成删除后的单链表. 输入 第一行输入数据个数n: 第二行依次输入n个整数: 第三行输入欲删除数据m. 输出 第一行输出原始单链表的长度: 第二行依次输出原始单链表的数据: 第三行输出完成删除后的单链表长度: 第四行依次输出完成删除后的

数据结构之 线性表---单链表操作A (删除链表中的指定元素)

数据结构上机测试2-1:单链表操作A Time Limit: 1000MS Memory limit: 4096K 题目描述 输入n个整数,先按照数据输入的顺序建立一个带头结点的单链表,再输入一个数据m,将单链表中的值为m的结点全部删除.分别输出建立的初始单链表和完成删除后的单链表. 输入 第一行输入数据个数n: 第二行依次输入n个整数: 第三行输入欲删除数据m. 输出 第一行输出原始单链表的长度: 第二行依次输出原始单链表的数据: 第三行输出完成删除后的单链表长度: 第四行依次输出完成删除后的

数据结构之 线性表---单链表的操作B(先逆序+再删除重复元素)

数据结构上机测试2-2:单链表操作B Time Limit: 1000MS Memory limit: 65536K 题目描述 按照数据输入的相反顺序(逆位序)建立一个单链表,并将单链表中重复的元素删除(值相同的元素只保留最后输入的一个). 输入 第一行输入元素个数n: 第二行输入n个整数. 输出 第一行输出初始链表元素个数: 第二行输出按照逆位序所建立的初始链表: 第三行输出删除重复元素后的单链表元素个数: 第四行输出删除重复元素后的单链表. 示例输入 10 21 30 14 55 32 63

Java数据结构-线性表之单链表LinkedList

线性表的链式存储结构,也称之为链式表,链表:链表的存储单元可以连续也可以不连续. 链表中的节点包含数据域和指针域,数据域为存储数据元素信息的域,指针域为存储直接后继位置(一般称为指针)的域. 注意一个头结点和头指针的区别: 头指针: 指向链表的第一个节点的指针,若链表有头结点,则是指向头结点的指针: 头指针具有标识作用,所以常用头指针作为链表的名字: 不论链表是否为空,头指针都不为空: 是链表的必要元素. 头结点: 头结点是为了操作的统一和方便而设立的,放在第一个元素节点的前面,其数据域一般无意

数据结构之 线性表---单链表的拆分

数据结构实验之链表五:单链表的拆分 Time Limit: 1000MS Memory limit: 65536K 题目描述 输入N个整数顺序建立一个单链表,将该单链表拆分成两个子链表,第一个子链表存放了所有的偶数,第二个子链表存放了所有的奇数.两个子链表中数据的相对次序与原链表一致. 输入 第一行输入整数N;: 第二行依次输入N个整数. 输出 第一行分别输出偶数链表与奇数链表的元素个数: 第二行依次输出偶数子链表的所有数据: 第三行依次输出奇数子链表的所有数据. 示例输入 10 1 3 22

大话数据结构之php实现单链表

最近想起来两件事1.大话数据结构和大话设计模式 这两本书很有意思,C语言有指针,所以实现起来容易理解,所以突然想到用PHP写一下来熟悉一下数据结构的线性表,不过看的比较慢.一般两三天才看完一部分,毕竟还要工作,老板还安装摄像头看着每天干了啥.....老板事业兴隆,嘻嘻. 线性表的概念不赘述,直接去看大话数据结构,代码也是在参考众多实现方案,比较符合大话数据结构的原本思想,就是基本上还原C语言的实现过程. 直接上代码 线性表 <?php /*  *文件名:linearList.php   * 功能

数据结构----顺序表与单链表(JAVA)

下面为学习顺序表和单链表的一些基本操作函数: 1 public class SeqList<T> extends Object { 2 protected int n; 3 protected Object[] element; 4 5 public SeqList(int length) { 6 this.element = new Object[length]; 7 this.n = 0; 8 } 9 10 public SeqList() { 11 this(64); 12 } 13 1

数据结构复习--java实现单链表基本操作

单链表的基本操作包括建立单链表.查找运算(按序查找和按值查找).插入运算(前插和后插)和删除运算.下面给出具体的java实现程序: package com.zpp.test; //首先创建一个节点类 public class Node { private Node next; //指针域 private int data;//数据域 public Node( int data) { this. data = data; } } package com.zpp.test; public class

【c++版数据结构】之循环单链表的实现(带头结点以及尾节点)

所实现的循环单链表的结构如下图所示: 循环单链表的实现,和上一篇文章单链表的实现大致相同点击打开链接,略有区别: 1:循环判断的条件不再是s == NULL或者s->next == NULL,而是他们是否等于头指针.2: 断开链表时的处理,尾节点的next不是NULL,而是指向头结点 具体细节参考上一篇文章 头文件:SCList.h #ifndef SCLIST_H #define SCLIST_H #include<iostream> #include<cassert> u