《C和指针》第三章学习摘要

来源:互联网 发布:软件系统测试报告模板 编辑:程序博客网 时间:2024/05/17 06:40

Chapter 3

3.1 基本数据类型

在C语言中,仅有4种基本数据类型-整型、浮点型、指针和聚合类型。

3.1.1 整型家族

  • 包括 字符、短整型、整型和长整型,它们均分为有符号(signed)无符号(unsighed)
  • ANSI C规定:long int 至少和int一样长,int至少和short int一样长。

3.1.2 浮点类型

  • 浮点数字面值在缺省情况下都是double类型的,除非它的后面跟一个L或l表示long double,或者跟一个F或f表示它是一个float类型的值

3.1.3 指针

  • 每个内存位置都由地址唯一确定并引用,指针变量就是一个其值为另外一个数据内存地址的变量。
  • C语言中字符串就是一串以NUL字节结尾的零个或多个字符,之所以选择NUL作为终止符,是因为NUL不可打印。
  • “”是空字符串(也有NUL),”不是空字符而是非法的

3.2 基本声明

  • char缺省情况下可能是unsigned也可能是signed,但是其他整型类型缺省情况下均是signed
  • 编译器并不检查程序对数组下标的引用是否在数组的合法范围之内
  • char *a = “Hello”这个定义实际上是定义一个指向char类型的指针变量a,并且该变量指向“Hello”第一个字符的地址
  • 函数如果不显式地声明返回值的类型,它就默认返回整型

3.3 typedef

  • 功能:为数据类型定义新名字
  • 举一个指针声明的例子:
typedef char *ptr_to_char;ptr_to_char a;

在这个例子中,ptr_to_char作为指向字符的指针类型的新名字。

3.4 常量

  • int const 和 const int是等同的。
  • 涉及指针的const用法比较灵活
int *p;

p是一个普通的指向整型的指针

int const *p;

p是一个指向整型常量的指针,你可以修改指针的值,但你不能修改它所指向的值

int *const p;

p是一个指向整型的常量指针,你可以修改它所指向的值,但你不能修改指针的值

int const * const p;

p是一个指向整型常量的常量指针,两个你都不能修改
- 在使用特殊含义的字面值,我们建议使用#define来定义

3.5 作用域(Scope)

  • 标识符的作用域就是程序中该标识符可以被使用的区域。
  • 编译器可以确认4种不同类型的作用域——文件作用域、函数作用域、代码块作用域和原型作用域。

3.5.1 代码块作用域

  • 位于一对花括号之间的所有语句称为一个代码块
  • 位于代码块开头定义的变量和函数的形参都具有代码块作用域
  • 内层同名变量会屏蔽外层同名变量,同作用域的变量不能同名
  • 两个非嵌套的代码块最多只有一个处于活动状态

3.5.2 文件作用域

  • 任何在所有代码块之外声明的标识符都具有文件作用域,它表示这些标识符从它们的声明之处直到它所在的源文件结尾处都是可以访问的。
  • 在文件中定义的函数名也具有文件作用域,因为函数名本身并不属于任何代码块。
  • 应该指出,在头文件中编写并利用#include指令包含到其他文件中的声明就好像它们是直接写在那些文件中一样,它们的作用域并不局限于头文件的结尾。

3.5.3 原型作用域

  • 原型作用域只适用于在函数原型中声明的参数名,并且参数名也不是必需的。
  • 事实上,唯一可能出现的冲突就是在同一个原型中不止一次地使用同一个名字。

3.5.4 函数作用域

  • 函数作用域只适用于语句标签,即一个函数中的所有语句标签必须唯一。

3.6 链接属性

  • 标识符的链接属性决定如何处理在不同文件中出现的标识符。
  • 链接属性共有三种——external(外部)、internal(内部)和none(无)。
  • none:该属性的多个标识符被当作独立不同的实体。
  • internal:该属性的标识符在同一个源文件内的所有声明中都指同一个实体,但位于不同源文件的多个声明则分属不同的实体。
  • external:该属性的标识符不论声明多少次,位于几个源文件都表示同一个实体。
    typedef char *a;    int b;    int c(int d)    {        int e;        int f(int g)        ...    }
  • 这段程序中,在缺省情况下,b、c、f链接属性都是external,其余标识符的链接属性均为none。
  • 关键字extern和static用于在声明中修改标识符的链接属性,如果某个声明在缺省情况下具有external链接属性,在它前面加上static关键字可以使它的链接属性变为internal
    注意,static只能修改external的变量,对none无用(效果不一样)
  • extern关键字,一般而言,它为一个标识符指定external链接属性,这样就可以访问在其他任何位置定义的这个实体。
    static int i;    int func()    {        int j;        extern int k;        extern int i;        ...    }
  • 当extern关键字用于源文件中一个标识符的第一次声明时,它是有用的,但是它用于第二次或以后的声明时,它并没有用。例如上段程序中,i仍然是internal链接属性。

3.7 存储类型

  • 变量的存储类型(storage class)是指存储变量值的内存类型。
  • 变量的存储类型决定变量何时创建、何时销毁。
  • 有三个地方用于存储变量:普通内存、运行时堆栈、硬件寄存器。
  • 变量的缺省存储类型取决于它的声明位置。
  • 凡是在任何代码块之外声明的变量总是存储于静态内存中,也就是不属于堆栈的内存,这类变量称为静态(static)变量。对于这类变量,你无法为它指定其他存储类型。静态变量在程序 运行之前创建,在程序的整个执行期间始终存在,它始终保持原先的值,除非赋值或程序结束。
  • 在代码块内部声明的变量的缺省类型时自动(automatic),也就是说存储于堆栈中,称为自动(auto)变量,有一个关键字auto就是用于修饰这种类型。对于自动变量,在程序运行到声 明自动变量的代码块时才被创建,当程序离开该代码块时,这些自动变量自行销毁。在代码块中,这些自动变量在堆栈中所占据的内存位置有可能和原先的相同,也可能不相同。
  • 对于自动变量,加上static,可以使它的存储类型从自动变为静态。
  • 注意,修改变量的存储类型并不表示修改该变量的作用域。
  • 函数的形参不能声明为静态,因为实参总是在堆栈中传递给函数,用于支持递归。
  • 关键字register可以用于自动变量的声明,提示它们应该存储在机器的硬件寄存器,这类变量称为寄存器变量。
  • 通常,寄存器变量比存储于内存的变量访问起来效率更高,但是编译器并不一定要理睬register关键字,如果有太多的寄存器变量,它秩认定前几个,后面的仍当作普通变量,如果一个编 译器自己有一套寄存器优化方案,它也可能忽略。
  • 寄存器变量的创建和销毁时间和自动变量相同,但它需要一些额外的工作,在一个使用寄存器变量的函数返回之前,这些寄存器先前存储的值必须恢复,确保调用者的寄存器变量未被破坏。
  • 初始化:自动变量和静态变量的初始化差别很大。静态变量缺省为0,寄存器和自动变量不确定。

3.8 static关键字的总结

  • 当它用于函数名定义或用于代码块之外的变量声明时,static关键字用于修改标识符的链接属性,从external改为internal,但标识符的存储类型和作用域不受影响,用这种方式声明的函数或变量只能在声明它们的源文件中访问。
  • 当它用于代码块内部的变量声明时,static关键字用于修改变量的存储类型,从自动变量修改为静态变量,但变量的链接属性和作用域不受影响。用这种方式声明的变量在程序执行之前创建,并在程序的整个执行期间一直存在,而不是每次在代码块开始执行时创建,在代码块执行完毕后销毁。

3.10 总结

  • 具有external链接属性的实体在其他语言的术语称为全局(global)实体。
  • 只要变量并非声明于代码块或者函数定义内部,它在缺省情况下就是external。
  • 具有external链接属性的实体总是具有静态存储类型。
变量类型 声明的位置 是否存于堆栈 作用域 如果声明为static 全局 所有代码块之外 否 从声明处到文件尾 不允许从其他源文件访问 局部 代码块起始处 是 整个代码块 变量不存储于堆栈中,它的值在程序整个执行期一直保持 形式参数 函数头部 是 整个函数 不允许从其他源文件访问
1 0
原创粉丝点击