《The C programming language》学习笔记

时间:2021-04-29 16:04:39

Brian和Dennis的《c程序设计语言》(The C Programming Language)真不愧为c语言方面的经典书籍,薄薄的一本,却让人爱不释手,每次阅读都能有所收获。好记忆不如烂笔头,有些规则、算法还是记录下来比较好,记录下来常常回顾,才是提升编程内功的不二法门。

变量与表达式

1 整数除法操作将执行舍位。

2 浮点常量取的是整数,在书写时最好还是为它加上一个显示的小数点,这样可以强调其浮点信息,便于阅读。

3 在允许使用某种类型变量值的任何场合,都可以使用该类型的更复杂的表达式。

for语句

4 for语句比较适合初始化和增长步长都是单条语句并且逻辑相关的情形,因为它将循环控制语句集中放在一起,且比while语句更紧凑。

符号常量

5 #define指令可以把符号常量定义为一个特点的字符串:

#define 名字   替换文本

6 符号常量名通常用大写字母拼音,这样可以很容易和小写字母拼音的变量名区别

7 #define指令行的末尾没有分号。

字符输入\输出

8 文本流是由多行字符构成的字符序列,而每行字符则由0个或多个字符组成,行末是一个换行符。标准库负责使每个输入输出流都能够遵循这一模型。

9 文件复制10 任何整型( int )也可以用于存储字符型数据。

  void main()
   {
       int c;
       c = getchar(); /*Each time it is called, getchar reads the next input 
                   character from a text stream and returns that as its value*/
       while (c != EOF) {
           putchar(c);
           c = getchar();
       }
   }

11 EOF定义在头文件<stdio.h>中,是一个整型数。其具体数值并不重要,它与任何char型的值都不相同。

12 赋值可以作为更大的表达式的一部分出现。

   #include<stdio.h>
   /* copy input tooutput; 2nd version  */
   void main()
   {
       int c;
       while ((c =getchar()) != EOF)
          putchar(c);
   }

13 for循环改写

    #include <stdio.h>
 
   /* count characters in input; 2nd version */
    void main()
   {
       double nc;
 
       for (nc = 0; gechar() != EOF; ++nc)
           ;
       printf("%.0f\n", nc);/*%.0f强制不打印小数点和小数部分*/
   }

14 行计数

   /*程序虽然简单,但是确实是经典!*/
   #include <stdio.h>
   /* count lines in input */
    void main()
   {
       int c, nl;
       nl = 0;
       while ((c = getchar()) != EOF)
           if (c == '\n')
               ++nl;
       printf("%d\n", nl);
   }
15 单词计数
   #include <stdio.h>
   /*常量,让程序更易读*/
   #define IN   1  /* 在单词内 */
   #define OUT  0  /* 在单词外 */
   /* 统计输入的行数、单词数与字符数 */
   void main()
   {
       int c, nl, nw, nc, state;
       state = OUT;/*state记录程序当前是否位于一个单词之中*/
       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);
   }

16 &&比||高一个优先级。由&&或||连接的表达式由左至右求值,并保证求值过程中只要能够判断最终的结果为真或假,求值就立即终止。

数组

17 统计各个数字、空白符(包括空格、制表符及换行符)及其他字符出现的次数

    #include <stdio.h>
    void main()
   {
       int c, i, nwhite, nother;
       int ndigit[10];
       nwhite = nother = 0;
       for (i = 0; i < 10; ++i)
           ndigit[i] = 0;


       while ((c = getchar()) != EOF)
           if (c >= '0' && c <= '9')
               ++ndigit[c-'0'];
           else if (c == ' ' || c == '\n' || c == '\t')
               ++nwhite;
           else
               ++nother;


       printf("digits =");
       for (i = 0; i < 10; ++i)
           printf(" %d", ndigit[i]);
       printf(", white space = %d, other = %d\n",
           nwhite, nother);
   }

函数

18 C语言中,被调函数不能直接修改主调函数中变量的值,只能修改其私有临时副本的值。
字符数组
19 读入一行文本并把最长的文本打印出来。
基本框架:
   while (there's another line)
       if (it's longer than the previous longest)
           (save it)
           (save its length)
   print longest line
   #include <stdio.h>
   #define MAXLINE 1000   /* maximum input line length */
   int getline(char line[], int maxline);
   void copy(char to[], char from[]);
   /* print the longest input line */
   void main()
   {
       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 = getline(line, MAXLINE)) > 0)
           if (len > max) {
               max = len;
               copy(longest, line);
           }
       if (max > 0)  /* there was a line */
           printf("%s", longest);
       return 0;
   }
   /* getline:  read a line into s, return length  */
   int getline(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;
   }
   /* 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;
   }

外部变量与作用域

20 在每个需要访问外部变量的函数中,必须声明相应的外部变量,此时说明其类型。声明可以用extern语句显式声明,也可以通过上下文隐式声明。
   #include <stdio.h>
   #define MAXLINE 1000    /* maximum input line size */
   int max;                /* maximum length seen so far */
   char line[MAXLINE];     /* current input line */
   char longest[MAXLINE];  /* longest line saved here */
   int getline(void);
   void copy(void);
   /* print longest input line; specialized version */
   void main()
   {
       int len;
       extern int max;
       extern char longest[];
       max = 0;
       while ((len = getline()) > 0)
           if (len > max) {
               max = len;
               copy();
           }
       if (max > 0)  /* there was a line */
           printf("%s", longest);
       return 0;
   }
   /* getline:  specialized version */
   int getline(void)
   {
       int c, i;
       extern char line[];
       for (i = 0; i < MAXLINE - 1
            && (c=getchar)) != EOF && c != '\n'; ++i)
                line[i] = c;
       if (c == '\n') {
           line[i] = c;
           ++i;
       }
       line[i] = '\0';
       return i;
   }
   /* copy: specialized version */
   void copy(void)
   {
       int i;
       extern char line[], longest[];
       i = 0;
       while ((longest[i] = line[i]) != '\0')
           ++i;
   }

21 在源文件中,如果外部变量的定义出现在使用它的函数之前,那么那个函数就没有必要使用extern声明。因此,main,getline,copy中的几个extern声明都是多余的。

常量

    字符常量'\0'表示值为0的字符。也就是空字符(null)。我们通常用'\0'的形式代替0,以强调某些表达式的字符属性,但其数字值为0。

    字符串常量就是字符数组。字符串的内部表示使用一个空字符'\0’作为串的结尾,因此。存储字符串的物理存储单元数比括在双引号中的字符数多一个。

   /* strlen:  return length of s */
   int strlen(chars[])
   {
       int i;
       while (s[i]!= '\0')
           ++i;
       return i;
   }

    我们应该搞清楚字符常量与仅包含一个字符的字符串之间的区别:'x‘与”x“是不同的。前者是一个整数,其值是字母x在机器字符集中对应的数值(内部表示值);后者是一个包含一个字符(即字母x)以及一个结束符‘\0’的字符数组。

    在没有显式说明的情况下,enum类型中第一个枚举名的值为0,第二个为1依此类推。如果只指定了部分枚举名的值,那么未指定值的枚举名的值将依着最后一个指定值向后递增。 

    枚举为建立常量值与名字之问的关联提供了一种便利的方式。相对于#define语句来说,它的优势在于常量值可以自动牛成。

类型转换

    atoi()函数将一串数字转换为相应的数值。

  /* atoi:  convert s to integer */
   int atoi(char s[])
   {
       int i, n;
       n = 0;
       for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i)
           n = 10 * n + (s[i] - '0');
       return n;
   }

    函数lower是将char类型转换为int类型的另个例子,它将ASCII字符集中的字符映射到对应的小写字母。如果待转换的字符不是大写字母,lower函数将返回字符本身。

   /* lower:  convert c to lower case; ASCII only */
   int lower(int c)
   {
       if (c >= 'A' && c <= 'Z')
           return c + 'a' - 'A';
       else
           return c;
   }

自增运算符与自减运算符

    下面的函数squeeze(s, c),它删除字符串s中出现的所有字符c:

   /* squeeze:  delete all c from s */
   void squeeze(char s[], int c)
   {
      int i, j;
      for (i = j = 0; s[i] != '\0'; i++)
          if (s[i] != c)
              s[j++] = s[i];
      s[j] = '\0';
   }

   标准函数strcat(s,t),它将字符串t连接到字符串s的尾部。

   /* strcat:  concatenate t to end of s; s must be big enough */
   void strcat(char s[], char t[])
   {
       int i, j;
       i = j = 0;
       while (s[i] != '\0') /* find end of s */
           i++;
       while ((s[i++] = t[j++]) != '\0') /* copy t */
           ;
   }

    函数getbits(x,p,n),它返回x中从右边数第p位开始向右数n位的字段。这里假定最右边的一位是第O位,n与p都是合理的正值。

  /* getbits:  get n bits from position p */
   unsigned getbits(unsigned x, int p, int n)
   {
       return (x >> (p+1-n)) & ~(~0 << n);
   }
  函数bitcount统计其整型参数的值为1的二进制位的个数。

   /* bitcount:  count 1 bits in x */
   int bitcount(unsigned x)
   {
       int b;
       for (b = 0; x != 0; x >>= 1)
           if (x & 01)
               b++;
       return b;
   }

 运算符优先级与求值次序

运算符
结合性 
() [] -> .
 从左到右
! ~ ++ -- + - * (type) sizeof
 从右到左
* / % 
 从左到右
+ - 
 从左到右
<<  >> 
 从左到右
< <= > >= 
 从左到右
== != 
 从左到右
& 
 从左到右
^ 
 从左到右
| 
 从左到右
&&
 从左到右
|| 
 从左到右
?: 
 从右到左
= += -= *= /= %= &= ^= |= <<= >>=
 从右到左
, 
 从左到右

经典代码

1 折半查找

   /* binsearch:  find x in v[0] <= v[1] <= ... <= v[n-1] */
   int binsearch(int x, int v[], int n)
   {
       int low, high, mid;
       low = 0;
       high = n - 1;
       while (low <= high) {
           mid = (low+high)/2;
           if (x < v[mid])
               high = mid - 1;
           else if (x  > v[mid])
               low = mid + 1;
           else    /* found match */
               return mid;
       }
       return -1;   /* no match */
   }
2 将字符串转化为对应数值
   #include <ctype.h>
   /* atoi:  convert s to integer; version 2 */
   int atoi(char s[])
   {
       int i, n, sign;
       for (i = 0; isspace(s[i]); i++)  /* skip white space */
           ;
       sign = (s[i] == '-') ? -1 : 1;
       if (s[i] == '+' || s[i] == '-')  /* skip sign */
           i++;
       for (n = 0; isdigit(s[i]); i++)
           n = 10 * n + (s[i] - '0');
       return sign * n;
   }
3 Shell排序
Shell排序算法是D. L. Shell于1959年发明的,其基本思想是:先比较距离远的元素,而不是像简单交换排序算法那样先比较相邻的元素。这样可以快速减少大量的无序情况,从而减轻后续的工作。被比较的元素之间的距离逐步减少,直到减少为1,这时排序变成了相邻元素的互换。
   /* shellsort:  sort v[0]...v[n-1] into increasing order */
   void shellsort(int v[], int n)
   {
       int gap, i, j, temp;
       for (gap = n/2; gap > 0; gap /= 2)
           for (i = gap; i < n; i++)
               for (j=i-gap; j>=0 && v[j]>v[j+gap]; j-=gap) {
                   temp = v[j];
                   v[j] = v[j+gap];
                   v[j+gap] = temp;
               }
   }
4 倒置字符串s中各个字符的位置
   #include <string.h>
   /* reverse:  reverse string s in place */
   void reverse(char s[])
   {
       int c, i, j;
       for (i = 0, j = strlen(s)-1; i < j; i++, j--) {
           c = s[i];
           s[i] = s[j];
           s[j] = c;
       }
   }
5 将数字转换为字符串
   /* itoa:  convert n to characters in s */
   void itoa(int n, char s[])
   {
       int i, sign;
       if ((sign = n) < 0)  /* record sign */
           n = -n;          /* make n positive */
       i = 0;
       do {      /* generate digits in reverse order */
           s[i++] = n % 10 + '0';  /* get next digit */
       } while ((n /= 10) > 0);    /* delete it */
       if (sign < 0)
           s[i++] = '-';
       s[i] = '\0';
       reverse(s);
   }
6 删除字符串尾部的空格符、制表符与换行符
   /* trim:  remove trailing blanks, tabs, newlines */
   int trim(char s[])
   {
       int n;
       for (n = strlen(s)-1; n >= 0; n--)
           if (s[n] != ' ' && s[n] != '\t' && s[n] != '\n')
               break;
       s[n+1] = '\0';
       return n;
   }

知识补充

1 static类型的内部变量是一种只能在某个特定函数中使用但一直占据存储空间的变量。
2 在不进行显式初始化的情况下,外部变量和静态变量都将被初始化为0,面自动变量和寄存器变量的初值则没有定义(即初值为无用的信息)。
3 打印出数字
   #include <stdio.h>
   /* printd:  print n in decimal */
   void printd(int n)
   {
       if (n < 0) {
           putchar('-');
           n = -n;
       }
       if (n / 10)
           printd(n / 10);
       putchar(n % 10 + '0');
   }
4 快速排序
   /* qsort:  sort v[left]...v[right] into increasing order */
   void qsort(int v[], int left, int right)
   {
       int i, last;
       void swap(int v[], int i, int j);
       if (left >= right) /* do nothing if array contains */
           return;        /* fewer than two elements */
       swap(v, left, (left + right)/2); /* move partition elem */
       last = left;                     /* to v[0] */
       for (i = left + 1; i <= right; i++)  /* partition */
           if (v[i] < v[left])
               swap(v, ++last, i);
       swap(v, left, last);            /* restore partition  elem */
       qsort(v, left, last-1);
       qsort(v, last+1, right);
   }
   /* swap:  interchange v[i] and v[j] */
   void swap(int v[], int i, int j)
   {
       int temp;


       temp = v[i];
       v[i] = v[j];
       v[j] = temp;
   }
5 宏替换
  #define  max(A, B)  ((A) > (B) ? (A) : (B))
6 保证hdr.h文件的内容只被包含一次
   #ifndef HDR
   #define HDR

   /* contents of hdr.h go here */

   #endif
7 比较字符串大小
   /* strcmp:  return <0 if s<t, 0 if s==t, >0 if s>t */
   int strcmp(char *s, char *t)
   {
       int i;
       for (i = 0; s[i] == t[i]; i++)
           if (s[i] == '\0')
               return 0;
       return s[i] - t[i];
   }
8 类型名
   int                   整型
   int *                 指向整型的指针
   int *[3]              包含3个指向整型的指针的数组
   int (*)[]             指向未指定元素个数的整型数组的指针
   int *()               未指定参数、返回指向整型的指针的函数
   int (*[])(void)       一个数组,其长度未指定,数组的元素为指向函数的指针,该函数没有参数且返回一个整型值
转载请注明出处: http://blog.csdn.net/lsh_2013/article/details/45064991