第一章很杂,介绍了c核心功能。没啥可总结的了。通过习题来可见一斑了。
来讨论一下10题遇到的问题: 回退符输入不了。几经查验,网路上找到这句 "scanf或gets吧它们都是文件流级的,你用getch试一试,它可以得到单个的字符。" ,by 这个帖子。
本来用的是getchar(),输入一行后再回车才会输出改动后的。
换成了getch()就不会有,直接tab就输出\t,backspace就输出\b,输入字符就输出字符。原因呢:
昨天一整天,今天半天,终于完了。这才第一章。感觉还好。
/*
1-3.修改温度转换程序,使之能在转换表的顶部打印一个标题 。
华氏温度0~300,步长=20
*/
#include <stdio.h>
int main(){
float fahr,celsius;
int upper,step;
fahr=0;
upper=300;
step=20;
printf("fahr\tcelsius\n");
while(fahr<=upper){
celsius = 5.0/9.0*(fahr-32.0);
printf("%3.0f\t%6.1f\n",fahr,celsius);
fahr+=step;
}
return 0;
}
/*
练习1-4 编写一个程序打印摄氏温度转换为华氏温度的转换表。
摄氏温度-40~+40 ,步长=2
*/
#include <stdio.h>
int main(){
float fahr,celsius;
int upper,step;
celsius=-40;
upper=40;
step=2;
printf("celsius\tfahr\n");
while(celsius<=upper){
fahr=(celsius*9.0)/5.0+32.0;
printf("%3.0f\t%6.1f\n",celsius,fahr);
celsius+=step;
}
return 0;
}
/*
1-5.修改温度转换程序,要求以逆序(300-0)打印温度转换表。
*/
#include <stdio.h>
#define LOWER 300
#define UPPER 0
#define STEP 20
int main(){
int fahr;
printf(" fahr\tcelsius\n");
for(fahr=LOWER;fahr>=UPPER;fahr-=STEP)
printf(" %3d\t%6.1f\n",fahr,(5.0/9.0)*(fahr-32));
return 0;
}
/*
1-6.验证表达式getchar()!=EOF,
1-7.编写一个打印EOF值的程序。
*/
#include <stdio.h>
int main(){
printf("%d\n",getchar()!=EOF);
/* //书上用的是!=优先级大于=优先级特性来写的
int c;
while(c = getchar()!=EOF)
printf("%d\n",c);
*/
printf("EOF = %d\n",EOF);
return 0;
}
/*
1-8.编写一个统计空格、制表符与换行符个数的程序。
*/
#include <stdio.h>
int main(){
int c,nb=0,nt=0,nl=0;
while((c=getchar()) != EOF){
//书上用的if else 检测
switch(c){
case ' ': ++nb;break;
case '\n':++nl;break;
case '\t':++nt;break;
}
}
printf("[ ]=%d\t[\\n]=%d\t[\\t]=%d\n",nb,nl,nt);
return 0;
}
/*
1-9.编写一个将输入复制到输出的程序,并将其中连续的多个空格用一个空格代替。
*/
#include <stdio.h>
int main(){
int c,lastc='a';
while((c=getchar())!=EOF){
if(c!=' '|| lastc!=' ') //漂亮阿-书上的。只有这个字符和前一个字符都为空格时,才会输出空格的字符
putchar(c);
lastc=c;
}
return 0;
}
/*
1-10.编写一个将输入复制到输出的程序,并将其中的制表符替换\t,回退符替换\b,反斜杠替换为\\,以可见的方式显示出来。
//书上还是用if else 实现,我就还用switch吧
遇到问题了,回退符输入不了。几经查验,网路上找到这句 "scanf或gets吧它们都是文件流级的,你用getch试一试,它可以得到单个的字符。" 。
*/
#include <stdio.h>
int main(){
int c;
while((c=getch())!=EOF){
switch(c){
case '\t': printf("\\t");break;
case '\b': printf("\\b");break;
case '\\': printf("\\\\");break;
default: putchar(c);
}
}
return 0;
}
来讨论一下10题遇到的问题: 回退符输入不了。几经查验,网路上找到这句 "scanf或gets吧它们都是文件流级的,你用getch试一试,它可以得到单个的字符。" ,by 这个帖子。
本来用的是getchar(),输入一行后再回车才会输出改动后的。
换成了getch()就不会有,直接tab就输出\t,backspace就输出\b,输入字符就输出字符。原因呢:
函数名: getchar
功 能: 从stdin流中读字符
用 法: int getchar(void);
函数名: getch
功 能: 从控制台无回显地取一个字符
用 法: int getch(void);
原因出来了,而且可以看到getchar确实是从流中读取。所以才会用到缓冲区。以至于\b无法输入。
p.s.要使用另一个函数getche()会比getch()多了个回显功能。
/*
1-11.(测试)单词计数程序
*/
#include <stdio.h>
#define IN 1
#define OUT 0
int main(){
int c,nl=0,nw=0,nc=0,state=OUT;
while((c=getchar())!=EOF){
++nc;
if(c=='\n') ++nl;
if(c==' ' || c=='\n'|| c=='\t') state=OUT;
else if(state==OUT){
state = IN;
++nw;
}
}
printf("%d行 %d单词 %d字符\n",nl,nw,nc);
return 0;
}
/*
1-12.编写程序,以每行一个单词的形式打印其输出。
*/
#include <stdio.h>
#define IN 1
#define OUT 0
int main(){
int c,state=OUT;
while((c=getchar())!=EOF){
if(c==' ' || c=='\n'|| c=='\t') state=OUT;
else if(state==OUT){
state = IN;
putchar('\n');
}
if(state==IN) putchar(c);
else ;
}
//printf("%d行 %d单词 %d字符\n",nl,nw,nc);
return 0;
}
/*
1-13.编写一个函数,打印输入中单词长度的直方图。 水平方向。
底下注释是书里面的 , 第一次看题目,没看懂,敲了遍书上的,知道了。
然后自己写了(修改自1.11单次统计程序)。不过貌似现在条形图的长度是用单词个数来显示的。不好,优化下。
最后, 绘制借鉴了 书上的算法。
*/
#include <stdio.h>
#define IN 1
#define OUT 0
#define MAX 11
int main(){
int c,nl=0,nw=0,nc=0,state=OUT; //nc改用记录每个单词长度。 nl记录长度大于10字符的个数。nw依旧是单词数
int ndigit[MAX] ;
int i;
for(i=0;i<MAX;++i) ndigit[i]=0;
while((c=getchar())!=EOF){
++nc;
if(c==' ' || c=='\n'|| c=='\t') state=OUT;
else if(state==OUT){
state = IN;
if(nc>10 ) nl++;
else ndigit[nc]++;
nc=0;
++nw;
}
}
int max=0,len;
for(i=1;i<MAX;++i)
if(ndigit[i]>max)
max=ndigit[i];
printf("总单词:%d\n 长度 个数",nw);
for(i=1;i<MAX;++i){
printf("\n%5d - %5d : ",i,ndigit[i]);
if(ndigit[i] > 0) {
if((len = ndigit[i] * MAX / max)<=0) len=1; //MAX在这里用处是 直方图的最大长度。(数组最大长度和直方图最大长度都是这个数。)
}
else
len=0;
while(len--) putchar('*');
}
printf("\n>%4d - %5d.",10,nl);
return 0;
}
/*
#include <stdio.h>
#define MAXHIST 15
#define MAXWORD 11
#define IN 1
#define OUT 0
int main(){
int c,i,nc,state;
int len;
int maxvalue;
int ovflow;
int wl[MAXWORD];
state=OUT;
nc=0;
ovflow=0;
for(i=0;i<MAXWORD;++i) wl[i]=0;
while((c=getchar())!=EOF){
if(c==' ' || c=='\n'||c=='\t'){
state = OUT;
if(nc>0)
if(nc<MAXWORD)
++wl[nc];
else
++ovflow;
nc=0;
}else if(state == OUT){
state = IN;
nc=1;
}else
++nc;
}
maxvalue=0;
for(i=1;i<MAXWORD;++i){
if(wl[i] > maxvalue)
maxvalue = wl[i];
}
for(i=1;i<MAXWORD;++i){
printf("%5d - %5d : ",i,wl[i]);
if(wl[i] > 0){
if((len =wl[i]*MAXHIST/maxvalue)<=0)
len = 1;
}else len = 0;
while(len>0){
putchar('*');
--len;
}
putchar('\n');
}
if(ovflow > 0) printf("there are %d words >= %d\n",ovflow,MAXWORD);
return 0;
}
*/
/*
1-13.编写一个函数,打印输入中单词长度的直方图。 垂直方向 。
好麻烦,仿书上的也弄了好久了。
*/
#include <stdio.h>
#define IN 1
#define OUT 0
#define MAX 11
int main(){
int c,nl=0,nw=0,nc=0,state=OUT; //nc改用记录每个单词长度。 nl记录长度大于10字符的个数。nw依旧是单词数
int ndigit[MAX] ;
int i,j;
for(i=0;i<MAX;++i) ndigit[i]=0;
while((c=getchar())!=EOF){
++nc;
if(c==' ' || c=='\n'|| c=='\t') state=OUT;
else if(state==OUT){
state = IN;
if(nc>10 ) nl++;
else ndigit[nc]++;
nc=0;
++nw;
}
}
int max=0,bo=0;
for(i=1;i<MAX;++i)
if(ndigit[i]>max)
max=ndigit[i];
printf("总单词:%d\n",nw);
for(i= MAX;i >0;--i){
for(j=1;j<MAX;++j)
if((ndigit[j]* MAX / max )>=i){
printf(" * ");
}
else if( ndigit[j]>0 && i==1) //加上这句是防止个数大于1但是一个*输出也没有,所以,如此输出。
printf(" * ");
else
printf(" ");
putchar('\n');
}
for(i=1; i <MAX; ++i)
printf("%2d ",i);
printf(" ----长度\n");
for(i=1;i<MAX;++i)
printf("%2d ",ndigit[i]);
printf(" ----个数\n");
printf("\n>%4d 个数 %5d.\n",10,nl);
return 0;
}
/*
1-14.编写一个程序,打印输入中各个字符出现频度的直方图。
*/
#include <stdio.h>
#include <ctype.h>
#define MAXHIST 15 //直方图最大长度
#define MAXCHAR 128 //字符最大值
int main(){
int c,i;
int len;
int maxvalue;
int cc[MAXCHAR];
for(i=0; i<MAXCHAR;i++)
cc[i]=0;
while((c=getchar())!=EOF)
if(c < MAXCHAR)
++cc[c];
maxvalue=0;
for(i=1;i<MAXCHAR;++i)
if(cc[i]>maxvalue)
maxvalue = cc[i];
for(i=1;i<MAXCHAR; ++i){
if(isprint(i)) //判断是否是可打印字符
printf("%5d - %c - %5d : ",i,i,cc[i]);
else
printf("%5d - - %5d : ",i,cc[i]);
if(cc[i]>0){
if((len=cc[i]*MAXHIST/maxvalue)<=0)
len=1;
}else
len=0;
while(len--) putchar('*');
putchar('\n');
}
return 0;
}
/*
1-15.重新编写1.2节中的温度转换程序,使用函数实现温度转换计算。
*/
#include <stdio.h>
#define LOWER 0
#define UPPER 300
#define STEP 20
float celsius(float );
int main(){
float fahr=LOWER;
printf("fahr\tcelsius\n");
while(fahr <= UPPER){
printf("%3.0f\t%6.1f\n",fahr,celsius(fahr));
fahr += STEP;
}
return 0;
}
float celsius(float fahr){
return (5.0/9.0)*(fahr-32.0);
}
/*
1-16.修改打印最长文本行的程序的主程序main,使之可以打印任意长度的输入行的长度,并尽可能多地打印文本。
*/
#include <stdio.h>
#define MAXLINE 10
int getline(char [],int);
void copy(char [],char []);
int main(){
int len;
int max;
char line[MAXLINE];
char longest[MAXLINE];
max = 0;
while((len=getline(line,MAXLINE))>0){
printf("%d, %s",len,line);
if(len > max){
max = len;
copy(longest,line);
}
}
if(max>0)
printf("%s",longest);
return 0;
}
int getline(char s[],int lim){ //这段很赞,比TCPL上的原程序还赞。字符串s一直在最安全的范围保存字符。可以把最大行MAXLINE设为10观察。
int i,j=0,c;
for(i=0;(c=getchar())!=EOF && c!='\n';++i)
if(i<lim-2)
s[j++]=c;
if(c=='\n'){
s[j++]=c;
++i;
}
s[j]='\0';
return i;
/* int c,i;
for(i=0;i<lim-1 && (c=getchar())!=EOF && c!='\n';++i)
s[i]=c;
//printf("\nfor .end. %d\n",i);
if(c=='\n'){
s[i]=c;
++i;
// printf("\n\\n .in. %d\n",i);
}
s[i] = '\0';
//printf("\n\\0 .end. %d\n",i);
//printf("\n--end getline() ,line=%d\n",i);
return i;
*/
}
void copy(char to[],char from[]){
int i=0;
while((to[i]=from[i])!='\0') ++i;
}
/*
1-17.编写一个程序,打印长度大于80个字符的所有输出行
*/
#include <stdio.h>
#define MAXLINE 1000
#define LONGLINE 80
int main(){
int getline(char [], int );
int len;
char line[MAXLINE];
while((len=getline(line,MAXLINE))>0)
if(len > LONGLINE)
printf("%s",line);
return 0;
}
int getline(char s[],int lim){
int c,i,j=0;
for(i=0;(c=getchar())!=EOF && c!='\n';++i)
if(i<lim-2)
s[j++]=c;
if(c=='\n'){
s[j++]=c;
++i;
}
s[j]='\0';
return i;
}
/*
1-18.编写一个程序,删除每个输入行末尾的空格及制表符,并删除完全是空格的行。
*/
#include <stdio.h>
#define MAXLINE 1000
int getline(char [],int );
int removes(char []);
int main(){
char line[MAXLINE];
while(getline(line,MAXLINE) > 0) { //这个条件应该用不着 判断>0. 因为等于0就不会执行,getline一般也不存在小于0的情况, 除非溢出了。
if(removes(line) >0){
printf("%s",line);
}
else
printf("空行、完全是空格的行\n");
}
return 0;
}
int getline(char s[],int lim){
int c,i,j=0;
for(i=0;(c=getchar())!=EOF && c!='\n';++i)
if(i<lim-2)
s[j++]=c;
if(c=='\n'){
s[j++]=c;
++i;
}
s[j]='\0';
printf("原始长度: %d\n",i);
return i;
}
int removes(char s[]){
int i=0;
while(s[i] != '\n')
++i;
--i;
while(i>=0 && (s[i] == ' ' || s[i] == '\t'))
--i;
if(i>=0){
s[++i]='\n';
s[++i]='\0';
}
printf("修改后长度: %d\n",i); //这样才可以看出来到底有木有删除行末的空格制表符阿、
return i;
}
/*
1-19.编写函数reverse(s),将字符串s中的字符顺序颠倒过来。使用该函数编写一个程序,每次颠倒一个输入行中的字符顺序。
*/
#include <stdio.h>
#define MAXLINE 1000
int getline(char [],int);
void reverse(char []);
int main(){
char line[MAXLINE];
while(getline(line,MAXLINE)>0){
reverse(line);
puts(line);
}
return 0;
}
int getline(char s[],int lim){
int c,i,j=0;
for(i=0;(c=getchar())!=EOF && c!='\n';++i)
if(i<lim-2)
s[j++]=c;
if(c=='\n'){
s[j++]=c;
++i;
}
s[j]='\0';
return i;
}
void reverse(char s[]){
int i=0,j=0;
char temp;
while(s[i] != '\0') ++i;
--i;
if(s[i]=='\n') --i;
//前面排除了末尾的\0和\n字符,i指向最后,j=0为最前。开始颠倒。
while(j<i){
temp=s[j];
s[j]=s[i];
s[i]=temp;
--i;
++j;
}
}
/*
1-20.编写程序detab,将输入中的制表符替换成适当数目的空格, 使空格充满到下一个制表符终止位的地方。
假设制表符终止位的未知是固定的,比如每隔n列就会出现一个制表符终止位。n应该作为变量还是符号常量呢?
最后修改为'\r'而非EOF,因为getch读取一个字符。所以不会读到EOF。
TAB设为8。输出和控制台显示的是一样的。看来对了。而且知道控制台的tab也是8个空格。耶,睡觉。2014-7-17 0:12:52
*/
#include <stdio.h>
#define TAB 8
void detab(int );
int main(){
int c,i=0;
char line[1000];
int j=0;
while((c=getch()) != '\r'){
switch(c){
default:
putchar(c);
break;
case '\t':
detab(i);
i=-1;
break;
case '\b':
putchar(c);
--i;
break;
case '\r': //不知为何,\n识别不了。试验几次知道了,getch没回显,按Enter后会回到此行首端。只是回车的功能。没有换行,那就\r喽。 还真行。
putchar(c);
i=-1;
break;
}
++i;
line[j]=c;
++j;
}
line[++j]='\0';
printf("\n%s\n",line);
return 0;
}
void detab(int i){
int j=TAB- (i)%TAB ;
while(j--) putchar(' ');
}
/*
1-21.编写程序entab,将空格串替换为最少数量的制表符和空格,但要保持单词之间的间隔不变。假设制表符终止位的位置与练习1-20的detab程序的程序情况相同。
当使用一个制表符或者一个空格都可以到达下一个制表符终值位时,选用哪一种替换字符比较好?
*/
#include <stdio.h>
#define TABING 8
int main(){
int c,nb,pos,nt; //nb,nt用来替换空格串的空格和制表符的最少个数。pos是程序在文本行的当前位置。
nb=0;
nt=0;
for(pos=1;(c=getchar())!=EOF;++pos){
if(c==' '){
if(pos%TABING != 0) ++nb;
else {
nb=0;
++nt;
}
}else{
for(;nt>0;--nt)
//putchar('\t');
printf("\\t"); //上句换成这句,可以清楚看到换制表符的地方。
if(c=='\t')
nb=0;
else for(;nb>0;--nb)
putchar('-');//用-表示空格。清楚点
putchar(c);
//修改改动的pos。
if(c=='\n') pos=0;
else if(c=='\t'){
pos += (TABING -(pos-1)%TABING)-1;
}
}
}
return 0;
}
/*
1-22.编写一个程序,把较长的输入行“折”成短一些的两行或多行,折行的位置在输入行的第n列之前的最后一个非空格之后。
要保证程序能够智能地处理输入行很长以及在指定的列前没有空格或制表符时的情况。
*/
#include <stdio.h>
#define LINES 10
#define MAXLINE 15
void printl(char [],int);
int main(){
int c,i=0,pos=0;
char line[MAXLINE];
while((c=getchar())!=EOF){
line[i++]=c;
pos++;
if(c==' ' || c=='\t' ) {
printl(line,--i);
i=0;
}
else if(pos%(LINES)==0) {
printl(line,i);
i=0;
}
else if(c=='\n') {
printl(line,--i);
i=0;
pos--;
}
else ;
}
return 0;
}
void printl(char s[],int i){
if(i>0){
s[i]='\0';
printf("%s\n",s);
}
}
/*
#include <stdio.h>
#define MAXCOL 10
#define TABING 8
char line[MAXCOL];
int exptab(int);
int findblnk(int);
int newpos(int);
void printl(int pos);
int main(){
int c,pos=0;
while( (c=getchar())!= EOF){
line[pos]=c;
if(c=='\t')
pos = exptab(pos);
else if(c=='\n'){
printl(pos);
pos=0;
}
else if(++pos >= MAXCOL){
pos = findblnk(pos);
printl(pos);
pos=newpos(pos);
}
}
return 0;
}
void printl(int pos){
int i;
for(i=0;i<pos;++i)
putchar(line[i]);
if(pos>0)
putchar('\n');
}
int exptab(int pos){
line[pos]=' ';
for(++pos;pos<MAXCOL && pos % TABING != 0;++pos)
line[pos] = ' ';
if(pos < MAXCOL)
return pos;
else {
printl(pos);
return 0;
}
}
int findblnk(int pos){
while(pos>0 && line[pos]!=' ')
--pos;
if(pos==0)
return MAXCOL;
else
return pos+1;
}
int newpos(int pos){
int i,j;
if(pos<=0 || pos>=MAXCOL)
return 0;
else {
i=0;
for(j=pos;j<MAXCOL;)
line[i++] = line[++j];
return i;
}
}
*/
/*
1-23.编写一个删除C语言程序中所有的注释语句。要正确处理带引号的字符串与字符常量。在C语言中,注释不允许嵌套。
这程序不会过滤//注释,可能因为c99才加上行注释的原因吧。这会导致一个bug,比如一个正常的程序。 有了这么一行 [ // /* 这里是注释 ],程序就会运行出错。一直要求读入,直到找到匹配的 才可输出字符串。
所以小改进了一下。rcomment函数判断到了//后,进入新加的判断行注释函数line_comment即可,遇到回车则注释结束。
*/
#include <stdio.h>
#define MAXLINE 1000
void rcomment(int);
void in_comment();
void line_comment();
void echo_quote(int);
char delcomment[MAXLINE];
int i=0;
int main(){
int c;
while((c=getchar())!=EOF)
rcomment(c);
delcomment[i++]='\0';
puts(delcomment);
return 0;
}
void rcomment(int c){
int d;
if(c=='/')
if((d=getchar())=='*')
in_comment();
else if(d=='/'){
//putchar(c);
//delcomment[i++]=c;
line_comment();
}
else {
//putchar(c);
delcomment[i++]=c;
//putchar(d);
delcomment[i++]=d;
}
else if(c=='\'' || c=='\"')
echo_quote(c);
else
// putchar(c);
delcomment[i++]=c;
}
void in_comment(){
int c,d;
c=getchar();
d=getchar();
while(c!='*' || d!='/'){
c=d;
d=getchar();
}
}
void echo_quote(int c){
int d;
//putchar(c);
delcomment[i++]=c;
while((d=getchar())!=c){
//putchar(d);
delcomment[i++]=d;
if(d=='\\')
//putchar(getchar());
delcomment[i++]=getchar();;
}
// putchar(d);
delcomment[i++]=d;
}
void line_comment(){
int c;
c=getchar();
while(c!='\n'){
c=getchar();
}
}
/*
1-24.编写一个程序,查找C语言程序中的基本语法错误,如圆括号、方括号、花括号不匹配等。要正确处理引号(包括单引号和双引号)、转义字符序列与注释。
*/
#include <stdio.h>
#define MAXLINE 1000
int brace=0,brack=0,paren=0; //brace大括号,brack方括号,paren圆括号。
void in_quote(int );
void in_comment();
void line_comment();
void search(int);
int main(){
int c;
while((c=getchar())!=EOF){
if(c=='/'){
if((c=getchar()) == '*')
in_comment();
else if(c=='/')
line_comment();
else
search(c);
}else if(c=='\'' || c=='\"')
in_quote(c);
else
search(c);
if(brace<0){
printf("Unbalanced braces\n");
brace=0;
}else if(brack<0){
printf("Unbalanced brackets\n");
brack=0;
}else if(paren<0){
printf("Unbalanced parentheses\n");
paren=0;
}
// printf("brace=%d\tbrack=%d\tparen=%d\n",brace,brack,paren);
}
if(brace>0)
printf("--Unbalanced braces\n");
if(brack>0)
printf("--Unbalanced brackets\n");
if(paren>0)
printf("--Unbalanced parentheses\n");
return 0;
}
void search(int c){
switch(c){
case '{':
++brace; break;
case '}':
--brace; break;
case '[':
++brack; break;
case ']':
--brack; break;
case '(':
++paren; break;
case ')':
--paren; break;
default: break;
}
}
void in_comment(){
int c,d;
c=getchar();
d=getchar();
while(c!='*' || d!='/'){
c=d;
d=getchar();
}
}
void line_comment(){
int c;
c=getchar();
while(c!='\n'){
c=getchar();
}
}
void in_quote(int c){
int d;
while((d=getchar())!=c)
if(d=='\\')
getchar();
}
昨天一整天,今天半天,终于完了。这才第一章。感觉还好。
end.