把字符串s转换为相应的双精度浮点数
#include <ctype.h>
#define MAXLINE 100
double atof(char s[]);
/*把字符串s转换为相应的双精度浮点数*/
double atof(char s[]){
double val, power;
int i,sign;
for(i=0;isspace(s[i]);i++); /*跳过空白符*/
sign=(s[i]=='-')?-1:1;
if(s[i]=='+'||s[i]=='-') i++;
for(val=0.0;isdigit(s[i]);i++)
val=val*10+(s[i]-'0');
if(s[i]=='.')
i++;
for(power=1.0;isdigit(s[i]);i++){
val=val*10+(s[i]-'0');
power=power*10;
}
return sign*val/power;
}
练习4-3
主要函数 : int getop() //获取下一个运算符或者操作数
void push(double) 将数压入栈中
double pop 获得栈中的值
getch 取一个字符
ungetch 将字符压回到栈中
为什么需要getch() 和ungetch()? 程序中需要超前读入一些输入。 读入一些字符合成一个数字:在看到第一个非数字字符之前,已经读入的数是否完整是不能确定的,所以要超前读入一个字符,这样就导致有一个字符不属于当前所要读入的数。
/*
4.3 在计算器中加入取模%功能,注意考虑负数的情况
*/
#include <stdio.h>
#include <stdlib.h> //for atof函数
#include <math.h>
#define MAXOP 100
#define NUMBER '0'
int getop(char []);
void push(double);
double pop(void);
int main()
{
int type;
double op2;
char s[MAXOP];
while((type = getop(s))!='EOF')
{
switch (type)
{
//是数则压入栈
case NUMBER:
push(atof(s));
break;
//是操作符则取出操作需要数目的数,运算后将结果压入栈
case '+':
push(pop()+pop());
break;
case '-':
op2 = pop();
push(pop()-op2);
break;
case '*':
push(pop()*pop());
break;
case '/':
op2 = pop();
if (op2!=0.0)
push(pop()/op2);
else
printf("error:zero divisor.\n");
break;
case '%':
op2 = pop();
if (op2!=0.0)
push(fmod(pop(),op2));
else
printf("error:zero divisor.\n");
case '\n':
printf("计算结果:%.8g\n",pop());
break;
default:
printf("error:unknown command.\n");
break;
}
}
return 0;
}
#define MAXVAL 100
int sp = 0;
double val[MAXVAL];
//push操作 把值压入堆栈
void push(double f)
{
if (sp<MAXVAL)
{
val[sp++] = f;
}
else
printf("error: stack full, can't push %g\n",f);
}
//pop操作 从堆栈中弹出值
double pop(void)
{
if (sp>0)
{
return val[--sp];
}
else
{
printf("error: stack empty\n");
return 0.0;
}
}
#include <ctype.h>
int getch(void);
void ungetch(int);
//getop:获取下一个操作数或运算符
int getop(char s[])
{
int i,c;
//跳过空格和制表符
while((s[0] = c =getch()) == ' '||c == '\t')
;
s[1]='\0';
//不是数字,返回
if (!(isdigit(c)) && c!='.' && c!='-')
return c;
i = 0;
//有可能是负号,也有可能是减号
if (c == '-')
{
//如果是负号
if (isdigit(c = getch())||c == '.')
{
s[++i] = c;
}
//如果是减号
else
{
if (c!=EOF)
ungetch(c);
return '-';
}
}
//收集整数部分
if(isdigit(c))
while(isdigit(s[++i]= c= getch()))
;
//收集小数部分
if(c == '.')
while(isdigit(s[++i]= c= getch()))
;
s[i] = '\0';
if (c!=EOF)
{
ungetch(c);
}
return NUMBER;
}
#define BUFSIZE 100
char buf[BUFSIZE];
int bufp = 0;
//缓冲区不为空则getch从缓冲区读出字符,否则用getchar函数直接从输入读取
int getch(void)
{
return(bufp>0)?buf[--bufp]:getchar();
}
//把要压回的字符放到缓冲区
void ungetch(int c)
{
if (bufp>=BUFSIZE)
{
printf("ungetch:too many characters\n");
}
else
buf[bufp++] = c;
}
4.4 作用域规则
名字的作用域指的是 程序中可以使用改名字的部分。
对于在函数开头声明的自动变量来说,其作用域是声明该变量名的函数。 不同函数中声明的具有相同名字的各个局部变量之间没有任何关系。函数的参数也是这样的,可以看作是局部变量。
外部变量或函数的作用域 从声明它的地方开始,到其所在的(待编译的)文件的末尾结束。
例如:
main(){...}
int sp=0;
double val[MAXVAL];
void push(double f){...}
double pop(void){...}
push和pop可以不进行任何声明就能访问sp,val 但是这两个变量名不能用在main中,push函数和pop函数也不能用在main函数中。
另一方面,如果要在外部变量的定义之前使用该变量, 或者 外部变量的定义与使用不再同一个源文件中, 则必须在相应的变量声明中强制性地使用关键字extern
将外部变量的声明与定义严格区分开来很重要。变量声明用于说明变量的属性, 而变量定义除此以外还将引起存储器的分配。
在一个源程序的所有源文件中,一个外部变量只能在某个文件中定义一次,其他文件通过extern声明来访问它。
外部变量的定义中必须指定数组的长度,但extern声明则不一定要指定数组的长度。
4.6 静态变量
见 http://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777441.html
4.7 寄存器变量
register声明告诉编译器,它所声明的变量在程序中使用频率较高。但每个函数只有很少的变量可以保存在寄存器中。
4.9 初始化
在不进行显示初始化的情况下,静态变量和外部变量将被初始化为0,而自动变量和寄存器变量的初值则没有定义。
4.11 c预处理器
4.11.2 宏替换
可以通过#undef指令取消名字的宏定义 这样做可以保证后续的调用是函数调用,而不是宏调用。
形式参数不能用带引号的字符串替换。但是,如果在替换文本中,参数名以#作为前缀则结果将被扩展为由实际参数替换该参数的带引号的字符串。
例如: #define dprint(expr) printf(#expr "=%g\n",expr)
使用语句 dprint(x/y);
调用该宏时,该宏将被扩展为 printf("x/y""=%g\n",x/y); 这样就相当于 printf("x/y=%g\n",x/y);
预处理器运算符##为宏扩展停工了一种连接实际参数的手段。 如果替换文本中的参数与##相邻,则该参数将被实际参数替换,##与前后的空白将被删除,并对替换后的结果重新扫描。
#define paste(front.back) front ##back 那么 paste(name,1) 的结果将建立记号name1
4.11.3 条件包含
可以使用条件语句对预处理本身进行控制,这种条件语句的值是在预处理执行的过程中进行计算。
这种方式为在编译过程中根据计算所得的条件值选择性地包含不同代码提供了一种手段。
#if对其中的常量整型表达式(不能包含sizeof、类型转换运算符或enum)进行求值,若果表达式的值不等于0,则包含其后的各行,
直到遇到#endif #elif 或#else位置
#if !defined(HDR)
#define HDR
.............
#endif