程序结构
- 预处理指令
- 函数
- 变量
- 语句
- 表达式
- 注释
语句结束符
在C语言中,分号是语句结束符,它表明一个逻辑实体的结束。
注释
//:单行注释。
/* */:多行注释,不能在注释内嵌套注释。
标识符
C语言的标识符以字母或下划线开头,后跟零个或多个字母、下划线或数字。标识符内不允许出现标点符号,且区分大小写。
整数类型
类型 | 存储大小 | 值范围 |
char | 1B | -128-127或0-255 |
unsigned char | 1B | 0-255 |
signed char | 1B | -128-127 |
int | 2B或4B | -32768-32767或-2147483648-2147483647 |
unsigned int | 2B或4B | 0-65535或0-4294967295 |
short | 2B | -32768-32767 |
unsigned short | 2B | 0-65535 |
long | 4B | -2147483648-2147483647 |
unsigned long | 4B | 0-4294967295 |
浮点类型
类型 | 存储大小 | 值范围 | 精度 |
float | 4B | 1.2E-38到3.4E+38 | 6位小数 |
double | 8B | 2.3E-308到1.7E+308 | 15位小数 |
long double | 16B | 3.4E-4932到1.1E+4932 | 19位小数 |
void类型
类型 | 描述 |
函数返回为空 | 不返回值的函数的返回类型为空 |
函数参数为空 | 不带参数的函数可以接受一个void |
指针指向void | 类型为void *的指针代表对象的地址,而不是类型,可以转换为任何数据类型 |
变量
变量只不过是程序可操作的存储区的名称,变量类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上。变量的声明有两种情况,一种是需要建立存储空间的,另一种是不需要建立存储空间的,通过使用extern关键字声明变量而不定义它。C语言中,表达式左边的值是指向内存位置的,右值指的是存储在内存中某些地址的数值。
常量
在C语言中,有两种简单的定义常量的方式,一种是使用#define预处理器,另一种是使用const关键字。
存储类
存储类定义C程序中变量/函数的范围和生命周期。
auto:所有局部变量默认的存储类。auto只能用在函数内,即只能修饰局部变量。
register:定义存储在寄存器中的局部变量,这意味着变量的最大尺寸等于寄存器的大小,且不能对它应用&运算符。使用该修饰符并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。
static:指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。使用static修饰局部变量可以在函数调用之间
保持局部变量的值。static也可以应用于全局变量。当static修饰全局变量时,会使变量的作用域限制在声明它的文件内。static是全局变量的默认存储类。
extern:提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当使用extern时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。当有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用extern来得到已定义的变量或函数的引用。extern修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候。
运算符
运算符 | 描述 |
+ | 加法 |
- | 减法 |
* | 乘法 |
/ | 除法 |
% | 求余 |
++ | 自增 |
-- | 自减 |
== | 相等 |
!= | 不等 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
&& | 逻辑与 |
|| | 逻辑或 |
! | 逻辑非 |
& | 按位与 |
| | 按位或 |
^ | 异或 |
~ | 取反 |
<< | 左移 |
>> | 右移 |
= | 赋值 |
+= | 加等 |
-= | 减等 |
*= | 乘等 |
/= | 除等 |
%= | 余等 |
<<= | 右移赋值 |
>>= | 左移赋值 |
&= | 按位与赋值 |
^= | 按位异或赋值 |
|= | 按位或赋值 |
sizeof() | 返回变量的大小 |
& | 返回变量的地址 |
* | 指向一个变量 |
?: | 三目运算符 |
控制语句
if(){}
if(){}else{}
if(){}else if(){}else{}
switch(){case 1:expression;default:expression}
while(){}
do{}while()
for(){}
break
continue
goto
函数
函数是一组一起执行一个任务的语句。函数声明会告诉编译器函数名称以及如何调用函数,函数的实际主体可以单独定义。当在一个源文件中定义函数且在另一个文件中调用函数时,函数声明是必须的。在这种情况下,应该在调用函数的文件顶部声明函数。
如果一个函数只能被本文件中其他函数所调用,它被称为内部函数。在定义内部函数时,在函数名和函数类型的前面加上static。如果在定义函数时在函数的首部的最左端加上关键字extern,则此函数是外部函数,可供其他文件调用。
C语言规定,如果在定义函数时省略extern,则默认为外部函数。在需要调用此函数的其他文件中,需要对此函数作声明。在对此函数作声明时,要加上关键字extern,表示该函数是在其他文件中定义的外部函数。
作用域
局部变量:在某个函数或块的内部声明的变量,它们只能被该函数或该代码块内部的语句使用。
全局变量:定义在函数外部,通常是在程序的顶部,全局变量在整个程序生命周期内都是有效的,在任意的函数内部能访问全局变量。
形式参数:函数的参数,被当做该函数内的局部变量,它们会优先覆盖全局变量。
当局部变量被定义时,系统不会对其进行初始化,定义全局变量时,系统会自动对其初始化。
数组
数组可以存储一个固定大小的相同类型元素的顺序集合。所有的数组都是由连续的内存位置组成,最低的地址对应第一个元素,最高的位置对应最后一个元素。
1 int a[10]; 2 int b[5] = {1,2,3,4,5}; 3 b[0] = 1;
指针
指针是一个变量,其值为另一个变量的地址。在使用指针存储其他变量地址之前,需要对其进行声明。可以使用*运算符来返回位于操作数所指定地址的变量的值。在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个NULL值,NULL值被称为空指针。
指针是一个用数字表示的地址,因此可以进行四则运算和自增自减运算。
函数指针是指向函数的指针变量,函数指针可以像一般函数一样,用于调用函数、传递参数。函数指针变量可以作为某个函数的参数来使用,回调函数就是一个通过函数指针调用的函数。
1 int (*add)(int,int);
字符串
在C语言中,字符串实际上是使用‘\0‘终止的一维字符数组。C编译器在初始化字符数组时,会自动把‘\0‘放在字符串的末尾。
结构体
1 struct [structure tag] 2 { 3 member definition; 4 member definition; 5 ... 6 member definition; 7 } [one or more structure variables];
访问结构体的成员使用“.”操作符,访问结构体指针的成员,需要使用“->”运算符。
有些信息在存储时,并不需要占用一个完整的字节,而只需要占用几个或一个二进制位。位域就是把一个字节中的二进制位划分为几个不同的区域,并说明每个位域的位数。每个域都有一个域名,允许在程序中按域名进行操作。
1 struct packed_struct { 2 unsigned int f1:1; 3 unsigned int f2:1; 4 unsigned int f3:1; 5 unsigned int f4:1; 6 unsigned int type:4; 7 unsigned int my_int:9; 8 } pack;
一个位域必须存储在同一个字节中,而不能跨两个字节。如果一个字节所剩余空间不够存放另一个域时,应从下一个单元起存放该位域。也可以有意使某位域从下一单元开始。由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进制。如果最大长度大于计算机的整数字长,一些编译器可能会允许域的内存重叠,另外一些编译器可能会把大于一个域的部分存储在下一个字中。位域可以是无名位域,这时它只用来作填充或调整位置,无名的位域不能使用。位域的使用和结构成员的使用相同。
共用体
共用体允许在相同的内存位置存储不同的数据类型。
1 union [union tag] 2 { 3 member definition; 4 member definition; 5 ... 6 member definition; 7 } [one or more union variables];
共用体占用的内存应足够存储共用体中最大的成员。
typedef
typedef用于为类型取一个新的名字。按照惯例,定义时会使用大写字母,以便提醒用户类型名称是一个象征性的缩写。
预处理器
C语言的预处理器是一个文本替换工具,它们会指示编译器在实际编译之前完成所需的预处理。所有的预处理命令都是以#开头,它必须是第一个非空字符。
指令 | 描述 |
#define | 定义宏 |
#include | 包含一个源代码文件 |
#undef | 取消已定义的宏 |
#ifdef | 如果宏已经定义,则返回真 |
#ifndef | 如果宏没有定义,则返回真 |
#if | 如果给定条件为真,则编译下面代码 |
#else | #if的替代方案 |
#elif | 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码 |
#endif | 结束条件编译块 |
#error | 当遇到标准错误时,输出错误消息 |
#pragma | 使用标准化方法,向编译器发布特殊的命令到编译器 |
宏 | 描述 |
__DATE__ | 当前日期,一个以“MMM DD YYYY”格式表示的字符常量 |
__TIME__ | 当前时间,一个以“HH:MM:SS”格式表示的字符常量 |
__FILE__ | 包含当前文件名的一个字符串常量 |
__LINE__ | 包含当前行号的一个十进制常量 |
__STDC__ | 当编译器以ANSI标准编译时,定义为1 |
预处理器运算符
宏延续运算符(\)
字符串常量化运算符(#):在宏定义中,当需要把一个宏的参数转换为字符串常量时,则使用该运算符。在宏中使用的该运算符有一个特定的参数或参数列表。
标记粘贴运算符(##):合并两个参数,允许在宏定义中两个独立的标记被合并为一个标记。
defined()运算符:用在常量表达式中,用来确定一个标识符是否已经使用#define定义过。
头文件
头文件中包含了C语言函数声明和宏定义,可以被多个源文件引用。引用头文件相当于复制头文件的内容。如果一个头文件被引用两次,编译器会处理两次头文件的内容,这将产生错误。
错误处理
C语言不提供对错误处理的直接支持,它以返回值的形式允许访问底层数据。在发生错误时,大多数的C或UNIX函数调用返回1或NULL,同时会设置一个错误代码errno,该错误代码是全局变量,表示在函数调用期间发生了错误,可以在<error.h>头文件中找到各种各样的错误代码。C程序员可以通过检查返回值,然后根据返回值决定采取哪种适当的动作。开发人员应该在程序初始化时,把errno设置为0。
perror():显示传给它的字符串,后跟一个冒号、一个空格和当前errno值的文本表示形式。
strerror():返回一个指针,指针指向当前errno值的文本表示形式。
可变参数
使用可变参数,需要使用stdarg.h头文件,该文件提供了实现可变参数功能的函数和宏。
- 定义一个函数,最后一个参数为省略号,省略号前面可以设置自定义参数。
- 在函数定义中创建一个va_list类型变量,该类型是在stdarg.h头文件中定义的。
- 使用int参数和va_start宏来初始化va_list变量为一个参数列表。宏va_start是在stdarg.h头文件中定义的。
- 使用va_arg宏和va_list变量来访问参数列表中的每个项。
- 使用宏va_end来清理赋予va_list变量的内存。
1 #include <stdio.h> 2 #include <stdarg.h> 3 4 double average(int num,...) 5 { 6 7 va_list valist; 8 double sum = 0.0; 9 int i; 10 11 /* 为 num 个参数初始化 valist */ 12 va_start(valist, num); 13 14 /* 访问所有赋给 valist 的参数 */ 15 for (i = 0; i < num; i++) 16 { 17 sum += va_arg(valist, int); 18 } 19 /* 清理为 valist 保留的内存 */ 20 va_end(valist); 21 22 return sum/num; 23 }
内存管理
函数 | 描述 |
void *calloc(int num,int size) | 在内存中动态地分配num个长度为size的连续空间,并且每个字节都初始化为0 |
free(void *address) | 释放address所指向的内存块,释放的是动态分配的内存空间 |
void *malloc(int num) | 在堆区分配一块指定大小的内存空间,用来存放数据。这块空间在函数执行完成后不会被初始化,它们的值是未知的 |
void *realloc(void *address,int newsize) | 重新分配内存,把内存扩展到newsize |