首页 > 代码库 > 第一章
第一章
练习1-6:
验证表达式getchar()!=EOF的值是0还是1
#include <stdio.h> main() { int c; while(1) printf("%d",c=getchar()!=EOF); }
练习1-7:
编写一个打印EOF值的程序
#include <stdio.h> main() { int c; c=EOF; printf("%d",c); }
练习1-8:
编写一个统计空格、制表符与换行符个数的程序
#include <stdio.h> main() { int c,space,tab,enter; t=0,space=0,enter=0; /*remember init ,otherwise something may be wrong*/ while((c=getchar())!= ‘#‘) { if (c==‘ ‘) ++space; if(c == ‘\t‘) ++tab; if (c==‘\n‘) ++enter; } printf("space: %d tab:%d enter:%d",space,tab,enter); }
练习1-9:
编写一个将输入复制到输出的程序,并将其中连续的多个空格用一个空格代替
#include <stdio.h> main() { int c; while((c=getchar())!=EOF) { if(c==‘ ‘) { while((c=getchar()) == ‘ ‘) { continue; } printf(" "); } putchar(c); } }
练习1-10:
编写一个将输入复制到输出的程序,并将其中的制表符替换为\t,把回退符替换为\b,把反斜杠替换为\\。这样可以将制表符和回退符以可见的方式像是出来。
#include <stdio.h> main() { int c; while ((c=getchar())!=EOF) { if(c ==‘\t‘) printf("\\t"); else if(c == ‘\b‘) printf("\\b"); else if(c == ‘\\‘) printf("\\"); else putchar(c); } }
练习1-11:
你准备如何测试单词计数程序? 如果程序中存在某种错误,那么什么样的输入最可能发现这类错误?
个人答案:输入测试满足边界条件的情况,比如此程序中,就分别测试换行,空格,制表符,情况下的单词计数是否准确。顺便贴下此程序:
#include <stdio.h> #define IN 1 #define OUT 0 int main() { int c, nl, nw, nc, state; state = OUT; nl = nw = nc = 0; 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); }
练习 1-12:
编写一个程序,以每行一个单词的形式打印其输入。
#include <stdio.h> main() { int c; while((c=getchar())!=EOF) { if(c==‘ ‘||c==‘\t‘) { while((c=getchar())==‘ ‘||c==‘\t‘) { continue; } printf("\n"); } putchar(c); } }
个人的debug过程:
1.while((c=getchar())==‘ ‘||c==‘\t‘) 不能写成while((c=getchar())==‘ ‘||(c=getchar())==‘\t‘) 因为这样的话,当变量满足空格的时候,进入的话会被赋值两次,从而导致结果缺失。
比如,当你输入“ ss”,因为第一次空格会进入循环,而当第一个输入s的时候,getchar了一次,c=getchar())==‘ ‘不满足,但是后面还有判断‘\t‘的,又getchar了一次,相当于损失了一个s,所以最后答案会输出s,而不是ss
2.putchar()不能放在if的else后面,也就是:
if(c==‘ ‘||c==‘\t‘) { while((c=getchar())==‘ ‘||c==‘\t‘) { continue; } printf("\n"); } else putchar(c);
原因跟1是类似的,当有空格的时候,循环会消耗一个getchar的值,也就是说,进入循环的几次就会缺少几个字符。
练习1-13:
编写一个程序,打印输入中单词长度的直方图
水平直方图: (这里假设最大单词长度为10)
#include <stdio.h> #define MAXLENTH 10 main() { int w,L,wl[MAXLENTH],j,i; w=0,L=0,j=0; for(i=0;i<=MAXLENTH;i++) wl[i]=0; while((w=getchar())!=EOF) { if(w==‘ ‘||w==‘\n‘||w==‘\t‘) { ++wl[L]; L=0; } else ++L; } printf("\n"); for(i=1;i<=MAXLENTH;i++) { printf("%d :",i); for(j=0;j<wl[i];j++) printf("#"); printf("\n"); } }
debug:
这里的++wl[L]不能写成跟书上类似的++wl[L-‘0‘],因为书本的案例中c是个字符,c-‘0‘是为了把c转化为整型,(0的ascii值为48,而字符1-9的ascii恰好为49-57,差值恰好符合自身)而这里L是数字不需要转换,(L-‘0’相当于L的值减去48,可以写成L-0,这样就没错)
垂直直方图:
思路:首先求得所有不同长度单词的个数的最大值,这也是纵坐标的最大值m,纵坐标递减,每次逐一对比每个长度单词的个数,如果个数等于m,那么就打印‘#‘,否则打印空格。 我的程序中,额外加了个数组t[],这个数组是用来打tag,表明这个长度的单词个数需要多次打印,比如说,长度为1的单词出现了5次,那么在纵坐标[1,5],它都需要打印‘#’
#include <stdio.h> #define MAXLENTH 10 main() { int w,L,wl[MAXLENTH],j,i,m,t[MAXLENTH]; //w:word,L:lenth,m:maxLenth// w=0,L=0,j=0,m=0; for(i=0;i<=MAXLENTH;i++) wl[i]=0,t[i]=0; while((w=getchar())!=EOF) { if(w==‘ ‘||w==‘\n‘||w==‘\t‘) { ++wl[L-0]; L=0; } else ++L; } printf("\n"); for(i=1;i<=MAXLENTH;i++) { if(m<wl[i]) m=wl[i]; } printf(" ^\n |\n |\n"); for(i=m;i>0;i--) { if(i>=10) printf("%d|",i); else printf("%d |",i); for(j=0;j<=MAXLENTH;j++) { if(wl[j]==i) t[j]=1; if(t[j]==1) printf(" #"); else printf(" "); } printf("\n"); } printf(" - - - - - - - - - - - - - - - - - >\n " ); for(i=1;i<=MAXLENTH;i++) printf(" %d",i); printf("\n"); }
练习1-14:
编写一个程序,打印输入中各个字符出现频度的直方图
#include <stdio.h> #define MAXHIST 15 #define MAX 128 main() { int c,i,j; int cc[MAX]; for(i=0;i<MAX;++i) cc[i]=0; while((c=getchar())!=EOF){ if(c<MAX) ++cc[c]; } printf("\n"); for(i=1;i<=MAX;i++) { if(isprint(i)) { putchar(i); printf(" :"); for(j=0;j<cc[i];j++) printf("#"); printf("\n"); } } }
思路:
利用ascii码的特性保存输入字符的位置(getchar获得的是字符的ascii编码值,我们把这个值保存在数组对应的位置上,然后getchar对应的值可以输出该字母。 比如:我们输入字母a,它对应的ascii值为97,所以我们保存在数组cc[97],然后对c[97]进行增值,打印字母用getchar打印出a),这里因为字符很多,所以只打印水平直方图。 isprint函数是判断该字符是否可以打印,用这个选项可以过滤掉那些不可打印的字符。 竖直直方图的打印可以考虑直接打印那些非0次数的字母,这样就可以放得下,但是这样做就得先提取出来那些非0次数的,这里我写了一个简洁的:
#include <stdio.h> #define MAXHIST 15 #define MAX 128 main() { int c,i,j,m=0; int cc[MAX],t[MAX]; for(i=0;i<MAX;++i) cc[i]=t[i]=0; while((c=getchar())!=EOF){ if(c<MAX) ++cc[c]; } printf("\n"); for(i=1;i<=MAX;i++) { if(m<cc[i]) m=cc[i]; } printf(" ^\n |\n |\n"); for(i=m;i>0;i--) { if(i>=10) { putchar(i); printf("|"); } else { putchar(i); printf(" |"); } for(j=0;j<=MAX;j++) { if(cc[j]==0) continue; if(cc[j]==i) t[j]=1; if(t[j]==1) printf(" #"); else printf(" "); } printf("\n"); } printf(" - - - - - - - - - - - - - - - - - >\n "); for(i=0;i<=MAX;i++) { if(cc[i]==0) continue; else { printf(" "); putchar(i); } } printf("\n"); }
这个程序有个问题就是要手动输入才行,利用echo通过管道作为输入坐标轴就不准,具体原因有待查明。
效果图:
练习1-15:
重新编写1.2节中的温度转换程序,使用函数实现温度转换计算。
#include <stdio.h> main() { fahr_to_celsius(0,300,20); } fahr_to_celsius(int begin,int end,int step) { int b,e,s,f,c; b=begin; e=end; s=step; f=begin; while(f<=e){ c=5*(f-32/9); printf("%d\t%d\n",f,c); f=f+s; } }
练习1-16:
修改打印最长文本行的程序的主程序main,使之可以打印任意长度的输入行的长度,并尽可能多地打印文本
#include <stdio.h> #define MAXLINE 10 int getline1(char s[],int lim); void copy(char to[],char from[]); main() { int max,len; max=len=0; char line[MAXLINE]; char longest[MAXLINE]; while((len=getline1(line,MAXLINE))>0) if(len>max) { printf("%d",len); max=len; copy(longest,line); } if (max > 0) { printf("max= %d, the longestLine = %s",max, longest); } } int getline1(char s[],int lim) { int c,i,j; j=0; for(i=0;(c=getchar())!=EOF&&c!=‘\n‘;i++) if(i<lim-2) { s[j]=c; j++; } if(c==‘\n‘) { s[j]=c; j++; i++; } s[j]=‘\0‘; return i; } void copy(char to[],char from[]) { int i; i=0; while((to[i]=from[i])!=‘\0‘) ++i; }
debug:
1.一直没很懂题意,所以参考了:http://blog.csdn.net/pollution_free/article/details/53411685
2.之所以用getline1这个函数名,是因为getline会和stdio.h里面的getline冲突,我的是centos6.5,gcc4.47版本请根据实际情况来
3.getline1里面的判断要把i<lim-2独立出来,不能跟课本一样写成 i<lim-2&&(c=getchar())!=EOF的形式,因为这样的话i就不会再自动加1了
4.其他都是小问题,具体原理参考上面那个链接
练习1-17:
编写一个程序,打印长度大于80个字符的所有输入行
#include <stdio.h> #define MAXLINE 1000 int getline1(char s[],int lim); void copy(char to[],char from[]); main() { int max,len; max=len=0; char line[MAXLINE]; char longest[MAXLINE]; while((len=getline1(line,MAXLINE))>80) printf("max= %d, the longestLine = %s",len, line); } int getline1(char s[],int lim) { int c,i,j; j=0; for(i=0;(c=getchar())!=EOF&&c!=‘\n‘;i++) if(i<lim-2) { s[j]=c; j++; } if(c==‘\n‘) { s[j]=c; j++; i++; } s[j]=‘\0‘; return i; } void copy(char to[],char from[]) { int i; i=0; while((to[i]=from[i])!=‘\0‘) ++i; }
有了1-16的例子,1-17就很简单了,参考1-16
练习1-18
编写一个程序,删除每个输入行末尾的空格及制表符,并删除完全是空格的行
#include <stdio.h> #define MAXLINE 1000 int getline1(char line[],int maxline); int remove1(char s[]); int main() { char line[MAXLINE]; while(getline1(line,MAXLINE)>0) { if(remove1(line)>0){ printf("%s",line); } } return 0; } int getline1(char s[],int lim) { int c,i,j; j=0; for(i=0;((c=getchar())!=EOF) && (c!=‘\n‘);++i){ if(i<lim-2){ s[j]=c; ++j; } } if(c==‘\n‘){ s[j]=c; ++j; ++i; } s[j]=‘\0‘; return i; } int remove1(char s[]) { int i; i=0; while(s[i]!=‘\n‘){ ++i; } --i; while(i>=0 && (s[i]==‘ ‘||s[i]==‘\t‘)){ --i; } if(i>=0){ ++i; s[i]=‘\n‘; ++i; s[i]=‘\0‘; } return i; }
参考:http://blog.csdn.net/civil_chow/article/details/51533119
先读取字符串的最后一位,然后往后推,直到遇到不是空格和制表符的字符位置,在其位置填充代表行结束的‘\n‘和‘\0‘,这个就完成了删掉末尾的空格和制表符的目的,然后打印字符长度不为0的字符串就达到了去空行的目的。
练习1-19
编写函数reverse(s),将字符串s中的字符顺序颠倒过来,使用该函数编写一个程序,每次颠倒一个输入行中的字符顺序
#include <stdio.h> #define MAXLINE 1000 int getline1(char line[],int maxline); int reverse1(char s[]); int main() { int len; len=0; char line[MAXLINE]; while((len=getline1(line,MAXLINE))>0) reverse1(line); } int getline1(char s[],int lim) { int c,i,j; j=0; for(i=0;((c=getchar())!=EOF) && (c!=‘\n‘);++i){ if(i<lim-2){ s[j]=c; ++j; } } if(c==‘\n‘){ s[j]=c; ++j; ++i; } s[j]=‘\0‘; return i; } int reverse1(char s[]) { int i,j; i=0; while(s[i]!=‘\n‘) i++; char to[i+2]; j=0; while(i>=0) { to[j]=s[i]; i--; j++; } to[j]=‘\n‘; j++; to[j]=‘\0‘; printf("%s",to); }
参考:http://blog.csdn.net/civil_chow/article/details/51542081
个人解题思路:
先计算出字符串有多长,然后以赋值方式从最后一个字符往前赋值给新的字符串数组to[i+2] (这里i+2是因为默认最后的字符以‘\n‘和‘\0‘作为结束标志,也就是i+2是整个字符串长度,i是字符串最后一个真实的字符),然后打印to数组。
练习1-20:
编写程序detab,将输入中的制表符换成适当数目的空格,使空格充满到下一个制表符终止的地方
参考:http://www.cnblogs.com/ningvsban/p/3774519.html
#define DETAB 8 //将制表符替换为适当的空格,并且空格填充到下一个制表符终止位 void main(){ int c,pos,i,nbs; pos = 1; nbs = 0; while((c=getchar())!=EOF){ if(c == ‘\t‘){ nbs = (DETAB-pos%DETAB)%DETAB+1; i = 0; for(;i<nbs;i++){ putchar(‘ ‘); } pos += nbs; }else if(c == ‘\n‘){ putchar(c); pos = 1; nbs = 0; }else{ pos++; putchar(c); } } }
源码抄自参考上面的链接,老实说,这个题目什么意思,我现在也没看懂..
练习1-21:
编写程序entab,将空格串替换成最少数量的制表符和空格,但要保持单词之间的间隔不变。假设制表符终止位的位置与练习1-20的detab程序的情况相同。当使用一个制表符或者一个空格都可以到达下一个制表符终止位时,选用哪种替换字符比较好?
思路参考:http://www.mamicode.com/info-detail-55979.html
源码抄自:http://www.cnblogs.com/jango/archive/2013/10/25/3388574.html
(因为实在看不懂什么意思,所以没自己写)
#include <stdio.h> #define TABINC 8 main() { int c, nb, nt, pos; nb = 0; nt = 0; for(pos = 1; (c = getchar()) != EOF; ++pos) if(c == ‘ ‘) { if(pos % TABINC != 0) ++nb; else { nb = 0; ++nt; } }else { for( ; nt > 0; --nt) putchar(‘\t‘); if(c == ‘\t‘) nb = 0; else for( ; nb > 0; --nb) putchar(‘ ‘); putchar(c); if(c == ‘\n‘) pos = 0; else if (c == ‘\t‘) pos = pos + (TABINC - (pos - 1) % TABINC) - 1; } }
练习1-22:
编写一个程序,把较长的输入行‘折’成短一些的两行或多行,折行的位置在输入行的第n列之前的最后一个非空格之后。要保证程序能够只能第处理输入行很长以及在指定的列前有空格或制表符的情况。
参考:http://www.cnblogs.com/ningvsban/p/3775383.html
#include<stdio.h> #define MAX_LEN 1000 #define TABLEN 8 #define SEPNUM 10 int getCol(char charArr[MAX_LEN],int start,int index); //主函数 void main(){ int c,i,index,nbs,pos; char charArr[MAX_LEN]; index=0; nbs = 0; while((c=getchar())!=EOF && c!=‘\n‘){ if(c==‘\t‘){ //制表符替换为空格 nbs = TABLEN-(index+1)%TABLEN; for(i=0;i<=nbs;i++){ charArr[index]=‘ ‘; index++; } }else{ charArr[index]=c; //保存字符 index++; } } index--; pos=0; while(pos<=index){ pos = getCol(charArr,pos,index); } } //该函数用于获取n个字符,并输出最后一个非空字符(包括该字符)之前的字符,返回紧跟该非空字符之后的空格的位置 int getCol(char charArr[MAX_LEN],int start,int index){ int end,pos,i,tmp; tmp = start+SEPNUM-1; pos = 0; if(tmp>index){ end = index; }else{ end = tmp; } for(i=end;i>=start;i--){ if(charArr[i]!=‘ ‘){ pos = i; break; } } if(pos>0){ for(i=start;i<=pos;i++){ putchar(charArr[i]); } putchar(‘\n‘); return pos+1; }else{ //该n个字符全部为空格的情况 return end+1; } }
ps: 还是抄的...看不懂题目要干嘛..
练习1-23:
编写一个删除c语言程序中所有的注释语句。要正确处理带引号的字符串与字符常量。在c语言中,注释不允许嵌套。
参考:http://blog.csdn.net/ycjnx/article/details/73649924
http://blog.csdn.net/yuezhiren/article/details/7957823
练习1-24:
编写一个程序,查找C语言程序中的基本语法错误,如圆括号,方括号以及花括号不配对等。要正确的处理引号(包括单引号,双引号)~转移字符序列与注释(如果读者想把该程序编写成完全通用的程序,难度会比较大。)
参考:http://www.cnblogs.com/jango/p/3390990.html
1-23,1-24都超出我目前能力了,打个tag,有机会回来补上
第一章