《C和指针》——C语言补漏(基本用法篇)

《C和指针》——Kenneth A.Reek

这本书应该是我最后一次买盗版的经典书籍了,买了这本书后我就写下了学生应尽量购买正版,发誓以后都在正规的书店(包括网上书店)买书。

本来以为这本书的内容挺少的,但当读到指针的部分发现我以前了解的真的很少,还好我上学期认真学过《数据结构》这门课,对指针的使用还算熟练,不然光靠以前自学的谭浩强的C语言和课堂上教的,根本就只能算是一个只知道C语言这个东西的人。

至于这本书的阅读时间,我觉得我的时间并不正确,这本书应该是在C语言基础学完后,在数据结构之前来学习,不仅可以深入理解一下C语言的精髓,还可以顺利过渡到数据结构的学习。

在这里就以前我不了解的一些C语言知识做一些归纳(不过这里很多都是一些以前没见过的函数以及一些用法,当然这些函数或者是用法并不是罕见的,只是在学生中比较罕见罢了,我在网上看过很多高手的代码,发现有一些还是挺常用的)。

  1. 要从逻辑上删除一段C代码,更好的办法是使用#if指令。这样可以防止在一段内部已经有注释的代码外面加上注释所引起的注释不匹配的问题:

    1
    2
    3
    #if 0
    statements
    #endif
  2. EOF是一个整型值,在很多读入字符的情况下,如果要判断输入是否结束,需要把字符读入为整型值,这样可以判断输入是否结束。

  3. 程序会使用一个运行时堆栈,它用于存储函数的局部变量和返回地址。程序同时也可以使用静态内存,存储与静态内存中的变量在程序的整个执行过程中将一直保留着他们的值。

  4. 标准规定了几个特殊的三字母词,比如??(代表[,不过这个应该很少见,至少我之前一直没碰到这种情况

  5. 如果要创建新的类名,应该使用typedef而不是#define,因为后者无法正确处理指针类型,例如:

    1
    2
    #define ptr char*
    ptr a,b;

    上面代码会使a成为一个char指针,而b却被声明为一个char

  6. 在定义常量的时候我们应该使用#define而不是const:

    1
    2
    #define MAX_ELEMENTS 50
    int const max_elements 50;

    因为只要允许使用字面值的地方都可以使用前者,比如声明数组的长度,const变量只能用于允许使用变量的地方。

  7. 链接属性:分为3种——external(外部)、internal(内部)和none(无)。没有链接属性的标识符(none)总是被当作单独的个体,也就是说该标识符被当作独立不同的实体。属于internal链接属性的标识符在同一个源文件内的所有声明都指同一个实体,但位于不同源文件的多个声明则分属不同的实体。最后,属于external链接属性的标识符不论声明多少次,位于几个源文件都表示同一个实体。

  8. 凡是在任何代码块之外声明的变量总是存储于静态内存中,也就是不属于堆栈的内存,这类变量称为静态变量。

  9. 标准说明无符号值的所有移位操作都是逻辑移位,但对于有符号值,到底是采用逻辑移位还是算术移位取决于编译器。也就是说一个程序如果所使用了有符号数的移位操作,他就是不可移植的。

  10. C函数可以理解为所有的参数均以“传值调用”的方式进行传递,传递数组时也传递的是指针的值。

  11. 只要有可能,函数的指针形参都应该声明为const

  12. 不要使用strlen来相减,因为strlen是无符号数,那么相减的结果必然也是无符号数,所以像if( strlen(x) - strlen(y)= 0 )这种用法就是错的,因为结果永远都是大于等于零

  13. 注重可移植的程序应该避免使用位段。任何可以用位段实现的任务都可以使用移位和屏蔽来实现。

  14. 编译器为一个结构变量的成员分配内存时要满足他们的边界对齐要求。在实现结构存储的边界对齐时,可能会浪费一部分内存空间。根据边界对齐要求降序排列结构成员可以最大限度地减少结构存储中浪费的内存空间。

  15. 在程序中,数组的长度一般是根据运行后决定的,所以最好使用动态内存分配。

  16. 几个预定义符号:
    FILE: 进行编译的源文件名
    LINE: 文件当前行的行号
    DATE: 文件被编译的日期
    TIME: 文件被编译的时间
    STDC: 如果编译器遵循ANSI C,其值就为1,否则未定义

  17. 如果相同的代码需要出现在程序的几个地方,通常的做法是将它实现为一个函数,而不是宏#define

  18. 条件编译:可以允许选择某条语句或某组语句进行翻译或者被忽略,这常用语本地调试:

    1
    2
    3
    #if constant-expression
    statements
    #endif
  19. 文件包含:如果是库文件一般用<>,如果是本地文件一般用””

  20. 删除多重包含引发的危险:

    1
    2
    3
    4
    5
    6
    #ifndef _HEADERNAME_H
    #define _HEADERNAME_H 1
    /*
    ** All the stuff that you want in the header file
    */
    #endif
  21. 绝大多数流是完全缓冲的,意味着“读取”和“写入”实际上是从一块被称为缓冲区的内存区域来回赋值数据。用于输出流的缓冲区只有当它写满时才会被刷新到设备或文件中。也可使用fflush迫使缓冲区的数据立即写入,不管它是否已满。

  22. 函数库为I/O所提供的接口称为流。

  23. fgets函数比gets函数更为安全,它把缓冲区长度作为参数之一,可以保证一个长输入行不会溢出缓冲区,gets函数会去除它所读取的行的换行符,puts函数在写入到缓冲区的文本后面加上一个换行符。