c 程序由一系列文本文件组成,以文件为单位,经过预处理、编译链接生成可执行程序,由系统调用执行
c 语言中的实体
c 语言有如下实体:对象、函数、标签( struct 、 union 或枚举)、结构体或联合体成员、枚举常量、 typedef 类型别名、标号名、宏名、宏形参名。
实体是由声明所引入的,使其与名字对应起来,并定义了其属性。为一个实体定义其使用所需的所有性质的声明是一个定义。
什么是对象?
一个对象是执行环境中数据存储的一个区域,其内容可以表示值(值是对象的内容转译为特定类型时的含义)。
简单说,就是程序执行时,除了代码段,其他堆栈段、bss 段、数据段里面的都是对象
所以,以下实体都不是对象:值,引用,函数,枚举项
原因是以上这些实体只是在编译阶段使用,说白了就是写给编译器看的,函数直接生成进代码段,值会以二进制嵌入到代码中
如何判断?
对象类型:所有不是函数类型的类型
标识符声明和定义
声明是向程序中引入一个名字
定义是足以使用该名字所标识的实体
1 | // 对于对象,分配存储的声明为定义 |
include 预处理指令,会将包含的头文件插入到当前行
所以,头文件如果定义了全局变量,如果重复包含此头文件,会出现重定义,可使用#pragma once 或者宏定义避免一个头文件被多次包含
但,即使每个编译单元编译通过,链接时也会出现重复定义,原因是全局变量有外部链接
所以头文件可以放所有声明和内部链接的定义
标识符查找和命名空间
c 语言有 4 个独立的命名空间:
标号命名空间:跟在 goto 后的标识符,在标号命名空间查找
标签命名空间:跟在 struct、union、enum 后的标识符,在标签命名空间查找
类型成员命名空间:跟在成员访问运算符后的标识符,在类型成员命名空间查找
通常命名空间:函数名称,对象名,以 typedef 声明的标识符,枚举常量,所有其他标识符
4 个作用域:
文件作用域:任何在代码块之外声明的标识符都具有文件作用域
块作用域:任何位于一对花括号声明的标识符具有块作用域
函数作用域:只适用于函数内部的标号
函数原型作用域:只适用于在函数原型中声明的参数
1 | struct A { |
当 c 程序遇到标识符时,会在当前作用域查找定位引入该标识符,
对象类型
每个对象有如下属性:
作用域:标识符在某一范围内可见
命名空间:同一作用域同名标识符可属于不同命名空间,指代不同对象
链接:跨作用域指代同一对象的能力
存储期:用于限定对象的生存期
链接分为:
无链接:只能在其所在的作用域使用,函数形参和所有非 extern 的块作用域对象
内部链接:当前翻译单元的任何作用域都可使用,所有 static 的函数和对象
外部链接:在其他翻译单元可以使用的标识符,文件作用域下的非 static 函数和对象、extern 对象
存储类指定符:指定对象和函数的存储期和链接
auto - 自动存储期与无链接
register - 自动存储期与无链接;不能取这种对象的地址
static - 静态存储期与内部链接(除非在块作用域)
extern - 静态存储期与外部链接(除非已声明带内部链接)
_Thread_local - 线程存储期
若不提供存储类指定符,则默认为:
对所有函数为 extern
对在文件作用域的对象为 extern
对在块作用域的对象为 auto
结论:默认情况下
文件作用域定义得对象和函数:都是外部链接
类型
对象、函数和表达式拥有称为类型的属性,它确定存储于对象或表达式求值所得的二进制值的转译方式。
c 语言类型分为:
- void 类型
- 基本类型:char、int、long、double、float 等
- 派生类型:数组、结构体、联合体、函数、指针、原子类型
- 枚举类型
表达式
表达式是运算符及其运算数的序列,它指定一个运算
表达式求值可得到一个结果,分为:左值、右值和函数指代器
某些类型的常量值可常量或字面量,直接嵌入程序中
- 整数常量
- 浮点常量
- 字符常量
- 字符串字面量:
- 复合字面量:结构体、联合体或数组的无名对象,( type ) { initializer-list }
1 | int *p = (int[]){2, 4}; // 创建一个无名的 int[2] 类型静态存储数组 |
声明和定义
声明是引入一个标识符到程序中,并指定其属性
格式:specifiers-and-qualifiers declarators-and-initializers ;
specifiers-and-qualifiers:空白符分割,类型指定符,类型限定符,存储类指定符,对齐指定符,函数指定符
declarators-and-initializers:逗号分割,声明器和初始化器
声明器格式:
1 | 1. 标识符:identifier |
初始化器格式:
1 | 1. = expression |
三种显示初始化器:
标量类型初始化:包括整型类型,浮点类型,指针类型等
数组初始化:2 种形式
1 | 1. = string_literal 字符串字面量,初始化字符数组 |
结构体及联合体类型初始化:
1 | // . member 形式的单独成员指代器,和 [ index ] 形式的数组指代器。 |