系列文章目录
1、C语言综述 2、C语言字符集 3、C语言词法元素
前言
C语言是一门面向过程的、抽象化的通用程序设计语言,广泛应用于底层开发
1、概述
- 翻译3~6阶段为预处理词法元素,7阶段开始为词法元素
若直至某个给定字符为止的输入流字符序列已被分析为预处理单词,则下一个预处理单词是可构成预处理单词的最长的字符序列 ,比如, x ++ + ++y 是不符合定义的,x++已被分析为预处理单词,后面的字符构成不了预处理单词
#include<bits/stdc++.h>
using namespace std;
int main(int argc,char *argv[]){
int x = 0, y = 0, c;
c = x ++ + ++ y;
cout << x << endl << y << endl << c;
return 0;
}
2、关键字
关键字 | 说明 |
---|
auto | 声明自动变量 | break | 跳出当前循环 | case | 开关语句分支 | char | 声明字符型变量或函数返回值类型 | const | 定义常量,如果一个变量被 const 修饰,那么它的值就不能再被改变 | continue | 结束当前循环,开始下一轮循环 | default | 开关语句中的"其它"分支 | do | 循环语句的循环体 | double | 声明双精度浮点型变量或函数返回值类型 | else | 条件语句否定分支(与 if 连用) | enum | 声明枚举类型 | extern | 声明变量或函数是在其它文件或本文件的其他位置定义 | float | 声明浮点型变量或函数返回值类型 | for | 一种循环语句 | goto | 无条件跳转语句 | if | 条件语句 | int | 声明整型变量或函数 | long | 声明长整型变量或函数返回值类型 | register | 声明寄存器变量 | return | 子程序返回语句(可以带参数,也可不带参数) | short | 声明短整型变量或函数 | signed | 声明有符号类型变量或函数 | sizeof | 计算数据类型或变量长度(即所占字节数) | static | 声明静态变量 | struct | 声明结构体类型 | switch | 用于开关语句 | typedef | 用以给数据类型取别名 | unsigned | 声明无符号类型变量或函数 | union | 声明共用体类型 | void | 声明函数无返回值或无参数,声明无类型指针 | volatile | 说明变量在程序执行中可被隐含地改变 | while | 循环语句的循环条件 |
C99新增关键字 | | | | |
---|
_Bool | _Complex | _Imaginary | inline | restrict |
C11新增关键字 | | | | |
---|
_Alignas | _Alignof | _Atomic | _Generic | _Noreturn | _Static | _assert | _Thread_local | | |
3、标识符
描述 标识符是由非数字字符(包括下划线和大小写字母)及数字构成的序列,其第一个字符应是非数字字符,不同的标识符取决于有效字符的不同 分类
- 结构成员或标记
- 联合成员或标记
- 枚举成员或标记,枚举成员称枚举常量
- 自定义类型名
- 标签名
- 宏名或宏形参
约束
- 组成标识符的字符序列不应与组成关键字的字符序列相同
- 标识符最大长度没有具体限制,标准规定,宏名或无外部链接的标识符前31个字符定为有效字符(不忽略大小写),外部链接的标识符前6个字符定为有效字符(可忽略大小写),具体由实现定义,存在有效字符相同、非有效字符不同的两个标识符的行为未定义
3.1、标识符作用域
3.1.1、作用域范围
如果在同一名称空间中存在相同标识符的外部声明,则该外部声明将被隐藏,直到当前作用域终止,然后再次可见,先内部,再外部
#include<bits/stdc++.h>
using namespace std;
int a = 100;
void test(){
int a = 1;
cout << a;
}
int main()
{
test();
return 0;
}
- 函数(function)作用域:标签,通过冒号隐式声明(go to语句)
- 文件(file)作用域:声明标识符的声明符或类型说明符出现在所有块或参数列表之外,在翻译单元的末尾终止,比如,全局变量
- 块(block)作用域:声明标识符的声明符或类型说明符出现在块内或函数定义中的参数声明列表内,终止于},比如,函数形参
- 函数原型(function prototype)作用域:如果声明标识符的声明符或类型区分符出现在函数原型(不是函数定义的一部分)的参数声明列表中,在函数声明的末尾终止
#include<bits/stdc++.h>
using namespace std;
int test1(int a, int b);
int test1(int buf1, int buf2){
return buf1 + buf2;
}
int N = 100, M;
int main()
{
label: M = test1(N, M);
if(M < 1000){
goto label;
}
cout << M;
return 0;
}
- 结构、联合和枚举标记的作用域在声明标记的类型区分符时,标记出现后立即开始
- 每个枚举常量的作用域都在其在枚举符列表中定义常量的枚举符后立即开始
- 任何其他标识符的作用域都是在其声明程序完成后立即开始
#include<bits/stdc++.h>
using namespace std;
enum test{
a = 100,
b
}num;
int main()
{
cout << 100 + num;
return 0;
}
3.1.2、链接(linkage)
通过链接可在不同作用域或同一作用域,引用同一个对象或函数
- 外部链接(external):一组翻译单元中指向同一个对象或函数,只需extern,若文件范围内有可见声明则与其具有相同的链接,若文件范围内没有可见声明则为外部链接
- 内部链接(internal):一个翻译单元中指向同一个对象或函数,文件作用域 + static,作用:别的文件不能访问
- 无链接(none):指向独一无二的实体,声明为对象或函数以外的任何标识符;声明为函数参数的标识符;没有extern的块内声明的对象标识符
- 如果在翻译单元中,同一标识符同时出现在内部和外部链接中,则该行为未定义
- 函数标识符和文件作用范围的对象默认外部链接
int b = 100;
#include<bits/stdc++.h>
using namespace std;
static int b = 1000;
void test_fuc(){
extern int b;
cout << b;
}
int main()
{
test_fuc();
return 0;
}
3.1.3、命名空间
不同类别的标识符具有不同的命名空间
类别 | 消除歧义方法 |
---|
标签名称 | 通过标签声明和使用的语法消除歧义 | 结构、联合和枚举的标记 | 通过跟随关键字struct、union或enum来消除歧义 | 结构或联合的成员 | 通过. 或 ->运算符访问成员时使用的表达式类型消除歧义 | 所有其他标识符称为普通标识符 | 在普通声明器中声明或作为枚举常量声明 |
#include<bits/stdc++.h>
using namespace std;
struct test1
{
int a = 1;
}test1;
int main()
{
int test1 = 100;
return 0;
}
3.1.4、生存期
- 静态(static)存储期:外部链接、内部链接、存储类说明符static,程序启动前初始化一次,保存存储最后一次修改,程序生存周期
- 自动(automatic)存储期:无链接且无static,声明对象的块中、从块外跳转到块中(可能不会初始化),保证保留值;块结束时不再保证保留值
3.2、类型
存储在对象的值或由函数返回的值的意义由访问它的表达式的类型决定
- 对象类型(描述对象的类型)
- 函数类型(描述函数的类型)
- 不完整类型(描述对象的类型,但还缺少决定其尺寸所需信息)
3.2.1、基本类型
说明
| |
---|
结构类型 | 一组按顺序分配的成员对象,每个成员对象都有一个可选的指定名称和可能不同的类型 | 联合类型 | 一组重叠的成员对象,每个成员对象都有一个可选的指定名称和可能不同的类型 | 数组类型(T数组) | 描述相邻接分配的对象的非空集合,具有特定成员对象类型(称为元素类型),从其元素类型派生,以其元素类型和数组成员数为特征,过程称称数组类型的派生 | 函数类型(返回T的函数) | 具有指定返回类型的函数,特征是其返回类型及其参数的数量和类型,从其返回类型派生,过程称称函数类型的派生 | 指针类型(引用类型、(指向)T的指针) | 描述一类对象,该类对象的值提供对引用类型实体的引用,可以派生自函数类型、对象类型或称为引用类型的不完整类型,过程称指针类型的派生 | 派生类型 | 构造派生类型的方法可以递归使用 | 派生声明符类型 | 从T构造派生 | 无符号整数类型 | 涉及无符号操作数永远不会溢出,详见类型转换 | char型 | 应足够大存储基本执行字符集的任何成员,源字符集成员的值保证为正 | 整型(integral) | 由纯二进制计数系统定义值 | 浮点类型 | 表示法未作规定 |
不完整类型 | 如何变得完整 |
---|
未知尺寸的数组类型 | 在以后的声明(内部、外部链接)指定尺寸后成为完整类型 | 未知内容的结构或联合 | 在以后同一作用域声明相同的结构或联合标记以及定义的内容时成为完整类型 | void类型 | 永不可能完整的不完整类型 |
3.2.2、限定类型
以上每一种非限定类型都有三种相应的限定形式。限定形式与非限定形式属同一类型类别,相同表示和对齐要求的不同类型。派生类型不受派生它的类型的限定词限定,比如,const int * 类型是派生自const int的类型
#include<bits/stdc++.h>
using namespace std;
int main()
{
const int a = 1;
const int b = 100;
const int *c = &a;
c = &b;
cout << *c;
return 0;
}
- const(常量)限定
- volatile(易变型)限定
- 兼有两种限定
3.2.3、相容类型
类型相同即为相容类型?
- 相容结构:成员个数、成员名、成员顺序、位段宽度相同,且成员类型相容
- 相容联合:成员个数、成员名、位段宽度相同,且成员类型相容
- 相容枚举:成员个数、成员名,且他们的成员具有相同的值
3.2.4、复合类型
从两种相容类型可构造出复合类型?,复合类型是与两种类型均相容的类型且满足以下条件:
- 若一种类型是已知尺寸的数组,则复合类型是该尺寸的数组
- 若仅有一种类型是带形参表的函数类型(函数原型),则复合类型是带该形参表的函数原型
- 若两种类型都是带形参表的函数类型,则复合的形参类型表中的每个形参类型是对应形参的相容类型
3.2.5、类型转换
- 显式转换
- 隐式转换
整型和整型
- 转换后的类型能表示转换前的类型的值,则值不变
- 有符号负值转换为无符号值小转大:加上比无符号值最大值大1的值;
- 转换成无符号值大转小:除以无符号值最大值大1的值得到的非负余数
- 转换成有符号值大转小:实现定义
浮点型和整型
- 浮点型转整型,忽略小数部分,若整数部分的值该整型不能表示,未定义
- 整型转浮点型,能确切表示则值不变,若不能确切表示,实现定义为接近、稍小、稍大的值
浮点型和浮点型
- 升格值不变
- 降格,若值表示不了则未定义,若不能确切表示则实现定义为接近、稍小、稍大的值
一般算术转换 运算过程中的隐式转换,比如,整数类型加上浮点类型,会先将整数类型转换成浮点类型。总是小的转换成大的,注,unsigned int可能比long int范围大,转换成unsigned long int
4、常量
每个常量都有一个类型,由其形式和值决定,常数的值应在其类型的代表值范围内。
4.1、浮点常量
最终值若在类型可表示的值范围内且不能确切表示,结果或是最接近的可表示的值,或是比最接近的可表示的值稍小,或是比最接近的可表示的值稍大,由实现定义
#include<bits/stdc++.h>
using namespace std;
int main(int argc,char *argv[]){
float test1 = 1.1;
double test2 = 1.1;
int test3 = 1;
long double test4 = 2;
cout << typeid(1).name() << endl;
cout << typeid(test1).name() << endl;
cout << typeid(test2).name() << endl;
cout << typeid(test3).name() << endl;
cout << typeid(test4).name() << endl << endl;
cout << 1. << endl;
cout << typeid(1.).name() << endl;
cout << .11 << endl;
cout << typeid(.11).name() << endl;
cout << 1.1 << endl;
cout << typeid(1.1).name() << endl;
cout << 1.e-1F << endl;
cout << typeid(1.e-1F).name() << endl;
cout << .1e+1f << endl;
cout << typeid(.1e+1f).name() << endl;
cout << 0e0 << endl;
cout << typeid(0e0).name() << endl;
cout << 00221e-1l << endl;
cout << typeid(00221e-1l).name() << endl;
cout << 0511e+22L << endl;
cout << typeid(0511e+22L).name() << endl;
return 0;
}
4.2、整数常量
递归了属于是
# include <bits/stdc++.h>
using namespace std;
int main()
{
printf("%d %s\n", 11,typeid(11).name());
printf("%d %s\n", 011,typeid(011).name());
printf("%d %s\n", 0x11,typeid(0x11).name());
printf("%lu %s\n", 011ul,typeid(011ul).name());
printf("%u %s\n", 11u,typeid(11u).name());
printf("%ld %s\n", 11l,typeid(11l).name());
return 0;
}
4.3、枚举常量
枚举常量即其标识符,标识符的类型为int
4.4、字符常量
单引号内一个或多个字符
约束 八进制或十六进制转义序列的值对于整型字符常量应在unsigned char类型的表示范围内,而对宽字符常量则应在wchar_t的无符号类型表示范围内
# include <bits/stdc++.h>
using namespace std;
int main()
{
cout << '\0' << ' '<< typeid('\0').name() << endl;
cout << '\102'<<' '<< typeid('\102').name() << endl;
cout << '\x66' << ' '<< typeid('\x66').name() << endl;
cout << '\\' << ' '<< typeid('\\').name() << endl;
cout << L'\0' << ' '<< typeid(L'\0').name() << endl;
cout << L'\102'<<' '<< typeid(L'\102').name() << endl;
cout << L'\x66' << ' '<< typeid(L'\x66').name() << endl;
cout << L'\\' << ' '<< typeid(L'\\').name() << endl;
return 0;
}
cout << 'ab' << ' '<< typeid('ab').name() << endl;
cout << L'ab' << ' '<< typeid(L'ab').name() << endl;
cout << L'寄' << ' '<< typeid(L'寄').name() << endl;
4.5、字符串常量
相邻的字符串常量会被连接成单个字符串并在最后添加一个空字符,宽字符串同理,但是相邻的宽字符串和字符串相邻行为未定义,比如,cout << "\102"“666”;
# include <bits/stdc++.h>
using namespace std;
int main()
{
cout << "\0444" << ' '<< typeid("\0444").name() << endl;
cout << "\1026666"<<' '<< typeid("\1026666").name() << endl;
cout << "\x66ssss" << ' '<< typeid("\x66ssss").name() << endl;
cout << L"\\adsad" << ' '<< typeid(L"\\adsad").name() << endl;
cout << L"" << ' '<< typeid(L"").name() << endl;
cout << "" << ' '<< typeid("").name() << endl;
cout << "\102""44" << ' '<< typeid("\102""44").name() << endl;
return 0;
}
5、算符
内容 [ ] ( ) . -> ++ – & * + - ~ ! sizeof / % << >> < > <= >= == != ^ | && || ? : = *= /= %= += -= <<= >>= &= ^= |= , # ##
优先级
约束 []、()、?:应成对出现,#和##只出现在宏定义和预处理指令中
6、标点符号
内容 [ ] ( ) { } * , : = ; … #
约束 [ ] ( ) { }成对出现,#只出现在预处理指令中
7、头文件名
# include <<bits/stdc++.h>
# include <bits/stdc>
# include <bits/stdc++.h>>
# include "bits/stdc"
约束 只能出现在#include 预处理指令中, ’ " \ or /* 出现在<>中未定义,’ \ or /* 出现在""中未定义
8、预处理数(pp-number)
内容 以数字开头,前面可选地加上句点(.),后面可以是字母、下划线、数字、小数点和e+、e-、E+或E-字符序列,覆盖浮点数和整数
9、注释(Comments)
内容 由/*引出注释,除非/*在字符常量、字符串常量、注释中。检查注释内容的过程包括识别多字节字符和找到 */序列
sd*/
**/
10、其他
10.1、左值
- 左值是指示对象的表达式,是一种对象类型或除void外的不完整类型,当一个对象被称为具有特定类型时,该类型由用于指定该对象的左值指定。
- 可修改左值,不具有数组类型、不具有不完整类型、不具有常量限定类型,并且如果它是结构或联合,则不具有任何具有常量限定类型的成员(递归地包括所有包含的结构或联合的任何成员)。
- 除sizeof的操作数、一元&运算符、++运算符、–运算符、算符或赋值运算符的左操作数,不具有数组类型的左值将转换为存储在指定对象中的值(不再是左值)。如果左值具有限定类型,则该值具有左值类型的非限定版本;否则,该值具有左值的类型。如果左值的类型不完整且没有数组类型,则行为未定义。
- 除sizeof运算符的操作、一元&运算符的操作数、用于初始化字符类型数组的字符串常量、用于初始化与wchar_t兼容的元素类型数组的宽字符串常量,将具有
x type的数组的左值 转换为指向x type的指针 的表达式,且其指向数组对象的初始成员,并且不是左值。 - 自我理解:左值即对象的定位符值,作为一个定位的作用,右值即作为一个值的作用
#include <bits/stdc++.h>
using namespace std;
int main()
{
int a = 0, b = 0;
a = b++;
b = a;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
int main()
{
int a[10] = {1, 2, 3};
sizeof(a); cout << &a;
int *b = a;
return 0;
}
10.2、函数指示符
函数指示符是具有函数类型的表达式。除非是sizeof运算符或一元运算符&的操作数,否则类型为“返回type类型的函数 ”的函数指示符将转换为类型为“指向返回type类型的函数的指针 ”的表达式
#include <bits/stdc++.h>
using namespace std;
int test(){
cout << 100;
return 100;
}
int main()
{
int(*a)();
a = *test;
a();
return 0;
}
10.3、void
void表达式(类型为void的表达式),其实际不存在值不得以任何方式使用,且不得将此类表达式隐式或显式转换为其他类型。如果任何其他类型的表达式出现在需要void表达式的上下文中,则此其他类型的表达式的值或指示符将被忽略。对void表达式求值仅是为了获得副作用,或使用void *。
10.4、指针
- 指向void的指针可以转换为指向任何不完整或对象类型的指针,反之亦然
- 指向任何不完整或对象类型的指针转换为指向void的指针并再次返回,结果与原始指针相同
- 指向非限定类型的指针可以转换为指向该类型的限定版本的指针,且原始指针和转换指针的值应相等
- 值为0的整型常量表达式、转换为void*类型的此类表达式称为空指针常量,保证不等于任何对象和函数的指针
#include <bits/stdc++.h>
using namespace std;
int main()
{
void* c;
int b = 100;
int* a = &b;
c = a;
cout << *(int *)(void *)a << endl;
cout << a << endl;
cout << (const int *)a;
a = 0;
return 0;
}
? ? ? ? ? ? ? ?
以上纯属个人观点,欢迎大佬批评指正
|