简易计算器
11 14, 2008 Linux
随便写的一个计算算术表达式的程序,支持正数,+,-,*/,()运算符。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Maxsize 81
/* 运算符栈 */
struct stack_oper
{
char oper[Maxsize];
int top;
}operstack;
/* 数据栈 */
struct stack_data
{
float data[Maxsize];
int top;
}datastack;
char pop_stack_oper(struct stack_oper *p_operstack)
{
char temp;
if (p_operstack->top >= 0)
{
temp = (p_operstack -> oper)[p_operstack->top];
}
else
{
fprintf(stderr, “Operator stack is empty…\n”);
return ‘+’; /* 如果运算符栈为空,默认返回’+’ */
}
–(p_operstack->top);
return temp;
}
void push_stack_oper(struct stack_oper *p_operstack, char oper)
{
++(p_operstack->top);
(p_operstack -> oper)[p_operstack->top] = oper;
}
float pop_stack_data(struct stack_data *p_datastack)
{
float temp;
if (p_datastack->top >= 0)
{
temp = (p_datastack -> data)[p_datastack->top];
}
else
{
fprintf(stderr, “Data stack is empty…\n”);
return 0.0; /* 如果数据栈为空,默认返回0.0 */
}
–(p_datastack->top);
return temp;
}
void push_stack_data(struct stack_data *p_datastack, float data)
{
++(p_datastack->top);
(p_datastack -> data)[p_datastack->top] = data;
}
float cal_part(float data[], char oper[], int data_index, int oper_index)
{
float result = 0.0;
int i, j, m, n;
i = data_index-1;
j = oper_index-1;
//result = data[i];
/* debug */
/*
{
int t1;
for (t1=0; t1<data_index; ++t1)
printf(”%.2f “, data[t1]);
printf(”\n”);
}
*/
/* 先处理’*'跟’/',因为优先级更高 */
–i;
for ( ; j >= 0; –j)
{
switch (oper[j])
{
case ‘*’:
data[i+1] = data[i+1] * data[i];
for (m = i + 1; m < data_index; ++m) /* 左移后面那段数据 */
{
data[m-1] = data[m];
}
–data_index;
for (n = j + 1; n < oper_index; ++n) /* 左移后面那些操作符 */
{
oper[n-1] = oper[n];
}
–oper_index;
break;
case ‘/’:
if (data[i] != 0.0)
{
data[i+1] = data[i+1] / data[i];
}
else
{
fprintf(stderr, “Devide 0…program quit!\n”);
exit(1);
}
for (m = i + 1; m < data_index; ++m) /* 左移后面那段数据 */
{
data[m-1] = data[m];
}
–data_index;
for (n = j + 1; n < oper_index; ++n) /* 左移后面那些操作符 */
{
oper[n-1] = oper[n];
}
–oper_index;
break;
default:
break;
}
–i;
}
i = data_index-1;
j = oper_index-1;
/* 再处理’+'跟’-’ */
result = data[i];
–i;
for ( ; j >= 0; –j)
{
switch (oper[j])
{
case ‘+’:
result += data[i];
break;
case ‘-’:
result -= data[i];
break;
default:
break;
}
–i;
}
return result;
}
float compvalue(char exp[])
{
char part_oper[20] = {0}; /* 存放运算符数组 */
float part_data[21] = {0.0};/* 存放浮点数数组 */
char p_data[16] = {0}; /* 存放浮点数字符串 */
char *p_exp = NULL; /* 指向表达式某个字符 */
float data = 0.0; /* 存放转换后的浮点数 */
float part_result = 0.0; /* 存放结果值 */
int sdata_index = 0; /* 浮点数字符串指示器 */
int oper_index = 0; /* 运算符数组指示器 */
int data_index = 0; /* 浮点数组指示器 */
char temp = 0; /* 临时存放出栈字符 */
p_exp = exp;
while (*p_exp != 0)
{
switch (*p_exp)
{
case ‘ ‘: /* 如果是空格,什么都不做 */
break;
case ‘(’:
push_stack_oper(&operstack, *p_exp);
break;
case ‘*’:
push_stack_oper(&operstack, *p_exp);
break;
case ‘+’:
push_stack_oper(&operstack, *p_exp);
break;
case ‘-’:
push_stack_oper(&operstack, *p_exp);
break;
case ‘/’:
push_stack_oper(&operstack, *p_exp);
break;
case ‘)’: /* 此时计算该括号内的表达式的值 */
part_data[data_index] = pop_stack_data(&datastack);
++data_index;
//temp = pop_stack_oper(&operstack);
while ( (temp=pop_stack_oper(&operstack)) != ‘(’)
{
part_oper[oper_index] = temp;
++oper_index;
part_data[data_index] = pop_stack_data(&datastack);
++data_index;
}
part_result = cal_part(part_data, part_oper, data_index, oper_index);
//printf(”part result:%.2f\n”, part_result); /* debug */
push_stack_data(&datastack, part_result);
/* reset */
memset(part_oper, 0, sizeof(part_oper));
memset(part_data, 0, sizeof(part_data));
data_index = 0;
oper_index = 0;
part_result = 0.0;
/* debug */
/*
{
int h;
for (h = 0; h <= datastack.top; ++h)
printf(”%.2f “, datastack.data[h]);
printf(”\n”);
for (h = 0; h <= operstack.top; ++h)
printf(”%c “, operstack.oper[h]);
printf(”\n”);
}
*/
break;
default:
p_data[sdata_index] = *p_exp;
if ( (*(p_exp+1) == ‘(’) || (*(p_exp+1) == ‘*’) || (*(p_exp+1) == ‘+’) ||
(*(p_exp+1) == ‘-’) || (*(p_exp+1) == ‘/’) || (*(p_exp+1) == ‘)’) ||
(*(p_exp+1) == 0) )
{
p_data[sdata_index+1] = 0;
data = atof(p_data); /* 把字符串转换成浮点类型 */
push_stack_data(&datastack, data); /* 该浮点数进栈 */
memset(p_data, 0, sizeof(p_data)); /* reset */
sdata_index = 0; /* reset */
}
else
++sdata_index;
break;
}
++p_exp; /* 字符指针后移,助于遍历 */
}
/* 先判断栈是否为空 */
if (operstack.top < 0) /* 运算符栈为空说明运算完毕,只要弹出数据栈中的数据(也就是结果) */
{
return pop_stack_data(&datastack);
}
else /* 计算 */
{
/*
* 所有数据跟运算符出栈
*/
part_data[data_index] = pop_stack_data(&datastack);
++data_index;
while ( operstack.top >= 0) /* 保证所有的运算符都出栈 */
{
part_oper[oper_index] = pop_stack_oper(&operstack);
++oper_index;
part_data[data_index] = pop_stack_data(&datastack);
++data_index;
}
return cal_part(part_data, part_oper, data_index, oper_index);
}
}
int main(void)
{
char exp[81] = {0};
memset(&operstack, 0, sizeof(operstack));
memset(&datastack, 0, sizeof(datastack));
operstack.top = -1;
datastack.top = -1;
printf(”请输入算术表达式:\n”);
scanf(”%s”, exp);
exp[strlen(exp)] = 0;
printf(”%s计算结果:%.2f\n”, exp, compvalue(exp) );
return 0;
}
函数指针浅谈
11 14, 2008 Linux
函数存放在内存的代码区域内,它们同样有地址。如果我们有一个int test(int a)的函数,那么,它的地址就是函数的名字,这一点如同数组一样,数组的名字就是数组的起始地址。
定义一个指向函数的指针用如下的形式,以上面的test()为例: int (*fp)(int a);//这里就定义了一个指向函数的指针
函数指针不能绝对不能指向不同类型,或者是带不同形参的函数,在定义函数指针的时候我们很容易犯如下的错误。int *fp(int a);//这里是错误的,因为按照结合性和优先级来看就是先和()结合,然后变成了一个返回整形指针的函数了,而不是函数指针,这一点尤其需要注意!
#include <stdio.h>
#include <stdlib.h>
typedef int (*FUNCTION)(int);
int f1(int k)
{
return k;
}
int f2(int j)
{
return j;
}
int f3(int i)
{
return i;
}
int (*foo1(void))[]
{
static int sdata[] = {1, 2, 3};
return sdata;
}
FUNCTION (*foo2(void))[]
{
static FUNCTION sfunc[] = {f1, f2, f3};
return sfunc;
}
int main(void)
{
FUNCTION *pfunc = foo2();
int *p = foo1();
int i = 0;
for ( ; i < 3; ++i)
printf(”%d\n”, p[i]);
for (i=0; i<3; ++i)
printf(”%d\n”, pfunc[i](100));
return 0;
}
利用typedef定义可以简化函数指针的定义,上述示例中typedef int (*FUNCTION)(int);,如果不用这个定义,则需要这样写:int (*((*foo3(void))(int)))[],这样看起来比较吃力。
指针数组元素所保存的只是一个内存地址,既然只是个内存地址就不可能进行a[0]()这样地址带括号的操作,而函数指针不同它是一个例外,函数指针只所以这么叫它就是因为它是指向函数指向内存的代码区的指针,它被系统授予允许与()括号操作的权利,进行间接的函数调用,既然函数指针允许这么操作,那么被定义成函数指针的数组就一定是可以一样的操作的。
gcc编译选项介绍(转)
11 6, 2008 Linux
gcc编译选项介绍(转)
[介绍]
gcc and g++分别是gnu的c & c++编译器
gcc/g++在执行编译工作的时候,总共需要4步
1.预处理,生成.i的文件[预处理器cpp]
2.将预处理后的文件不转换成汇编语言,生成文件.s[编译器egcs]
3.有汇编变为目标代码(机器代码)生成.o的文件[汇编器as]
4.连接目标代码,生成可执行程序[链接器ld]
[参数详解]
-x language filename
设定文件所使用的语言,使后缀名无效,对以后的多个有效.也就是根据约定C语言的后 缀名称是.c的,而
C++的后缀名是.C或者.cpp,如果你很个性,决定你的C代码文件的后缀 名是.pig 哈哈,那你就要用这个
参数,这个参数对他后面的文件名都起作用,除非到了 下一个参数的使用。
可以使用的参数吗有下面的这些 `c’, `objective-c’, `c-header’, `c++’, `cpp-output’,
`assembler’, and `a ssembler-with-cpp’. 看到英文,应该可以理解的。
例子用法: gcc -x c hello.pig
-x none filename 关掉上一个选项,也就是让gcc根据文件名后缀,自动识别文件类型
例子用法: gcc -x c hello.pig -x none hello2.c
-c 只激活预处理,编译,和汇编,也就是他只把程序做成obj文件
例子用法: gcc -c hello.c 他将生成.o的obj文件
-S 只激活预处理和编译,就是指把文件编译成为汇编代码。
例子用法 gcc -S hello.c 他将生成.s的汇编代码,你可以用文本编辑器察看
-E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面.
例子用法: gcc -E hello.c > pianoapan.txt gcc -E hello.c | more 慢慢看吧,一个
hello word 也要与处理成800行的代码
-o 制定目标名称,缺省的时候,gcc 编译出来的文件是a.out,很难听,如果你和我有同感 ,改掉它,哈
哈
例子用法
gcc -o hello.exe hello.c (哦,windows用习惯了)
gcc -o hello.asm -S hello.c -pipe 使用管道代替编译中临时文件,在使用非gnu汇编工具的时候,
可能有些问题
gcc -pipe -o hello.exe hello.c -ansi 关闭gnu c中与ansi c不兼容的特性,激活ansi c的专有特
性(包括禁止一些asm inl ine typeof关键字,以及UNIX,vax等预处理宏,
-fno-asm 此选项实现ansi选项的功能的一部分,它禁止将asm,inline和typeof用作关键字。
-fno-strict-prototype 只对g++起作用,使用这个选项,g++将对不带参数的函数,都认为是没有显式
的对参数 的个数和类型说明,而不是没有参数. 而gcc无论是否使用这个参数,都将对没有带参数的函数,
认为城没有显式说明的类型
-fthis-is-varialble 就是向传统c++看齐,可以使用this当一般变量使用.
-fcond-mismatch 允许条件表达式的第二和第三参数类型不匹配,表达式的值将为void类型
-funsigned-char -fno-signed-char -fsigned-char -fno-unsigned-char
这四个参数是对char类型进行设置,决定将char类型设置成unsigned char(前两个参 数)或者 signed
char(后两个参数)
-include file 包含某个代码,简单来说,就是便以某个文件,需要另一个文件的时候,就可以用它设
定,功能就相当于在代码中使用#i nclude 例子用法: gcc hello.c -include /root/pianopan.h
-imacros file 将file文件的宏,扩展到gcc/g++的输入文件,宏定义本身并不出现在输入文件中
-Dmacro 相当于C语言中的#define macro
-Dmacro=defn 相当于C语言中的#define macro=defn
-Umacro 相当于C语言中的#undef macro -undef 取消对任何非标准宏的定义
-Idir 在你是用#i nclude”file”的时候,gcc/g++会先在当前目录查找你所制定的头文件,如 果没有找
到,他回到缺省的头文件目录找,如果使用-I制定了目录,他回先在你所制定的目录查找,然后再按常规的顺
序去找. 对于#i nclude,gcc/g++会到-I制定的目录查找,查找不到,然后将到系统的缺 省的头文件目录查
找
-I- 就是取消前一个参数的功能,所以一般在-Idir之后使用
-idirafter dir 在-I的目录里面查找失败,讲到这个目录里面查找.
-iprefix prefix -iwithprefix dir 一般一起使用,当-I的目录查找失败,会到prefix+dir下查找
-nostdinc 使编译器不再系统缺省的头文件目录里面找头文件,一般和-I联合使用,明确限定头 文件的位
置
-nostdin C++ 规定不在g++指定的标准路经中搜索,但仍在其他路径中搜索,.此选项在创libg++库 使用
-C 在预处理的时候,不删除注释信息,一般和-E使用,有时候分析程序,用这个很方便的
-M 生成文件关联的信息。包含目标文件所依赖的所有源代码你可以用gcc -M hello.c 来测试一下,很
简单。
-MM 和上面的那个一样,但是它将忽略由#i nclude造成的依赖关系。
-MD 和-M相同,但是输出将导入到.d的文件里面
-MMD 和-MM相同,但是输出将导入到.d的文件里面
-Wa,option 此选项传递option给汇编程序;如果option中间有逗号,就将option分成多个选项,然 后传
递给会汇编程序
-Wl.option 此选项传递option给连接程序;如果option中间有逗号,就将option分成多个选项,然 后
传递给会连接程序.
-llibrary 制定编译的时候使用的库 例子用法 gcc -lcurses hello.c 使用ncurses库编
译程序
-Ldir 制定编译的时候,搜索库的路径。比如你自己的库,可以用它制定目录,不然 编译器将只在
标准库的目录找。这个dir就是目录的名称。
-O0 -O1 -O2 -O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
-g 只是编译器,在编译的时候,产生调试信息。
-gstabs 此选项以stabs格式声称调试信息,但是不包括gdb调试信息.
-gstabs+ 此选项以stabs格式声称调试信息,并且包含仅供gdb使用的额外调试信息.
-ggdb 此选项将尽可能的生成gdb的可以使用的调试信息.
-static 此选项将禁止使用动态库,所以,编译出来的东西,一般都很大,也不需要什么 动态连接库
,就可以运行.
-share 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
-traditional 试图让编译器支持传统的C语言特性


