C程序设计语言课后习题答案

刚好在读这本书,故将习题代码进行分享,经过测试均可以运行。

如果有所帮助,也希望收到你的正向反馈~

目录为题目的一个简要描述,方便回溯代码。

仓库地址:

[TOC]

第一章

1-8: 统计空白字符个数

// Count blank characters
#include <stdio.h>

int main(void) {
int c;
int space, tab, nl;

space = tab = nl = 0;
while ((c = getchar()) != EOF){
if (c == ' ')
++space;
if (c == '\t')
++tab;
if (c == '\n')
++nl;
}
// 注意,采用 '\' 可以实现句内换行,但其前面的空白会被打印至输出
printf("Space: %d\
Tab: %d\
Newline: %d\n", space, tab, nl);
}

1-9: 复制输入到输出,多个空格转一个

// Ignore extra space
#include <stdio.h>

int main(void) {
int c;

while ((c = getchar()) != EOF){
putchar(c);
if (c == ' ') // 打印第一个空格,其他的略去
while ((c = getchar()) == ' ')
;
}
}

1-10: 复制输入到输出,可视化制表符,回退符及反斜杠

// exercise 1-10
#include <stdio.h>
#include <stdlib.h>

int main(void) {

int c;

// 关闭输入缓存机制,让 getchar() 达到 getch() 的效果
// -icanon: Enable (disable) canonical input (ERASE and KILL processing). -- From manual of stty
// Ps: Mac 没有 conio.h 库
system("stty -icanon");

while ((c = getchar()) != EOF){
if (c == '\t')
printf("\\t");
else if (c == '\b')
printf("\\b");
else if (c == '\\')
printf("\\\\");
else
putchar(c);
}
system("stty -icanon"); // 重新打开缓存机制,不然会影响到其他 exercise 的运行
}


1-12: 以每行一个单词的形式输出

// exercise 1-12
#include <stdio.h>

#define IN 1 /* inside a word */
#define OUT 0 /* outside a word */

int main()
{
int c, state;

while ((c = getchar()) != EOF) {
putchar(c);
if (c == ' ' || c == '\n' || c == '\t')
putchar('\n');
}
}

1-13-a: 水平打印单词长度直方图

#include <stdio.h>
#define LENGTH 10
int main()
{
int c, i, j, lw;
int lws[LENGTH];

lw = 0;
for (i = 0; i < LENGTH; ++i)
lws[i] = 0;

while ((c = getchar()) != EOF) {
if (c == ' ' || c == '\n' || c == '\t'){
++lws[lw];
lw = 0;
}
else
++lw;
}

for (i = 0; i < LENGTH; ++i){
printf("%d |", i);
for (j = 0; j < lws[i]; ++j)
putchar('#');
putchar('\n');
}
}

1-13-b: 垂直打印单词长度直方图

#include <stdio.h>
#define LENGTH 10
#define HAVEUNPRINTED 1 // 还没打印完
#define EOP 0 // End of printing

int main()
{
int c, i, j, lw, state;
int lws[LENGTH];

lw = 0;
state = HAVEUNPRINTED;
for (i = 0; i < LENGTH; ++i)
lws[i] = 0;

while ((c = getchar()) != EOF) {
if (c == ' ' || c == '\n' || c == '\t'){
++lws[lw];
lw = 0;
}
else
++lw;
}

// 这里打印一下“坐标”,之所以和上面循环分开是因为这样可能更易读 :)
for (i = 0; i < LENGTH; ++i)
printf("%d\t", i);
putchar('\n');

for (i = 0; i < LENGTH; ++i)
printf("-\t");
putchar('\n');

while (state){
j = 0;
for (i = 0; i < LENGTH; ++i){
if (lws[i] > 0){
printf("#\t");
--lws[i]; // 打印完后频数-1
}
else{
putchar('\t');
j++;
}
}
putchar('\n');

if (j == LENGTH)
state = EOP; //当所有频数为 0 时,j == LENGTH
}
}

1-15: 重新编写温度转换程序

#include <stdio.h>
#define FAHR 0
#define UPPER 300
#define STEP 20
void temperature_convert(int fahr, int upper, int step);

int main(){
temperature_convert(FAHR, UPPER, STEP);
return 0;
}

void temperature_convert(int fahr, int upper, int step){
int celsius;
while (fahr <= upper) {
celsius = 5 * (fahr-32) / 9;
printf("%d\t%d\n", fahr, celsius);
fahr = fahr + step;
}
}

1-16: 打印任意长度输出行

#include <stdio.h>
#define MAXLINE 1000 /*maximum input line length*/

int get_line(char line[], int maxline);

/* print input line and length */
int main()
{
int len; /* current line length */
char line[MAXLINE]; /* current input line */

while ((len = get_line(line, MAXLINE)) > 0)
printf("Length: %d \nline: %s", len, line);
return 0;
}

/* getline: read a line into s, return length */
int get_line(char s[],int lim)
{
int c, i;
c = 0;

for (i=0; i < lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
s[i] = c;
if (c == '\n')
{
s[i] = c;
++i;
}
s[i] = '\0';
return i-1;
}

1-17: 打印大于长度80的行

#include <stdio.h>
#define MAXLINE 1000 /*maximum input line length*/

int get_line(char line[], int maxline);

/* print input line and length */
int main()
{
int len; /* current line length */
char line[MAXLINE]; /* current input line */

while ((len = get_line(line, MAXLINE)) > 0)
if(len > 80)
printf("%s", line);
return 0;
}

/* getline: read a line into s, return length */
int get_line(char s[],int lim)
{
int c, i;
c = 0;

for (i=0; i < lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
s[i] = c;
if (c == '\n')
{
s[i] = c;
++i;
}
s[i] = '\0';
return i-1;
}

/* copy: copy 'from' into 'to'; assume to is big enough */
void copy_then_print_length(char to[], char from[])
{
int i;
i = 0;
while ((to[i] = from[i]) != '\0')
++i;
printf("Length: %d", i);
}


1-18: 删除行末尾的空白和全为空白字符的行

#include <stdio.h>
#define MAXLINE 10000 /*maximum input text length*/

void get_text(char text[], int maxline);
void delete_space(char text[]);

/* 可以看到本段程序中有重复的代码和一些可以优化的地方 */
int main()
{
char text[MAXLINE]; /* current input text */

get_text(text, MAXLINE);

delete_space(text); /* 删除每一行末尾的空格*/
delete_space(text); /* 第二次删除原来有空格的行*/
printf("%s", text);
return 0;
}

/* getline: read text into s */
void get_text(char s[],int lim)
{
int c, i;
c = 0;

for (i=0; i < lim-1 && (c=getchar())!=EOF; ++i)
s[i] = c;
s[i] = '\0';

}

/* 删除每行后多余的空白字符(就地修改) */
void delete_space(char text[])
{
int i, j, space; /* i, j 双指针,space用于记录行尾空白字符(除换行符)的数量,以更新 j 的位置 */

/* 利用 j 对 text[] 进行就地修改 */
for (i = j = space = 0; text[i] != '\0'; ++i, ++j)
{
if (text[i] == '\n')
{
if (j == 0) /* 防止数组在下面的判断中越界 */
{
--j;
continue;
}
else if (text[i] == text[j-1]) /* 判断是否是连续的空行 */
--j;
else if (space != 0) /* 判断换行符前是否有空白,其实 space != 0 可以删去,加上便于理解 */
j -= space;
}

if (text[i] == ' ' || text[i] == '\t')
++space; /* 下一次循环起作用 */
else
space = 0;

text[j] = text[i];
}
text[j] = '\0';
}


1-19: 颠倒每行的字符

#include <stdio.h>
#define MAXLINE 10000 /*maximum input text length*/

void get_text(char text[], int maxline);
void reverse(char text[], int i, int j);

int main()
{
char text[MAXLINE]; /* current input text */

get_text(text, MAXLINE);

printf("%s", text);
return 0;
}

/* getline: read text into s */
void get_text(char s[],int lim)
{
int c, i, j; /* i, j 记录当前行首和行尾的位置 */
c = 0;

for (i=0, j=0; i < lim-1 && (c=getchar())!=EOF; ++i)
{
s[i] = c;
if (c == '\n')
{
reverse(s, i-1, j);
j = i + 1;
}
}
s[i] = '\0';

}

/* 颠倒顺序 */
void reverse(char text[], int i, int j)
{
int temp;
while (i > j)
{
temp = text[i];
text[i] = text[j];
text[j] = temp;
--i;
++j;
}
}


1-20: tab转space

#include <stdio.h>
#define MAXLINE 10000 /*maximum input text length*/
#define TSPACE 4 /* Tab = 4 space */

void get_text(char text[], int maxline);
int detab(char s[], int i);

int main()
{
char text[MAXLINE]; /* current input text */

get_text(text, MAXLINE);

printf("%s", text);
return 0;
}

/* get_text: read text into s */
void get_text(char s[],int lim)
{
int c, i;
c = 0;

for (i=0; i < lim-1 && (c=getchar())!=EOF; ++i)
{
s[i] = c;
if (c == '\t')
i = detab(s, i);
}
s[i] = '\0';

}

/* 替换制表符,返回索引 i */
int detab(char s[], int i)
{
int fill_space;

fill_space = TSPACE - i % TSPACE; /* 计算填充的空格数量,i 从 0 开始索引,故 TSPACE-,否则 (TSPACE+1)- */
while(fill_space--){
s[i] = ' ';
++i;
}
return i-1; /* 最后需要让i-1,因为循环会自动加1,若不进行抵消程序会出错,当然,应当可以优化代码 & 下次一定 */
}


1-21: space转tab

#include <stdio.h>
#define MAXLINE 10000 /*maximum input text length*/
#define TSPACE 4 /* Tab = 4 space */
#define KEEPSPACE 1 /* 1: 倾向于保留空格,0: 倾向于替换空格为制表符 */

void get_text_entab(char text[], int maxline);
int detab(char s[], int i);

int main()
{
char text[MAXLINE]; /* current input text */

get_text_entab(text, MAXLINE);

printf("%s", text);
return 0;
}

/* get_text: read text into s*/
/* entab: 替换空格为制表符,本质上来说,这题和18题很像,均是减少数组内字符的操作 */
void get_text_entab(char s[],int lim)
{
int c, i, redundant_space; /* redundant_space 记录多余的空格,或者说替换时 i 需要倒退的次数 */
c = 0;

for (i=0, redundant_space=-1; i < lim-1 && (c=getchar())!=EOF; ++i)
{
s[i] = c;
if (c == ' ')
{
++redundant_space;
if (i % TSPACE == (TSPACE-1))
{
/* 若KEEPSPACE==1,那么当一个空格后是制表符终止位时,不进行替换,此时goto到else后的语句重置 redundant_space(这么写是为了简洁,然后熟悉一下goto语句)*/
if (KEEPSPACE && redundant_space == 0)
goto keepspace;
i = i - redundant_space; /* 若记录的是(连续)空格的数量,此处需要 +1,否则会覆盖空格前面的字符 */
s[i] = '\t';
}
}
else
keepspace:
redundant_space = -1;
}
s[i] = '\0';
}


1-22: “折”行

#include <stdio.h>
#define MAXLINE 10000 /*maximum input text length*/
#define LENGTH 3 /* 每行的字符数 */

void get_text(char text[], int maxline);

/* 此程序未对tab进行空格的替换从而使得真正分行,结合1-20的detab程序可以实现,不过还需处理多余的空格 */
int main()
{
char text[MAXLINE]; /* current input text */
get_text(text, MAXLINE);

// forcing_newline(text, LENGTH);

printf("%s", text);
return 0;
}

/* getline: read text into s */
void get_text(char s[],int lim)
{
int c, i, j;
c = 0;

for (i=0, j=0; i < lim-1 && (c=getchar())!=EOF; ++i)
{


s[i] = c;

if (s[i] == '\n')
j = 0;
else
++j;

if (j == LENGTH){
s[++i] = '\n';
// s[++i] = c;
j = 0;
}

}
s[i] = '\0';

}