读书笔记:C程序设计语言,第一章:知识要点 和 课后题全解

缘由

所谓C程序设计语言是: C程序设计语言 作者: (美)Brian W. Kernighan / (美)Dennis M. Ritchie  

为了加强C语言基础,觉得阅读这本优秀的书籍。首先,我会总结一些我认为的要点,如果有优秀的例子和题,我也觉得需要记录下来。

  • 练习题全解:"The C Programming Language", 2nd edition, Kernighan and RitchieAnswers to Exercises

1.1入门

  1. 程序运行过程:编写代码,编译,加载,运行,输入结果到某个地方(hello word的话)
  2. C语言程序无论大小,都是由变量和函数组成。函数的语句执行计算操作。变量则用于储存计算过错中使用的值
  3. #include 告诉编译器,本程序包含标准的输入/输出库的信息。
  4. "hello world\n”,双引号括起来的字符序列称为字符串或字符串常量
  5. \n 表示换行符,这种被称为转义字符的字符表示无法输入或者看不见的东西:\t(制表符),\b(回退符),\"(双引号),\\(反斜杠本身)。\n (newline)  \t (tab)  \b (backspace)  \" (double quote)  \\ (backslash)
  6. printf函数不会自动换行

练习题

Exercise 1-1. Run the ``hello, world'' program on your system. Experiment with leaving out parts of the program, to see what error messages you get.
答:

#include 

int main(void)
{
  printf("hello, world\n");
  return 0;
}
linux 下,可以这样执行:
gcc -W -Wall -ansi -pedantic -o hello hello.c
如此会得到一个hello的输出,直接执行可以得到结果。 

-w     关闭所有警告信息
-Wall 就是打开所有的警告。
-ansi : 关闭GNU扩展中与ANSI C相抵触的部分。 来源
-pedantic: 关闭所有的GNU扩展。 来源
-o 输出文件名,这里就是 hello
gcc --help可以获得帮助文档,也可以上网 官方。


Exercise 1-2. Experiment to find out what happens when prints's argument string contains \c, where c is some character not listed above.
答:
我觉得没有\c这个转移字符。其他转移字符如下:
转义字符
意义
ASCII码值(十进制)
\a
响铃(BEL)电脑会发出声音
007
\b
退格(BS) ,将当前位置移到前一列
008
\f
换页(FF),将当前位置移到下页开头
012
\n
换行(LF) ,将当前位置移到下一行开头
010
\r
回车(CR) ,将当前位置移到本行开头
013
\t
水平制表(HT) (跳到下一个TAB位置)
009
\v
垂直制表(VT)
011
\\
代表一个反斜线字符''\'
092
\'
代表一个单引号(撇号)字符
039
\"
代表一个双引号字符
034
\0
空字符(NULL)
000
\ddd
1到3位八进制数所代表的任意字符
三位八进制
\xhh
1到2位十六进制所代表的任意字符
二位十六进制

1.2变量与算术表达式

  1. 所有变量先声明后使用,声明包含一个类型名和一个变量名
  2. int的范围:-32768到+32767,这是16位的int。也有32位的int
  3. float类型是32位的,范围是10^-38到10^38
  4. c语言中整数除法将执行舍位了,执行中小数部分会被被去掉
  5. printf 不是c语言本身,而是标准函数库里面里面的函数,ANSI标准定义了printf函数
  6. printf("%3d %6d\n",i,j):使用这样方式可以固定打印出来的数字占据的宽度。比如i占3个字符。而%6.1f:则表示整个字符至少占6位,并且小数一位。%.2f 按照浮点数进行打印,小数点后有两位。%.0f强制不打印小数点和小数部分,因此小数部分的位数为0.
  7. 整数和float型做计算,会自动转化为整数型

练习题

Exercise 1-3. Modify the temperature conversion program to print a heading above the table.
答:
#include 

int main(void)
{
  float fahr, celsius;
  int lower, upper, step;

  lower = 0;
  upper = 300;
  step = 20;

  printf("  F     C\n");
  fahr = lower;
  while(fahr <= upper)
  {
    celsius = (5.0 / 9.0) * (fahr - 32.0);
    printf("%3.0f %6.1f\n", fahr, celsius);
    fahr = fahr + step;
  }
  return 0;
}


Exercise 1-4. Write a program to print the corresponding Celsius to Fahrenheit table.
答:

#include 

int main(void)
{
  float fahr, celsius;
  int lower, upper, step;

  lower = 0;
  upper = 300;
  step = 20;

  printf("C     F\n\n");
  celsius = lower;
  while(celsius <= upper)
  {
    fahr = (9.0/5.0) * celsius + 32.0;
    printf("%3.0f %6.1f\n", celsius, fahr);
    celsius = celsius + step;
  }
  return 0;
}

1.3for循环

练习题

Exercise 1-5. Modify the temperature conversion program to print the table in reverse order, that is, from 300 degrees to 0.
答:
#include 

/* print Fahrenheit-Celsius table */
int
main()
{
     int fahr;

     for (fahr = 300; fahr >= 0; fahr = fahr - 20)
             printf("%3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32));

   return 0;
}

1.4 符号常量

  1. 程序中使用那么300,20等magic number是不好的,因为不能向别人清楚表达这个数字的含义。使用#define可以给这些数字一个含义
  2. #define的使用:替换某些字符,可以不仅仅是数字,可以是任何序列符,通常称之为符号常量,
  3. #define的名字最好是大写
  4. 注意#define末尾没有分号。

1.5 字符输入和输出

标准库保证文本库是以序列的形式出现的,所以每行末尾肯定有一个换行符
标准库提供了一次读写一个字符的函数:getchar和putchar。getchar从文本流中读入下一个输入字符。putchar则是打印一个字符,输出到屏幕。
char在机器内部都是用位来储存的。

方法:getchar()在没有输入符的时候,会返回EOF(end of file),如果getchar()==EOF,就说明没有输入了。我打印了一下EOF,发现是-1。EOF是定义在标准库中的一个整型数。

long 要占32位储存单元,%ld是打印long整型

C语言 单引号和双引号的区别 :单引号表示一个单个字符,而双引号是字符串。单引号中的字符实际上表示一个整型值,该整型值对应ASCII字符集中的字符常量。比如值65对应'A'字符常量。'\n'这个换行符对应值10.

nl=nw=nc=0;这样一句语句的赋值的实际过程是:nl=(nw=(nc=0));


练习题

Exercsise 1-6. Verify that the expression getchar() != EOF is 0 or 1.
答:
/* This program prompts for input, and then captures a character
 * from the keyboard. If EOF is signalled (typically through a
 * control-D or control-Z character, though not necessarily),
 * the program prints 0. Otherwise, it prints 1.
 *
 * If your input stream is buffered (and it probably is), then
 * you will need to press the ENTER key before the program will
 * respond.
 *
 * zy测试结果:
 * 如果我点回车的话,会返回1,那么就是说明回车输入的不是EOF
 * 但是当我输入control-D就回打印0,说明control-D会向服务器输出EOF
 * control-Z没有输出
 */

#include 

int main(void)
{
  printf("Press a key. ENTER would be nice :-)\n\n");
  printf("The expression getchar() != EOF evaluates to %d\n", getchar() != EOF);
  return 0;
}

Exercise 1-7. Write a program to print the value of EOF.
答:
#include 

int main(void)
{
  printf("The value of EOF is %d\n\n", EOF);

  return 0;
}
结果是-1

Exercise 1-8. Write a program to count blanks, tabs, and newlines.
答:
#include 

int main(void)
{
  int blanks, tabs, newlines;
  int c;
  int done = 0;
  int lastchar = 0;

  blanks = 0;
  tabs = 0;
  newlines = 0;

  while(done == 0)
  {
    c = getchar();

    if(c == ' ')
      ++blanks;

    if(c == '\t')
      ++tabs;

    if(c == '\n')
      ++newlines;

    if(c == EOF)
    {
      if(lastchar != '\n')
      {
        ++newlines; /* this is a bit of a semantic stretch, but it copes
                     * with implementations where a text file might not
                     * end with a newline. Thanks to Jim Stad for pointing
                     * this out.
                     */
      }
      done = 1;
    }
    lastchar = c;
  }

  printf("Blanks: %d\nTabs: %d\nLines: %d\n", blanks, tabs, newlines);
  return 0;
}

Exercise 1-9. Write a program to copy its input to its output, replacing each string of one or more blanks by a single blank.
答:
#include 

int main(void)
{
  int c;
  int inspace;

  inspace = 0;
  while((c = getchar()) != EOF)
  {
    if(c == ' ')
    {
      if(inspace == 0)
      {
        inspace = 1;
        putchar(c);
      }
    }

    /* We haven't met 'else' yet, so we have to be a little clumsy */
    if(c != ' ')
    {
      inspace = 0;
      putchar(c);
    }
  }

  return 0;
}


Exercise 1-10. Write a program to copy its input to its output, replacing each tab by \t, each backspace by \b, and each backslash by \\. This makes tabs and backspaces visible in an unambiguous way.
#include 

int main()
{
    int c, d;

    while ( (c=getchar()) != EOF) {
        d = 0;
        if (c == '\\') {
            putchar('\\');
            putchar('\\');
            d = 1;
        }
        if (c == '\t') {
            putchar('\\');
            putchar('t');
            d = 1;
        }
        if (c == '\b') {
            putchar('\\');
            putchar('b');
            d = 1;
        }
        if (d == 0)
            putchar(c);
    }
    return 0;
}

Exercise 1-11. How would you test the word count program? What kinds of input are most likely to uncover bugs if there are any?
code:
#include 
#define IN 1 //在一个单词内
#define OUT 0//在一个单词外
int main(void) {
	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);//ctrl-d发送EOF,打印结果。
}


答:
  • 0. input file contains zero words 
  • 1. input file contains 1 enormous word without any newlines 
  • 2. input file contains all white space without newlines 
  • 3. input file contains 66000 newlines 
  • 4. input file contains word/{huge sequence of whitespace of different kinds}/word 
  • 5. input file contains 66000 single letter words, 66 to the line 
  • 6. input file contains 66000 words without any newlines 
  • 7. input file is /usr/dict contents (or equivalent) 
  • 8. input file is full collection of moby words 
  • 9. input file is binary (e.g. its own executable) 
  • 10. input file is /dev/nul (or equivalent) 
66000 is chosen to check for integral overflow on small integer machines. 
译:
  1. 单词数为0的情况
  2. 一个非常长的单词,只有一行
  3. 全是空格的一行
  4. 66000行数据
  5. 含有word,然后大量tab、空格、回车,再一个word的形式
  6. 66000的单个字母,66000行
  7. 一行66000个单词
  8. input file is /usr/dict contents (or equivalent) 
  9. input file is full collection of moby words (moby不知是什么)
  10. 输入文件是二进制的
  11. input file is /dev/nul (or equivalent) 
在某些机子(small integer machines)上66000是整数溢出的边界。

Exercise 1-12. Write a program that prints its input one word per line.
答:
#include 
int main(void)
{
  int c;
  int inspace;

  inspace = 0;
  while((c = getchar()) != EOF)
  {
    if(c == ' ' || c == '\t' || c == '\n')
    {
      if(inspace == 0)
      {
        inspace = 1;
        putchar('\n');
      }
      /* else, don't print anything */
    }
    else
    {
      inspace = 0;
      putchar(c);
    }
  }
  return 0;
}

1.6 数组

练习题
Exercise 1-13. Write a program to print a histogram of the lengths of words in its input. It is easy to draw the histogram with the bars horizontal; a vertical orientation is more challenging.
答:
#include 

#define MAXWORDLEN 10

int main(void)
{
  int c;
  int inspace = 0;//每遇见一个空格设置为1,每遇见一个字符设置为0
  long lengtharr[MAXWORDLEN + 1];//以单词长度为下标的数组,>10的全部算作一个
  int wordlen = 0;

  int firstletter = 1;
  long thisval = 0;//这一次长度
  long maxval = 0;//单个单词的最大次数
  int thisidx = 0;
  int done = 0;

  for(thisidx = 0; thisidx <= MAXWORDLEN; thisidx++)
  {
    lengtharr[thisidx] = 0;
  }

  while(done == 0)
  {
    c = getchar();

    if(c == ' ' || c == '\t' || c == '\n' || c == EOF)
    {
      if(inspace == 0)
      {
        firstletter = 0;
        inspace = 1;

        if(wordlen <= MAXWORDLEN)
        {
          if(wordlen > 0)
          {
            thisval = ++lengtharr[wordlen - 1];
            if(thisval > maxval)
            {
              maxval = thisval;//如果
            }
          }
        }
        else
        {
          thisval = ++lengtharr[MAXWORDLEN];
          if(thisval > maxval)
          {
            maxval = thisval;
          }
        }
      }
      if(c == EOF)
      {
        done = 1;
      }
    }
    else//不是空格,\t,\n,EOF,就是拿到了一个实在字符,单词长度加1
    {
      if(inspace == 1 || firstletter == 1)
      {
        wordlen = 0;
        firstletter = 0;
        inspace = 0;
      }
      ++wordlen;
    }
  }

  for(thisval = maxval; thisval > 0; thisval--)
  {
    printf("%4ld  | ", thisval);
    for(thisidx = 0; thisidx <= MAXWORDLEN; thisidx++)
    {
      if(lengtharr[thisidx] >= thisval)
      {
        printf("*  ");
      }
      else
      {
        printf("   ");
      }
    }
    printf("\n");
  }
  printf("      +");
  for(thisidx = 0; thisidx <= MAXWORDLEN; thisidx++)
  {
    printf("---");
  }
  printf("\n       ");
  for(thisidx = 0; thisidx < MAXWORDLEN; thisidx++)
  {
    printf("%2d ", thisidx + 1);
  }
  printf(">%d\n", MAXWORDLEN);

  return 0;
}

结果(就用代码作为输入,得到的结果):
  78  | *                                
  77  | *                                
  76  | *                                
  75  | *                                
  74  | *                                
  73  | *                                
  72  | *                                
  71  | *                                
  70  | *                                
  69  | *                                
  68  | *                                
  67  | *                                
  66  | *                                
  65  | *                                
  64  | *                                
  63  | *                                
  62  | *                                
  61  | *                                
  60  | *                                
  59  | *                                
  58  | *                                
  57  | *                                
  56  | *                                
  55  | *                                
  54  | *                                
  53  | *                                
  52  | *                                
  51  | *                                
  50  | *                                
  49  | *                                
  48  | *                                
  47  | *                                
  46  | *                                
  45  | *                                
  44  | *                                
  43  | *  *                             
  42  | *  *                             
  41  | *  *                             
  40  | *  *                             
  39  | *  *                             
  38  | *  *                             
  37  | *  *                             
  36  | *  *                             
  35  | *  *                             
  34  | *  *                             
  33  | *  *                             
  32  | *  *                             
  31  | *  *                          *  
  30  | *  *                          *  
  29  | *  *                          *  
  28  | *  *                          *  
  27  | *  *                          *  
  26  | *  *                          *  
  25  | *  *                          *  
  24  | *  *                          *  
  23  | *  *                          *  
  22  | *  *                          *  
  21  | *  *                          *  
  20  | *  *                          *  
  19  | *  *              *           *  
  18  | *  *              *           *  
  17  | *  *              *        *  *  
  16  | *  *              *        *  *  
  15  | *  *              *        *  *  
  14  | *  *     *        *        *  *  
  13  | *  *     *        *        *  *  
  12  | *  *  *  *        *        *  *  
  11  | *  *  *  *        *        *  *  
  10  | *  *  *  *        *        *  *  
   9  | *  *  *  *        *        *  *  
   8  | *  *  *  *        *        *  *  
   7  | *  *  *  *        *        *  *  
   6  | *  *  *  *        *        *  *  
   5  | *  *  *  *        *  *     *  *  
   4  | *  *  *  *     *  *  *     *  *  
   3  | *  *  *  *     *  *  *  *  *  *  
   2  | *  *  *  *     *  *  *  *  *  *  
   1  | *  *  *  *     *  *  *  *  *  *  
      +---------------------------------
        1  2  3  4  5  6  7  8  9 10 >10
Exercise 1-14. Write a program to print a histogram of the frequencies of different characters in its input.
答:
注意是字符的频率,a算一个字符,'\n'也算一个字符。
从代码来看,字符使用ASCII的数值来表示,比如,010表示的就是'\n':
#include 

/* NUM_CHARS should really be CHAR_MAX but K&R haven't covered that at this stage in the book */
#define NUM_CHARS 256

int main(void)
{
  int c;
  long freqarr[NUM_CHARS + 1];

  long thisval = 0;
  long maxval = 0;
  int thisidx = 0;

  for(thisidx = 0; thisidx <= NUM_CHARS; thisidx++)
  {
    freqarr[thisidx] = 0;
  }

  while((c = getchar()) != EOF)
  {
    if(c < NUM_CHARS)
    {
      thisval = ++freqarr[c];//字符的ASCII的值作为数组下标
      if(thisval > maxval)
      {
        maxval = thisval;
      }
    }
    else
    {
      thisval = ++freqarr[NUM_CHARS];
      if(thisval > maxval)
      {
        maxval = thisval;
      }
    }
  }

  for(thisval = maxval; thisval > 0; thisval--)
  {
    printf("%4ld  |", thisval);
    for(thisidx = 0; thisidx <= NUM_CHARS; thisidx++)
    {
      if(freqarr[thisidx] >= thisval)
      {
        printf("*");
      }
      else if(freqarr[thisidx] > 0)
      {
        printf(" ");
      }
    }
    printf("\n");
  }
  printf("      +");
  for(thisidx = 0; thisidx <= NUM_CHARS; thisidx++)
  {
    if(freqarr[thisidx] > 0)
    {
      printf("-");
    }
  }
  printf("\n       ");
  for(thisidx = 0; thisidx < NUM_CHARS; thisidx++)
  {
    if(freqarr[thisidx] > 0)
    {
      printf("%d", thisidx / 100);//打印百位
    }
  }
  printf("\n       ");
  for(thisidx = 0; thisidx < NUM_CHARS; thisidx++)
  {
    if(freqarr[thisidx] > 0)
    {
      printf("%d", (thisidx - (100 * (thisidx / 100))) / 10 );//打印十位
    }
  }
  printf("\n       ");
  for(thisidx = 0; thisidx < NUM_CHARS; thisidx++)
  {
    if(freqarr[thisidx] > 0)
    {
      printf("%d", thisidx - (10 * (thisidx / 10)));//打印个位
    }
  }
  if(freqarr[NUM_CHARS] > 0)
  {
    printf(">%d\n", NUM_CHARS);
  }

  printf("\n");

  return 0;
}

输入:
what's your name? 
my name is zy.

结果:

   5  | *                 
   4  | *                 
   3  | *   *   *       * 
   2  |**   **  **  *   * 
   1  |*******************
      +-------------------
       0000001111111111111
       1334690000111111122
       0296371459014567912

1.7 函数

  1. 一个函数内的参数使用的名字是不会与其他函数的内使用的名字发生冲突的。
  2. 写在main函数之前的,或者一个file开始的一部分叫做函数原型。函数原型可以不写参数名,比如int power(int m, int n); 也可以写成int power(int,int);

练习题

Exercise 1.15. Rewrite the temperature conversion program of Section 1.2 to use a function for conversion.
#include 

float FtoC(float f)//这样的做法相当于把函数原型和函数定义写在一起了
{
  float c;
  c = (5.0 / 9.0) * (f - 32.0);
  return c;
}

int main(void)
{
  float fahr, celsius;
  int lower, upper, step;

  lower = 0;
  upper = 300;
  step = 20;

  printf("F     C\n\n");
  fahr = lower;
  while(fahr <= upper)
  {
    celsius = FtoC(fahr);
    printf("%3.0f %6.1f\n", fahr, celsius);
    fahr = fahr + step;
  }
  return 0;
}

1.8 参数和传值调用

C语言中被调用函数不能直接修改主调函数的值,而只能修改其私有的临时变量的值,说明其是值传递

如果想要函数修改主调函数的变量,那么调用这需要向被调用的函数提供带设置值的变量地址,也就是指针。

数组传递的是数组的起始位置的地址,所以,调用函数可以通过数组下标来访问和修改元素的值

1.9 字符数组

字符串是用char数组来储存的,并却最后的常量的会以“\0”来表示结束。如"hello\n",的储存如下图所示:

练习题

Exercise 1-16. Revise the main routine of the longest-line program so it will correctly print the length of
arbitrary long input lines, and as much as possible of the text.
注意:正确打印出长度,尽可能的打印出文本。
code:
#include 

#define MAXLINE 1000 /* maximum input line size */

int getline1(char line[], int maxline);
void copy(char to[], char from[]);

/* print longest input line */
int main(void)
{
  int len;               /* current line length */
  int max;               /* maximum length seen so far */
  char line[MAXLINE];    /* current input line */
  char longest[MAXLINE]; /* longest line saved here */

  max = 0;

  while((len = getline1(line, MAXLINE)) > 0)
  {
    printf("%d: %s", len, line);

    if(len > max)
    {
      max = len;
      copy(longest, line);
    }
  }
  if(max > 0)
  {
    printf("Longest is %d characters:\n%s", max, longest);
  }
  printf("\n");
  return 0;
}

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

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

/* copy: copy 'from' into 'to'; assume 'to' is big enough */
void copy(char to[], char from[])
{
  int i;

  i = 0;
  while((to[i] = from[i]) != '\0')
  {
    ++i;
  }
}
由于发生下列错误,我将getline()改为了getline1。
test.c:37:5: error: conflicting types for ‘getline’
/usr/include/stdio.h:675:20: note: previous declaration of ‘getline’ was here

Exercise 1-17. Write a program to print all input lines that are longer than 80 characters.
code:
#include 

#define MINLENGTH 81

int readbuff(char *buffer) {
    size_t i=0;
    int c;
    while (i < MINLENGTH) {
        c = getchar();
        if (c == EOF) return -1;
        if (c == '\n') return 0;
        buffer[i++] = c;
    }
    return 1;
}

void copyline(char *buffer) {
    size_t i;
    int c;
    for(i=0; i
注意:
  • 我对参考答案,进行了修改,暂时没发现bug
  • 我对参考答案的做法:如果某行大于80,打印,但是只打印前80行也不是特别满意。我觉得该打印超过80行的全部。
Exercise 1-18. Write a program to remove trailing blanks and tabs from each line of input, and to delete entirely blank lines.
code:
#include 
#include 

#define MAXQUEUE 1001

int advance(int pointer)
{
  if (pointer < MAXQUEUE - 1)
    return pointer + 1;
  else
    return 0;
}

int main(void)
{
  char blank[MAXQUEUE];//用于存储制表符或者是空格。
  int head, tail;
  int nonspace;
  int retval;
  int c;

  retval = nonspace = head = tail = 0;
  while ((c = getchar()) != EOF) {
    if (c == '\n') {
      head = tail = 0;
      if (nonspace)
        putchar('\n');
      nonspace = 0;
    }
    else if (c == ' ' || c == '\t') {
      if (advance(head) == tail) {//这里只是用head判断一下,并没实际让head+1
        putchar(blank[tail]);
        tail = advance(tail);
        nonspace = 1;
        retval = EXIT_FAILURE;
      }

      blank[head] = c;//将这个是 空格或者是\t存了起来
      head = advance(head);//这里是让head+1
    }
    else {
      while (head != tail) {
        putchar(blank[tail]);
        tail = advance(tail);
      }
      putchar(c);
      nonspace = 1;
    }
  }

  return retval;
}
刚开始比较难懂。但是举个例子按着逻辑一点一点的走,还是比较好懂。

Exercise 1-19. Write a function reverse(s) that reverses the character string s. Use it to write a program that reverses its input a line at a time.
#include 

#define MAX_LINE 1024

void discardnewline(char s[])
{
  int i;
  for(i = 0; s[i] != '\0'; i++)//注意getline里面把这里
  {
    if(s[i] == '\n')
      s[i] = '\0';
  }
}

int reverse(char s[])
{
  char ch;
  int i, j;

  for(j = 0; s[j] != '\0'; j++)
  {
  }

  --j;

  for(i = 0; i < j; i++)
  {
    ch   = s[i];
    s[i] = s[j];
    s[j] = ch;
    --j;
  }

  return 0;
}

int getline1(char s[], int lim)
{
  int c, i;

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

  if(c == '\n')
  {
    s[i++] = c;//这些++的顺序都很微妙,因为在for语句之中已经++过了
  }

  s[i] = '\0';//末尾加的这个discardnewline()函数有影响。

  return i;

}

int main(void)
{
  char line[MAX_LINE];

  while(getline1(line, sizeof line) > 0)
  {
    discardnewline(line);
    reverse(line);
    printf("%s\n", line);
  }
  return 0;
}


1.10 外部变量与作用域名

不同函数内,声明的变量是局部变量,函数执行完毕后会消失,对应的是外部变量(全局变量)。

外部变量一直存在,不会因为函数的调用而产生。函数对外部变量的值的改变也会影响。

全局变量的使用可以使用extern关键字来显式声明,在同一份文件下,这种声明可以省略,但是如果变量在file1里面声明,却在file2里面使用,那么必须使用extern关键字来声明。习惯会把变量和函数的extern声明放在头文件中,再包含头文件。

定义(define)是指,分配内存。外部变量定义在所有函数外,可以声明于函数内,声明只是说明函数性质。

不要频繁使用外部变量原因有2: 外部变量会在不知道的情况下被修改;函数会失去通用性。业就是,不能把函数

练习题

Exercise 1-20. Write a program detab that replaces tabs in the input with the proper number of blanks to space to the next tab stop. Assume a fixed set of tab stops, say every n columns. Should n be a variable or a symbolic parameter?
答:
code:
#include 
#include 
#include 

#define MAX_BUFFER   1024
#define SPACE        ' '
#define TAB          '\t'

int CalculateNumberOfSpaces(int Offset, int TabSize)
{
   return TabSize - (Offset % TabSize);
}

/* K&R's getline() function from p29 */
int getline1(char s[], int lim)
{
  int c, i;

  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;
}

int main(void)
{
  char  Buffer[MAX_BUFFER];
  int TabSize = 5; /* A good test value */

  int i, j, k, l;

  while(getline1(Buffer, MAX_BUFFER) > 0)
  {
    for(i = 0, l = 0; Buffer[i] != '\0'; i++)
    {
      if(Buffer[i] == TAB)
      {
        j = CalculateNumberOfSpaces(l, TabSize);
        for(k = 0; k < j; k++)
        {
          putchar(SPACE);
          l++;
        }
      }
      else
      {
        putchar(Buffer[i]);
        l++;
      }
    }
  }

  return 0;
}
很奇怪,我看懂了代码,我感觉带代码和题干不一样。题干中有一句:to the next tab stop(使空格充满到下一个制表符终止的位置),我始终没想通。看了代码,觉得代码也做了一些特别的处理。
  • return TabSize - (Offset % TabSize);
In answer to the question about whether n should be variable or symbolic, I'm tempted to offer the answer 'yes'. :-) Of course, it should be variable, to allow for modification of the value at runtime, for example via a command line argument, without requiring recompilation.

Exercise 1-21. Write a program entab that replaces strings of blanks by the minimum number of tabs and blanks to achieve the same spacing. Use the same tab stops as for detab. When either a tab or a single blank would suffice to reach a tab stop, which should be given preference?
答:
code:
/******************************************************
   KnR 1-21
   --------
   Write a program "entab" which replaces strings of
   blanks with the minimum number of tabs and blanks
   to achieve the same spacing.

   Author: Rick Dearman
   email: [email protected]

******************************************************/
#include 

#define MAXLINE 1000 /* max input line size */
#define TAB2SPACE 4 /* 4 spaces to a tab */

char line[MAXLINE]; /*current input line*/

int getline1(void);  /* taken from the KnR book. */


int
main()
{
  int i,t;
  int spacecount,len;

  while (( len = getline1()) > 0 )
    {
      spacecount = 0;
      for( i=0; i < len; i++)
	{
	  if(line[i] == ' ')
	    spacecount++; /* increment counter for each space */
	  if(line[i] != ' ')
	    spacecount = 0; /* reset counter */
	  if(spacecount == TAB2SPACE) /* Now we have enough spaces
				      ** to replace them with a tab
				      */
	    {
	      /* Because we are removing 4 spaces and
	      ** replacing them with 1 tab we move back
	      ** three chars and replace the ' ' with a \t
	      */
	      i -= 3; /* same as "i = i - 3" */
	      len -= 3;
	      line[i] = '\t';
	      /* Now move all the char's to the right into the
	      ** places we have removed.
	      ** 因为用了一个'\t'替换调用了一个4个空格,还剩于三个,所以,我们需要把元素从右往左移
	      */
	      for(t=i+1;t
关于问题的答案,我认为是制表符。

Exercise 1-22. Write a program to ``fold'' long input lines into two or more shorter lines after the last
non-blank character that occurs before the n-th column of input. Make sure your program does something intelligent with very long lines, and if there are no blanks or tabs before the specified column.
 /******************************************************
   KnR 1-22
   --------
   Write a program that wraps very long lines of input
   into two or more shorter lines.

   Author: Rick Dearman
   email: [email protected]

******************************************************/
#include 

#define MAXLINE 1000 /* max input line size */

char line[MAXLINE]; /*current input line*/

int getline1(void);  /* taken from the KnR book. */


int
main()
{
  int t,len;
  int location,spaceholder;
  const int FOLDLENGTH=70; /* The max length of a line */

  while (( len = getline1()) > 0 )
    {
      if( len < FOLDLENGTH )
	{
	}
      else
	{
	/* if this is an extra long line then we
	** loop through it replacing a space nearest
	** to the foldarea with a newline.
	*/
	  t = 0;
	  location = 0;
	  while(t

Exercise 1-23. Write a program to remove all comments from a C program. Don't forget to handle quoted strings and character constants properly. C comments don't nest.
答案:
code:
/* Lew Pitcher  */

/*/
** derem - remove C comments
**
** (attempt to solve K&R Exercise 1-22)
**
** As I only have v1 copy of K&R, I cannot
** be sure what is covered in K&R ANSI chapter 1.
** So, I restrict myself to the components covered
** in K&R v1 chapter 1, but modified for requisite ANSI
** features (int main() and return value).
**
** Components covered in v1 K&R chapter 1 include:
**  while (), for (), if () else
**  getchar(), putchar(), EOF
**  character constants, character escapes
**  strings
**  array subscripting
**
** Not directly covered are
**  string subscripting ( "/*"[0] )
**  initializers ( int state = PROGRAM; )
**/

/*/*/

#include 

#define	PROGRAM		0
#define	BEGIN_COMMENT	1
#define	COMMENT		2
#define	END_COMMENT	3
#define	QUOTE		4

int main(void)
{
	int this_char, quote_char;
	int state;

	state = PROGRAM;

	while ((this_char = getchar()) != EOF)
	{
		if (state == PROGRAM)
		{
			if (this_char == '/')
				state = BEGIN_COMMENT;
			else if ((this_char == '"') || (this_char == '\''))
			{
				state = QUOTE;//引号状态的出现是因为在引号了输入什么都不会是注释
				putchar(quote_char = this_char);
			}
			else	putchar(this_char);
		}
		else if (state == BEGIN_COMMENT)
		{
			if (this_char == '*')
				state = COMMENT;
			else
			{
				putchar('/'); /* for the '/' of the comment 如果我把这句删掉,就也能可以将//的注释删掉,但是我不知道为什么作业要这样做。*/
				if (this_char != '/')
				{
					state = PROGRAM;
					putchar(this_char);
				}
				else	state = COMMENT;	/* stuttered */
			}
		}
		else if (state == QUOTE)
		{
			putchar(this_char);
			if (this_char == '\\')//因为只有在引号里面才会出现转义字符撒
				putchar(getchar());	/* escaped character */
			else if (this_char == quote_char)
				state = PROGRAM;
		}
		else if (state == COMMENT)
		{
			if (this_char == '*')
				state = END_COMMENT;
		}
		else if (state == END_COMMENT)
		{
			if (this_char == '/')
				state = PROGRAM;
			else if (this_char != '*')	/* stuttered */
				state = COMMENT;
		}
	}

	return 0;
}



这只是一个版本,我简单测试没有发现bug,而且并不一定是最优的版本,更多,请参考: Answer to Exercise 1-23, page 34

Exercise 1-24. Write a program to check a C program for rudimentary syntax errors like unmatched
parentheses, brackets and braces. Don't forget about quotes, both single and double, escape sequences, and comments. (This program is hard if you do it in full generality.)
答案:
code:
/******************************************************
   KnR 1-24
   --------
   Write a program to check the syntax of a C program
   for matching {} () "" '' []

   Author: Rick Dearman
   email: [email protected]

******************************************************/
#include 

#define MAXLINE 1000 /* max input line size */
char line[MAXLINE]; /*current input line*/

int getline1(void);  /* taken from the KnR book. */


int
main()
{
  int len=0;
  int t=0;
  int brace=0, bracket=0, parenthesis=0;
  int s_quote=1, d_quote=1;//单引号和双引号是的匹配是:“”,‘’这样的,所以必须使用这种方式


  while ((len = getline1()) > 0 )
    {
      t=0;
      while(t < len)
	{
	  if( line[t] == '[')
	    {
	      brace++;
	    }
	  if( line[t] == ']')
	    {
	      brace--;
	    }
	  if( line[t] == '(')
	    {
	      parenthesis++;
	    }
	  if( line[t] == ')')
	    {
	      parenthesis--;
	    }
	  if( line[t] == '\'')
	    {
	      s_quote *= -1;
	    }
	  if( line[t] == '"')
	    {
	      d_quote *= -1;
	    }
	  t++;
	}
    }
  if(d_quote !=1)
    printf ("Mismatching double quote mark\n");
  if(s_quote !=1)
    printf ("Mismatching single quote mark\n");
  if(parenthesis != 0)
    printf ("Mismatching parenthesis\n");
  if(brace != 0)
    printf ("Mismatching brace mark\n");
  if(bracket != 0)
    printf ("Mismatching bracket mark\n");
  if( bracket==0 && brace==0 && parenthesis==0 && s_quote == 1 && d_quote == 1)
        printf ("Syntax appears to be correct.\n");
  return 0;
}


/* getline: specialized version */
int getline1(void)
{
  int c, i;
  extern char line[];

  for ( i=0;i
这是一个简单的版本,因为它不能应付这样的情况:
[)](
Syntax appears to be correct.
更多,请参考: Answer to Exercise 1-24, page 34


其它总结

linux下的ctrl加字母

摘自: ctrl+c,ctrl+d,ctrl+z在linux中意义(zz)  
  • ctrl-c 发送 SIGINT 信号给前台进程组中的所有进程。常用于终止正在运行的程序。
  • ctrl-z 发送 SIGTSTP 信号给前台进程组中的所有进程,常用于挂起一个进程。
  • ctrl-d 不是发送信号,而是表示一个特殊的二进制值,表示 EOF。
  • ctrl-\ 发送 SIGQUIT 信号给前台进程组中的所有进程,终止前台进程并生成 core 文件。


你可能感兴趣的:(C/C++)