【C】C语言核心知识点总结(Reference Manual)

1、词法

(1)字符集——

C语言字符集使用ISO/IEC 10646基本拉丁字符集,包括52个大小写拉丁字母,10个数字,空格,水平制表符HT,垂直制表符VT,换页符FF,以及29个特殊字符,它们是感叹号!,数字号#,百分号%,折音符^,和号&,星号*,左括号(,下划线_,右括号),连字符-,加号+,等号=,波浪号~,左方括号[,右方括号],撇号',竖线|,反斜杠\,分号;,冒号:,引号",左花括号{,右花括号},逗号,,句号.,小于号<,大于号>,除号/,问号?

有些国家的本国字符集不包括上面提到的所有特殊字符,为此C89定义了三字符组与记号重拼,详见【C】标准C语言的三字符组及替换拼写的分隔符,以及为了适应包含大量字符的非英语字母表,标准C语言还引入了宽字符、宽字符串、多字节字符。有时还使用其它字符,包括退格符BS,回车符CR,美元号$,商务中的AT@,重音符`,再比如说编码为数值0的NULL空字符,作为行末标记的换行符,警报等,它们与标准字符相同,只是使用了反斜杠这个转义字符对标准字符进行转义,反斜杠的另外一种用途是续行。在C语言的源程序中,空格、行末符、水平制表符、垂直制表符和换页统称为空白符。

字符集有约定的编码方式,即计算机上的数字化表示,如常见的ASCII字符集。

(2)注释——

标准C语言中有两种编写注释的方式,传统上,注释语句以/*开头,以*/结束,注释可以包含任意多的字符,从C99开始,注释也可以用//开始,延伸到下一个行终结符。编译器在预处理之前删除注释语句,因此注释语句中的预处理命令无法识别,注释语句中的行终结符无法终止预处理命令。注释用法如下所示。

/*
multi-lines comments
*/
// single line comments

(3)记号——

C语言程序中的字符组合按照一定的规则聚合成词法记号,记号共五类,它们是运算符、分隔符、标识符、关键字和常量。编译器从左向右收集字符,总是尽量建立最长的记号,即使结果并不构成有效的C语言程序,相邻记号可以用空白符或注释语句分开。预处理器采用稍有不同的记号规则,特别地,标准C语言预处理器把###作为记号,分别表示把一个参数转变为字符串和把前后两个字符串拼接起来,它们在传统C语言中是无效的。

(4)运算符与分隔符——

简单运算符:! % ^ & * - + = ~ | . < > / ?

复合赋值运算符:+= -= *= /= %= <<= >>= &= ^= |=

其它复合运算符:-> ++ -- << >> <= >= == != && ||

分隔符:( ) [ ] { } , ; : ...

以及前面提到的使用没有某些美语或英语字符的IO设备的替换记号拼写,头文件iso646.h中定义了展开某些运算符的宏。

(5)标识符——

标识符或名称是大小写拉丁字母、数字和下划线组成的序列,不能以数字开头,不能与关键字的拼写相同,区分大小写,长度有一定的限制,为了增强代码可读性,多个单词组成的标识符一般以下划线分隔或者采用首字母大写的驼峰命名法。

(6)关键字——

关键字不能作为普通标识符,但可以作为宏名,因为所有预处理发生在识别这些关键字之前,关键字如下所示。

auto _Bool break case char _Complex const continue else enum extern float for goto if _Imaginary inline int long register return short signed sizeof static struct switch typedef union unsigned void volatile while

除了上面的关键字之外,还有一些常用语言扩展,如汇编扩展标识符asm,以及头文件iso646.h中定义的宏如and都当作保留字,也不应该作为普通标识符。另外,还有预定义标识符,如表示函数名的__func__

(7)常量——

常量包括整型、浮点型、字符型和字符串型,也就是说是一个不变的字面值。整型常量可以用十进制、八进制(数字0开头)或十六进制(0x0X开头)方法指定,可以带有若干后缀,lL表示longllLL表示long longuU表示unsigned,头文件stdint.h中定义了整型相关的一些内容。浮点型常量可以写成带小数点的、带符号指数的或同时包含两者的十进制数,可以用浮点型后缀fFlL指定floatlong double类型的常量,如果不用后缀,则常量类型为double,对于浮点数的限制记录在float.h头文件中,math.h中定义了特殊浮点型常量,例如无限大和NaN(非数字),十六进制格式的浮点型常量用字母p分开小数与指数部分,与十进制的e区分,另外复数浮点型常量用包含了complex.h中定义的虚数常量_Complex_II的浮点型常量表达式来表示。字符型常量把一个或几个字符放在一对撇号中,可以用特殊转义机制编写不方便或无法直接在源程序中输入的字符或数字值,标准C语言允许在字符型常量前面加上字母L来指定宽字符常量。字符串型常量是双引号中的字符序列,可以用字符常量所用的转义机制表示字符串中的字符,标准C语言允许在字符串型常量前面加上L前缀来指定宽字符串常量,值得注意的是最后一个字符是null字符\0。转义符包括字符转义和数字转义,前者如表示换行的\n,后者如表示问号的\x3F

(8)C++兼容性

C++几乎兼容C语言的所有内容,另外增加了3个新的复合运算符.*->*::,以及许多关键字asm export private throw bool false protected true catch friend public try class mutable reinterpret_cast typeid const_cast namespace static_cast typename delete new template using dynamic_cast operator this virtual explicit wchar_t,还有一点需要注意的是,C语言中单字符常量的类型为int,而C++中的类型为char,多字符常量是实现定义的,在两种语言中均为int类型,实际上,这样并没有什么差别,因为按照通常转换规则,C++字符型常量会升级为int类型,但是sizeof('c')C++中为sizeof(char)即1,而在C语言中为sizeof(int)即4。

(9)字符集、指令集和编码方式

不同的编码方式主要是考虑到了国际化和多语种,如称为ASCII的七位编码方式,但是许多国家采用不同的字符集,逐渐出现几种部分解决方案,例如,ISO 646-1083不变代码集是ASCII的子集,是许多非英语字符集之间共用的集合,人们已经提出一些方法替换这个不变集中没有的C语言字符,ISO/IEC 10646及其增补定义了字符集的一般解决方案,即通用多八位组编码字符集UCS,它定义了四字节或4八位组编码UCS-4,在表示了全世界所有文字中的所有字符之外还有很多空余的编码,UCS-4的16位子集称为基本多语种平面USC-2,有头两字节为0的UCS-4编码组成,UCS-2可以表示个主要语言字符集,包括大约两万个中文、日文、朝鲜文表意字符,但是,16位通常是不够的,而超过32位的值又很难在计算机上操作,因此定义了UCS-4。UCS字符添加前缀\u\U

Unicode字符集标准最初是由Unicode联盟建立的十六比特编码方式,Unicode 3.0与ISO/IEC 10646标准完全兼容。UCS-4、UCS-2与Unicode字符集标准与ASCII兼容,高8位为0的十六比特字符就是八比特扩展ASCII字符集,现在称为Latin-1字符集,原始的七比特ASCII字符集现在称为基本拉丁字符集,是高9位为0的UCS-2字符集。

2、预处理器

C语言预处理器是个简单的宏处理器,从概念上处理C程序源文本之后再让编译器正确读取源程序,在C语言的有些实现产品中,预处理器实际上是一个独立的程序,它读取原始的源文件并输出新的经过预处理的源文件,以作为C语言编译器的输出,在另一些实现产品中,一个程序一次性对源文件进行预处理和编译。关于预处理器的详细用法请参考【C】C语言预处理器的几个命令

3、声明

C语言中声明一个名称就是把一个标识符与某个C语言对象相关联,如变量、函数或类型,
C语言中可以声明的名称包括:变量、函数、类型、类型标志、结构成员或联合成员、枚举常量、语句标号、预处理器宏。除了语句标号和预处理器宏之外,所有标识符都在C语言声明中声明。变量、函数、类型放在声明的声明符中,类型标志、结构成员与联合成员和枚举常量在声明的某种类型说明符中声明,语句标号在C语言函数中出现时声明,而预处理器宏用#define预处理器命令声明。声明时,需要注意作用域和生存期,以及一些复杂语法的用法。关于声明的详细用法请参考【C】C语言中标识符声明的几个关键字总结

4、类型

C语言的类型如下:

short:整型,实数类型,算术类型,标量类型。
int:整型,实数类型,算术类型,标量类型。
long:整型,实数类型,算术类型,标量类型。
long long:整型,实数类型,算术类型,标量类型。
char:整型,实数类型,算术类型,标量类型。
_Bool:整型,实数类型,算术类型,标量类型。
enum {...}:整型,实数类型,算术类型,标量类型。
float:浮点型,实数类型,算术类型,标量类型。
double:浮点型,实数类型,算术类型,标量类型。
long double:浮点型,实数类型,算术类型,标量类型。
float _Complex:浮点型,复数类型,算术类型,标量类型。
double _Complex:浮点型,复数类型,算术类型,标量类型。
long double _Complex:浮点型,复数类型,算术类型,标量类型。
float _Imaginary:浮点型,复数类型,算术类型,标量类型。
double _Imaginary:浮点型,复数类型,算术类型,标量类型。
long double _Imaginary:浮点型,复数类型,算术类型,标量类型。
T *:指针类型,标量类型。
T [...]:数组类型,组合类型。
struct {...}:结构类型,组合类型。
union {...}:联合类型。
T (...):函数类型。
void:void类型。

(1)整型——

可以把整数类型分为4类:带符号类型signed、无符号类型unsigned、布尔类型和字符型,enum中的成员就是整型,字符类型还包括宽字符类型wchar_t。short(int)、int、long(int)默认是带符号类型,_Bool是无符号类型,char默认类型由具体的编译器而定,enum常量类型为int。标准C语言规定了大多数整数类型的最小精度,如char至少为8位,short至少为16位,long至少为32位,long long至少为64位。整型使用二进制编码方式——补码。C语言中用字符数组表示字符串。

(2)浮点型——

浮点类型包括单精度float和双精度double。

(3)指针——

指针是个难点,也是个很重要的话题。对任何类型T,可以建立指向T的指针。根据T是对象类型或函数类型,相应的指针类型称为对象指针或函数指针。指针类型的值是类型为T的对象或函数的地址。指针的简单例子如下。

int *ip; // a pointer to an object of type int
char *cp; // a pointer to an object of type char
int (*fp)(); // a pointer to a function returning an interger and without arguments

指针类型使用的最主要运算符是地址运算符&和间接访问运算符,前者生成指针值,后者访问指针所指的对象。指针长度是由实现定义的,通常在32位机器上长度为4个字节,在64位机器上长度为8个字节。在标准C语言中,指针类型可以用类型限定符const、volatile与restrict进行限定。通用指针可以转换成任何对象指针类型,在传统C语言中,习惯上使用类型char*表示通用指针,标准C语言引入类型void*作为通用指针,同时还保留了与类型char*相同的表示方法,以兼容早期的实现,但它对通用指针的处理方法与早期的实现不同,通用指针不能用或下标运算符取消引用,也不能作为加减运算的操作数,指向对象或不完整类型(而不是指向函数类型)的任何指针都可以转换为void*类型,然后再转换为原类型,值保持不变,void*既不是对象指针,也不是函数指针。C语言中每个指针类型都有一个特殊值,称为null指针,用数字0或关键字NULL表示。

(4)数组——

数组与指针息息相关,下面举例说明。

int a[3]; // 数组a有3个元素a[0]、a[1]和a[2],元素类型为int,长度sizeof(a)等于sizeof(int) * 4
int *p; // 指针p指向的对象类型为int
p = a; // 等效于 p = &a[0];
a[1] = 1; // a[1]等效于*(a + 1)
p[2] = 2; // p[2]等效于*(p + 2)
int m[3][4]; // 二维数组m有3个元素,元素类型为有4个int元素的数组
m[1][2] = 3; // m[1][2]等效于*(*(m + 1) + 2)

使用数组时需要特别注意的是数组的长度即数组边界,以防止非法操纵。变长数组只有到运行时才能确定其长度,先不考虑函数中的数组参数,变长数组应在块作用域中声明,不能是static存储类型或extern存储类型,只能将普通标识符声明为变长数组,而不能将结构成员或联合成员声明为变长数组。如果在typedef声明中使用变长数组,则长度表达式求值一次,即在遇到typedef声明时求值,而不是每次使用新类型名时都求值,如下例子所示。

int a = 5;
typedef int[n] vector;
n += 1;
vector a; // a有5个元素
int b[n]; // b有6个元素

变长数组可以作为函数参数类型,函数声明与定义时可能不同,用法如下。

void f(int r, int c, int a[c][r]) {...} // ok
void f(int a[c][r], int r, int c) {...} // ng, r and c are not visible to a[c][r]
void f(int, int [*][*]); // 用*表示可变数组长度
void f(int n, int [*][m]);
void f(int n, int [n][n]);
void f(int , int[])[*]); // 省略变长数组最内层长度

(5)enum——

enum用法如下。

enum color { red, green, blue }; // 类型名为color,元素red值为0,后面元素的值递增1
enum color r = red; // 定义变量
enum color { red, green, blue } r; // 同时声明一个变量r
r = red; // 赋值
enum color { red, green = 100, blue }; // 指定元素green的值为100
enum { red, green = 100, blue } r = red; // 匿名enum类型

枚举标志与结构标志和联合标志属于同一重载类,其作用域与原程序中同一位置声明的变量作用域相同。定义为枚举常量的标识符是与变量、函数和typedef名称同一重载类的成员,其作用域与源程序中同一位置定义的变量作用域相同。

(6)struct——

struct用法如下。

struct complex
{
    double real;
    double imag;
} x, *y; // 类型名为complex,有2个成员,real和image,同时声明了变量x和y
x = { 0.0, 0.0 }; // 赋值
x = { .real = 0.0, .imag = 0.0 }; // 赋值
x.real = 1.0; // 通过.访问成员real
y = &x;
y->real = 2.0; // 通过->访问成员real,此时x的成员real被修改为2.0

struct成员存储有特定的方式,首先成员按照出现的先后顺序在内存地址上进行递增排列,然后是成员在内存中适当对齐,因此连续成员之间和最后一个成员之后可能出现空穴或填充区,sizeof运算符返回的值包括填充区所占的空间,有些实现提供杂注(pragma)或开关来控制结构成员的存储。struct还有个特殊的位字段用法,如下。

struct S
{
    unsigned a : 4; // 成员a大小为4bit
    unsigned b : 5;
    unsigned   : 2; // 匿名位字段,提供相邻成员之间的填充,还支持一个特殊的值0,表示存储前一位字段的区中不能再放置更多的位字段,即直接将前一个存储单元填充满。
    unsigend c : 7;
};

下面是struct与数组结合起来的使用的一个特殊用法。

struct Vec { int len; double vec[]; }
struct Vec *p;
int n = 10;
p = malloc(sizeof(struct Vec) + n * sizeof(double));
p->len = n;
for (int i = 0; i < p->len; ++i) p->vec[i] = 0.0;

(7)union——

union类似于struct,但一次只能包含一个成员值,从概念上讲,成员共用分配给联合的存储空间,也就是说,其大小等于所有成员中长度最大的一个成员的大小再加上字节对齐所需的填充区大小。union用法例子可参照【C】用联合确定计算机的字节顺序

(8)函数——

函数用法如下所示。

int square(int x) { return x * x; } // 定义
int square(int x); // 声明,参数名x可省略
extern int square(int); // 使用extern进行声明,说明定义在其它地方,头文件中的函数声明默认是extern
int value = square(2); // 调用函数
int (*fp)(int); // 函数指针
fp = square; // 等效于fp = &square;
(*fp)(100); // 使用函数指针,等效于fp(100);

更多函数指针的用法可参照【C】函数指针用法

(9)void——

void类型没有数值,也没有运算,作用包括:
作为函数返回类型,表示这个函数无返回值;
在转换表达式中显式地放弃一个值;
建立void*类型,这是通用数据指针;
代替函数声明符中的参数表,表示函数不带参数。

(10)typedef——

typedef用于定义一些类型,如下例子。

typedef int *IP; // 指针
typedef int (*FP)(); // 函数指针
typedef int F(int); // 函数
typedef double A5[5]; // 数组
typedef int A[]; // 数组

定义了新的类型之后就可以像内置类型一样使用这些类型了。

(11)C++兼容性——

一个好的编程习惯是一定要通过显示类型转换把枚举类型或枚举常量作为整数类型来使用,与C语言中不同的是,C++认为枚举类型之间是相互区别的,且不同于整数类型,但可以通过转换表达式相互转换,C++还允许枚举类型隐式转换为整数类型,例子如下。

enum e { red, green, blue } e_var;
int i_var = red; // valid in both C and C++
i_var = (int)red; // valid in both C and C++
e_var = 1; // valid in C, not in C++
e_var = (enum e)1; // valid in both C and C++

和C语言一样,typedef名称可以在内部作用域中被重新声明为对象,但是,如果struct或union已近使用了原先的typedef名称,则C++不允许在struct或union中(即作用域)再对这些名称重新声明,例子如下。

typdef int INT;
struct S
{
    INT i;
    double INT; // OK in C, not C++, everywhere a bad idea
};

(12)数据的表示——

数据的表示需要注意以下几个方面:
存储单元,即字节,1Byte=8bit;
数据长度,不同类型的数据长度可能不同,特别是在类型转换是注意数据长度、精度问题;
字节顺序,高位存储法(大端)或者低位存储法(小端),网络字节序为大端;
对齐限制,特别是struct、union;
指针长度,如32位机器指针长度为4,64为机器为8;
寻址模型,如关键字__near、__far用于指定指针长度;
类型表示,即数据的有效位是如何使用的,因为有可能是填充位。

(13)数据的转换——

数据的转换最好使用显式类型转换,下列情况下,C语言可以将一种类型的值隐式转换成其它类型的值:
准备进行某种算术或逻辑运算时,操作数可以隐式地转换成另一类型;
一种类型的对象可以指定另一类型的地址(左值),产生隐式转换;
函数的实际参数可以隐式地转换为另一类型之后再进行函数调用;
函数的返回值可以隐式地转换为另一类型之后再进行函数返回。

char *cp;
void *vp;
cp = vp; // ok in C, not c++
cp = (char*)vp; // ok in both
vp = cp; // ok in both

5、表达式

表达式的相关内容请参照【C】表达式

6、语句

简单语句用分号进行分隔。

复合语句放在一对花括号中构成语句块。

条件语句:if-else if-else。

条件语句:switch-case,使用break关键字跳出。

循环语句:for,使用continue关键字继续下一次循环,break跳出循环。

循环语句:while,使用continue关键字继续下一次循环,break跳出循环。

循环语句:do-while,使用continue关键字继续下一次循环,break跳出循环。

null语句:只有一个分号,用null语句体作为迭代语句(while、do或for)的语句体,或者作为终止复合语句的右花括号前面用到的用于goto或者switch的标号。

return语句:在函数中任意位置立即从函数中退出。

goto语句:将控制转移到函数中任何语句。

7、函数

前面类型一节中的函数部分介绍了函数,另外,static函数只在当前文件内有效即属于文件作用域,inline函数是一种编译器优化形式,把函数调用转换成函数体的拷贝,从而消除函数调用的开销,任何静态函数都可以指定为inline,因为所有调用与定义都局限在一个翻译单元中,而extern函数则不同。

C++兼容性——

为了与C++兼容,所有函数都要用原型声明,事实上,非原型形式在C++中具有不同含义,空参数列表在C++中表示函数不带参数,而在C语言中表示函数带未知个数的参数。不要在参数列表或返回类型声明中放置类型声明,这在C++中是不允许的。在C++和C99中,要从具有非void返回类型的函数中返回适当类型的值,C89允许不返回数值,这样就保证了其向下兼容性。在C++中,main函数不能递归调用,也不能取得其地址,对程序启动有更多限制。

main函数说明参考【C】C语言main函数的三种形式

可变参数函数说明参考【C】可变参数的函数与宏

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值