写在最前,
写文章的初衷只是为了复习与记录自己的成长,笔者本人也还是学生,文章中难免会出现许多问题与错误,文章内容仅供参考,有不足的地方还请大家多多包涵并指正,谢谢~
本篇笔记分类描述了C语言语法方面的主要特性,可作为速查小手册或复习笔记使用。
1.关键字
C语言中共有32个关键字
auto | 声明自动变量 | break | 跳出当前循环 | case | 开关语句分支 |
char | 声明字符型变量或函数返回值类型 | const | 声明只读变量 | continue | 结束当前循环,开始下一轮循环 |
default | 开关语句中的默认分支 | do | 循环语句的循环体 | double | 声明双精度浮点型变量或函数返回值类型 |
else | 条件语句否定分支(与if连用) | enum | 声明枚举类型 | extern | 声明变量或函数是在其它文件或本文件的其他位置定义 |
float | 声明浮点型变量或函数返回值类型 | for | 循环语句 | goto | 无条件跳转语句 |
if | 条件语句 | int | 声明整型变量或函数 | long | 声明长整型变量或函数返回值类型 |
register | 声明寄存器变量 | return | 子程序返回语句 | short | 声明短整型变量或函数 |
signed | 声明有符号类型变量或函数 | sizeof | 计算数据类型或变量长度(即所占字节数) | static | 声明静态变量 |
struct | 声明结构体类型 | switch | 用于开关语句 | typedef | 用于给数据类型取别名 |
union | 声明共用体类型 | unsigned | 声明无符号类型变量或函数 | void | 声明函数无返回值或无参数,声明无类型指针 |
volatile | 说明变量在程序执行中可被隐含地改变 | while | 循环语句的循环条件 |
2.标识符
C语言的标识符由字母、数字和下划线组成,其中第一个字符必须是字母或下划线。标识符中英文字母的大小写是有区别的。
3.常量
常量是程序里直接写出的数据,包括各种整数、浮点数、字符和字符串。
(1)整数常量
整数常量通常由一串数字组成,数字前面可以有表示正的符号+或负的符号-
①如果第一个数字是0,那么该常量被看做八进制数,所有的数字都应该属于0~7
②如果是0x(或0X)开头,该常量被看做十六进制数,所有的数字都应该属于0~9和a~f(或A~F)
③如果在常量后面加上符号l或者L,那么该常量被当做long int类型
④如果在常量后面加上符号u或者U,那么该常量被当做一个unsigned int类型
(2)浮点数常量
浮点数由一组数字、一个小数点和另一组数字组成,前面可以有表示正负的符号。如果在浮点常数后面跟上e或者E,然后再跟上一个有符号整数,那么该浮点数常量采用的是科学计数法。该浮点常量的值是10的指数部分(整数)次方再乘以前面的小数部分。
(3)字符常量
字符常量指单个字符,用一对单引号及其所括起来的字符来表示。一些特殊字符的表示可以通过转义符“\”来实现。常见的特殊字符如下:
字符 | 含义 |
\a | 声音警铃 |
\b | 退格 |
\f | 表单 |
\n | 换行 |
\r | 回车 |
\t | 水平制表 |
\v | 垂直制表 |
\\ | 反斜线 |
\" | 双引号 |
\' | 单引号 |
\? | 问号 |
(4)字符串常量
字符串常量由一对双引号括起来的0个或者多个字符序列组成。双引号中可以包含任何字符,包括前面列出的转义字符。编译器自动在字符串后面加上结束的空字符'\0'
如果将字符串常量用作sizeof操作符的参数、&操作符的参数或者用于初始化字符数组,该常量被当做字符数组类型。
应用字符串常量时将返回一个指向该字符序列的第一个字符的指针。
程序中不能修改字符串常量。
4.运算符与表达式
(1)运算符的优先级及结合方式
优先级 | 运算符 | 名称 | 特征 | 结合方向 |
1 | ( ) [ ] -> . | 圆括号 下标 指针应用结构体成员 取结构体变量成员 | 初等运算符 | 从左到右 |
2 | ! ~ + - (类型名) * & ++ -- sizeof | 逻辑非 按位取反 正号 负号 类型强制转换 取指针内容 取地址 自增 自减 长度运算符 | 单目运算 (只有一个操作数) | 从右到左 |
3 | * / % | 相乘 相除 取两整数相除的余数 | 算数运算 | 从左到右 |
4 | + - | 相加 相减 | ||
5 | << >> | 左移 右移 | 移位运算 | |
6 | > < >= <= | 大于 小于 大于或等于 小于或等于 | 关系运算 | |
7 | == != | 等于 不等于 | ||
8 | & | 按位“与” | 位逻辑运算 | |
9 | ^ | 按位“异或” | ||
10 | | | 按位“或” | ||
11 | && | 逻辑“与” | 逻辑运算 | |
12 | || | 逻辑“或” | ||
13 | ?: | 条件运算 | 三目运算 | 从右到左 |
14 | = += -= /= %= &= ^= |= >>= <<= | 赋值运算 | ||
15 | , | 逗号运算 | 从左到右 |
说明:优先级1最高,15最低。运算时,运算符优先级高的运算先执行。运算符的优先级可以根据表中的“特征”列按大类记忆。
(2)算数表达式
如果a、b是除了void类型外的任何基本数据类型,i、j是整数数据类型,那么:
①-a:a的负数
②+a:a
③a+b:a与b的和
④a-b:a与b的差
⑤a*b:a与b的乘积
⑥a/b:a除以b的值
⑦i%j:i除以j所得到的余数
在每一个表达式中,运算时都要对操作数进行算术转换
如果除法的两个操作数都是整数,那么所得的结果将被取整。如果两个操作数中有一个是负数,那么取整的方向是不确定的
(3)自增和自减表达式
如果v是一个可修改的变量,那么:
①++v:先把v的值加1,然后将v的值作为表达式的值
②v++:先把v的值作为表达式的值,然后将v的值加1
③--v:先把v的值减1,然后将v的值作为表达式的值
④v--:先把v的值作为表达式的值,然后将v的值减1
(4)逻辑表达式
如果a、b是除了void类型外的任何基本数据类型,或者两者都是指针类型,那么:
①a&&b:当a和b的值都不是0时结果为1,否则结果为0(只有a不等于0时才对b求值)
②a||b:当a和b中有一个不为0时结果为1,否则结果为0(只有a等于0时才对b求值)
③!a:当a等于0时结果为1,否则结果为0;
补充:作为条件时,!x等价于x==0,即x等价于x!=0,因此,x、y不同时为0有3种表示方法:
!(x==0&&y==0)、x!=0||y!=0、x||y
(5)关系表达式
如果a、b是除了void类型外的任何基本数据类型,或者两者都是指针类型,那么:
①a<b:当a小于b时结果为1,否则结果为0
②a<=b:当a小于等于b时结果为1,否则结果为0
③a>b:当a大于b时结果为1,否则结果为0
④a>=b:当a大于等于b时结果为1,否则结果为0
⑤a==b:当a等于b时结果为1,否则结果为0
⑥a!=b:当a不等于b时结果为1,否则结果为0
注:如果a和b都是指针,那么只有当a和b指向同一个数组或者同一个结构或联合的成员时,这些操作才有意义
(6)字位表达式
如果i、j、n都是整型表达式,那么:
①i&j:i和j执行按位与操作
②i|j:i和j执行按位或操作
③i^j:i和j执行按位异或操作
④~i:i的补数
⑤i<<n:i左移n位
⑥i>>n:i右移n位
(7)强制类型转换表达式
如果type是一种数据类型,a是一个表达式,那么:
(type)a:将a转换为指定的类型
(8)逗号表达式
如果a和b分别是两个表达式,那么:
a,b:表示编译器先对a求值,然后再对b求值,整个表达式的结果和类型等于表达式b的结果和类型(9)赋值表达式:
如果v是一个可以修改的变量,op是一个有对应赋值操作符的操作符,a是一个表达式,那么:
①v=a:表示把a的值保存到v中
②v op =a:表示把op代表的操作作用于v和a,然后将结果保存在v中,相当于v=v op a
(10)条件表达式
如果a、b、c是表达式,那么;
a?b:c 表示当a的值不为0的时候,表达式等于吧,否则等于c
注意:整个过程支队b或者c中的一个求值
(11)sizeof运算符
如果type是一种数据类型,a是一个表达式,那么:
①sizeof(type):表达式的值等于容纳指定数据类型的值所需要的内存字节数
②sizeof(a):表达式的值等于保存表达式a的结果所需要的内存字节数
(12)数组的基本操作
如果a为含有n个元素的数组;i为整型数的表达式;v为表达式,那么:
①a[0]:数组a的第一个元素
②a[n-1]:数组a的最后一个元素
③a[i]:数组a的第i+1个元素(第i号元素)
④a[i]=v:把表达式v的结果值保存到a[i]中
注:上面表达式的类型要等于数组a中所保存元素的类型
(13)结构的基本操作
如果:
①x:是一个可以修改的变量,其类型为struct s
②y:是一个类型为struct s的表达式
③m:是类型struct s的一个成员变量的名字
④v:是一个表达式
那么:
①x:引用整个结构,其类型为struct s
②y.m:引用结构变量y的成员变量m,其类型为m的类型
③x.m=v:将表达式v的值保存到结构变量x的成员变量m中,其类型为m的类型
④x=y:将y的值赋值给x,结果的类型为struct s
⑤f(y):调用函数f( )并将结构变量y的内容作为参数传递给该函数。在函数f( )内部,形式参数的类型必须是struct s
⑥return y:返回结构变量y的内容,函数的返回值必须被声明为struct s类型
(14)指针的基本操作
如果:
①x:是一个类型为t的变量
②pt:是一个指向t类型变量的指针变量
③v:是一个表达式
那么:
①&x:生成一个指向x的指针,表达式的类型为指向t的指针
②pt=&x:使得指针pt指向x,表达式的类型为指向t类型变量的指针
③pt=NULL:将指针pt设置为空指针(也可以用0表示空)
④pt==NULL:判断pt是否为空指针
⑤*pt:取得指针pt指向的值,表达式的类型为t
⑥*pt=v:将表达式v的值保存在pt所指向的位置中,表达式的类型为t
(15)指向数组的指针
如果;
①a:是一个数组,其元素类型为t
②pa1:是一个指向t的指针变量,指向数组a中的某个元素
③pa2:是一个指向t的指针变量
④v:是一个表达式
⑤n:是一个整型表达式
那么:
①a,&a[0]:产生一个指向数组第一个元素的指针
②&a[n]:产生一个指向数组第n号元素的指针,类型为指向t的指针
③*pa1:引用pa1指向的元素,类型为t
④*pa1=v:讲表达式v的值保存在pa1指向的位置,表达式的类型为t
⑤++pa1:将pa1指向数组a的下一个元素,表达式类型为指向t的指针
⑥--pa1:将pa1指向数组a 的前一个元素,表达式类型为指向t的指针
⑦*++pa1:将pa1指向数组a的下一个元素,然后引用该元素,表达式的类型为t
⑧*pa1++:yinyongpt所指向的元素,表达式的类型为t;然后将pa1指向数组a的下一个元素
⑨pa1+n:生成一个指针,该指针指向数组a中pa1所指元素后面的第n个元素
⑩pa1-n:生成一个指针,该指针指向数组a中pa1所指元素前面的第n个元素
⑪*(pa1=n)=v:将表达式v的值保存在pa1+n所指向的位置,表达式的类型为t
⑫pa1<pa2:判断pa1指向的元素在数组中的位置是否在pa2所指的元素的前面,表达式的类型为整数(所有的关系操作符都可以用于指针比较)
⑬pa2-pa1:数组a中pa1所指向的元素和pa2所指向的元素之间的元素个数假设(pa2所指向的元素在pa1后面),表达式的类型为整数
⑭a+n:生成一个指针指向数组a的第n好元素,表达式的类型为指向t的指针,该表达式与&a[n]完全等价
⑮*(a+n):获取数组a的第n号元素的值,表达式的类型为t,该表达式与a[n]等价
(16)指向结构的指针
如果:
①x:是类型struct s的一个变量
②ps:是一个其类型为指向struct s的指针变量
③m:是类型struct s的成员变量,其类型为t
④v:是一个表达式
那么:
①&x:产生一个指向x的指针,该指针的类型为“指向struct s的指针”
②ps=&x:将指针ps指向x,表达式的类型为“指向struct s的指针”
③ps->m:引用ps所指向的结构的成员变量m,表达式的类型为t
④(*ps).m:同样引用ps所指向的结构的成员变量m,与ps->m完全等价
⑤ps->m=v:将表达式v的值保存在ps所指向的结构的成员变量m中,表达式的类型为t
5.语句
(1)空语句
空语句的一般形式如下:
;
空语句的执行不会产生任何效果。通常被用作为for、do、while语句的一个组成部分用以满足C语言的语法要求。
(2)注释语句
在程序中有两种插入注释的方法:
注释一行的一般形式如下:
//注释内容
使用//可以用来开始一个单行注释,在该行上所有位于//后面的字符都将被编译器忽略
注释多行的一般形式如下:
/*
注释内容
*/
多行注释以“/*”开始,“*/”结束。在“/*”和“*/”之间可以放任何内容,这些内容可以跨越多行。多行注释不允许嵌套使用,也就是说,即使前面有多个“/*”,第一个遇到的“*/”就表示注释结束了
在程序中任何可以插入空白的地方都可以插入注释
(3)复合语句
复合语句的一般形式如下:
{
语句1;
语句2;
...
语句n;
}
包含在一对大括号之间的一组语句被称为一条复合语句。在程序中任何可以使用单条语句的地方都可以使用复合语句。
在复合语句内可以定义局部变量,该局部变量将覆盖在该复合语句外定义的同名变量。这些局部变量的作用域限制在定义它们的复合语句的内部
(4)表达式语句
(5)控制语句
参见6.基本控制结构
(6)函数调用语句
6.基本控制结构
下面列出C语言的各种控制结构,并给出简短的解释
(1)break语句
break语句的一般形式如下:
break;
break语句只能用于for、while、do或者switch语句内部,在遇到break语句之后,这些语句将立即结束执行,计算机将接着执行这些语句后面的语句。
(2)continue语句
continue语句的一般形式如下:
continue;
continue语句只能用于循环语句内部。当遇到continue语句之后,循环体中continue语句后面的语句将被跳过,计算机将接着执行下一次循环。
(3)goto语句
goto语句的一般形式如下:
goto 标号;
执行goto语句将使得程序的执行流程直接转到标号对应的那条语句。该标号的语句必须和goto语句位于同一个函数中。一般不提倡使用goto语句。
(4)return语句
return语句的一种常见形式如下:
return;
执行return语句将使得程序的执行流程立刻回到调用者。这种形式的return语句只能用在那些无返回值的函数中。
return语句的第二种常见形式如下:
return 表达式;
这个语句将表达式的值作为函数返回值返回给调用者。
如果计算机执行到函数的最后一条语句,但是还没有遇到return语句,执行流程仍将返回调用者,就好像已经执行了return语句一样。在这种情况下,函数将不返回值。
(5)if语句
if语句的一种常见形式如下:
if(表达式)
语句1;
如果表达式的值不为0,那么语句1将被执行,否则的话该语句将被跳过。
另一种if语句的常用形式如下:
if(表达式)
语句1;
else
语句2;
首先对表达式求值,若得到的值非0就执行语句1部分;若得到的值为0,则执行语句2。如果语句2本身也是一个if语句,那么实际上就组成了一个if-else-if语句链,如下所示:
if(表达式1)
语句1;
else if(表达式2)
语句2;
...
else
语句n;
在这种情况下,else子句总和最后一个没有else子句的if语句配对。利用大括号可以改变这种关联性。
(6)while语句
while语句的一种形式是:
while(表达式)
循环体语句;
只要表达式的值不等于0,那么计算机将一直执行循环体语句。因为表达式的求值在循环体执行前进行,因此可能一次也不执行语句。
while语句的另一种形式是:
do{
循环体语句;
}while(表达式)
在这种形式下,如果表达式的值不为0,那么计算机将不断地执行循环体语句。因为表达式在循环体执行后才被求值,所以采用do-while语句时循环体至少会执行一次。
(7)for语句
for语句的一般形式如下:
for(初值表达式;条件表达式;步长表达式)
循环体语句;
在循环开始的执行的时候将对初值表达式求值;接下来,将对条件表达式求值如果求值结果不为0,那么计算机将执行循环体语句,然后对步长表达式求值。只要条件表达式的结果不为0,那么计算机将一直执行循环体语句并对步长表达式求值,需要注意的是,因为条件表达式的求值在循环体语句的执行之前,因此如果一开始条件表达式的值就是0的话,计算机将一次也不执行循环体。
for语句的初值表达式中可以声明只在该循环内部起作用的局部变量。如:
for(int i=0;i<100;i++)
...
上面的语句声明了整型变量i,并在开始循环的时候将其初始化为0.循环内的任何语句都可以访问该变量,但是在循环结束后,该变量将不可访问。
(8)switch语句
switch语句的一般形式如下:
switch(条件表达式){
case 常量表达式1:语句段1;break;
case 常量表达式2:语句段2;break;
...
case 常量表达式n:语句段n;break;
default: 语句段n+1;break;
}
计算机将首先对条件表达式求值,然后将结果与case语句后面的常量表达式1、常量表达式2......常量表达式n进行比较。如果该表达式的值与某个常量表达式相匹配,计算机将执行该case语句后面的语句。如果一个都不匹配,计算机将执行default语句后面的语句。如果没有default语句,那么计算机将不执行switch语句中的任何语句。
常量表达式的类型必须是整型,而且任意两个case语句中的常量表达式值不应该相等。如果在某个case语句后面没有break语句的话,那么计算机将接着执行下面case语句中的语句。
7.变量定义与声明
(1)声明
当处理结构、联合、枚举数据类型定义和typedef语句的时候,编译程序并不分配任何存储空间。这些语句只是告诉编译程序要定义某个特定类型的结构,并给该结构命名。这些数据类型既可以在函数内部定义,也可以在函数外部定义,如果是前者,那么该类型只能在函数内部起作用;如果是后者,那么该类型在整个编译单元中都起作用。
当声明了某个数据类型之后,可以声明该数据类型的变量。声明某个数据类型的变量将使得编译程序为该变量分配内存空间(如果所声明的变量是一个外部变量,则编译程序将根据具体情况决定是否为其分配内存空间)。
在定义某个结构、联合或者枚举类型数据类型时,如果在结尾的分号前面列出变量的名字,那么编译程序也将为这些变量分配内存空间。
(2)简单变量定义及初始化
以下是C语言的基本数据类型:
类型 | 意义 |
int | 整形数 |
short int | 短整形数 |
long int | 长整形数 |
long long int | 长长整形数 |
unsigned int | 无符号整形数 |
float | 浮点数,可以带有小数点 |
double | 双精度浮点数 |
long double | 扩展双精度浮点数 |
char | 字符类型 |
void | 无类型,用于表明某个函数没有返回值,或者用于丢弃某个表达式的结果,也可以用于通用类型指针(void *) |
_bool | 布尔类型,可以用来保存0和1(false和true) |
使用如下格式的语句即可声明基本数据类型的变量:
类型名 变量名表;
例如:
int a,b; //定义两个整型变量a和b,用于 存放整数
float x; //定义一个双精度浮点型变量x,用于存放实数
在声明的时候也可以同时初始化该变量:
类型名 变量名=初始值;
例如:
int number=20;
使用下面的形式可以在一条语句中同时声明和初始化多个变量:
类型名 变量名1=初始值1,变量名2=初始值2,...;
头文件stdbool.h可以让程序中更加方便地使用布尔类型。该文件中定义了宏BOOL、TRUE、FALSE。
(3)数组定义及初始化
数组中可以包含任何基本数据类型或者导出数据类型。
一维数组声明采用如下形式:
类型名 数组名[数组长度];
类型名指定数组中每个元素的类型;数组名是数组变量的名称,是一个合法的标识符;数组长度是一个整型常量表达式,确定数组的大小。
在定义数组的同时可以进行初始化,采用如下形式:
类型名 数组名[数组长度]={初始化表达式,初始化表达式...};
数组长度表达式代表了数组中所能容纳的元素个数,如果给出了数组的初始值列表,那么可以省略数组长度。在没有指定数组长度的情况下,编译程序根据初始化列表中元素的个数或者指定的最大下标来决定数组的长度。
在定义全局数据的时候,每一个初始化表达式都必须是常量表达式。初始值列表中的元素个数可以小于数组的长度,但是不能大于该长度。如果给出的元素个数小于数组的长度,那么编译程序将只初始化给定数目的数组元素,其他的数组元素将被设置为0。
字符数组的初始化是一个特例。C语言允许使用字符串常量来初始化字符数组,如下所示:
char student[]="LiHua";
//定义了一个字符数组student,该数组的初始化值为:'L','i','H','u','a'和'\0'
二维数组的一般声明形式如下:
类型名 数组名[行长度][列长度];
在定义二维数组时,也可以对数组元素赋初值,二维数组的初始化方法有两种。
①分行赋初值的一般形式为:
类型名 数组名[行长度][列长度]={{初值表0},...{初值表n}...};
把初值表n中的数据依次赋给第n行的元素。如:
int a[3][3]]={{1,2,3},{4,5,6},{7,8,9}};
②顺序赋初值的一般形式为:
类型名 数组名[行长度][列长度]={初值表};
根据数组元素在内存中的存放顺序,把初值表中的数据依次赋给数组元素。如:
int a[3][3]={1,2,3,4,5,6,7,8,9};
等价于
int a[3][3]={{1,2,3},{4,5,6},{7,8,9}};
多维数组的一般声明形式如下:
类型名 数组名[维度1长度][维度二长度]...[维度n长度];
(4)指针变量定义及初始化
定义指针变量的一般形式为:
类型名 *指针变量名
类型名指定指针变量所指向变量的类型,必须是有效的数据类型,既可以是基本数据类型,也可以是扩展数据类型。指针变量名是指针变量的名称,和C语言定义一般变量的规则一样,必须是一个合法的标识符。例如:
int *pt; //声明了一个指向int类型变量的指针pt
struct point *pt; //声明了一个指向point结构类型的指针pt
指向数组的指针被声明为指向该数组所容纳元素类型的指针。如:
char *pt[100]; //声明了一个包含100个字符指针元素的数组
struct point(*fnPrt)(int); //声明了一个指向返回值类型为struct point的函数的指针,该函数接受一个整型参数
void *是通用的指针类型,C语言保证类型的指针都可以保存到void *类型中,并且随后将其从void *类型的指针中取出来而不改变原来的值。
除了上面的特例之外,C语言不允许不同类型指针之间的转换。
(5)结构变量定义及初始化
结构的一般声明形式如下:
struct 结构名{
类型名 结构成员名1;
类型名 结构成员名2;
...
类型名 结构成员名n;
}变量列表;
结构中包含所有声明的成员变量。每个成员变量声明由一个类型名加上一个或者多个成员变量名组成。
如果要声明结构变量,可以在结构定义的时候,在结束的分号之前加上这些变量的名字,也可以在定义结构之后使用如下形式的语句声明结构变量:
struct 结构名 变量列表;
如果在定义结构类型的时候没有指定名字的话,就不能使用上述形式。在这种情况下,必须在定义结构类型时声明该类型的所有变量。
结构变量初始化的方式与数组初始化很类似,可以使用一对大括号将结构成员变量的初始值列表包围起来。在声明全局结构变量的时候,每一个成员变量的初始化表达式都必须是常量表达式。
C语言允许使用一个同类型的结构变量初始化另一个结构变量,如:
struct date tomorrow=today;
(6)联合变量定义和初始化
联合的一般声明形式如下:
union 联合名{
成员声明
成员声明
...
}变量列表;
上面的形式可以用来定义名为“联合名”的联合,该联合包含所有列出的成员变量。联合中的所有成员共享同一块内存空间,C语言编译程序保证分配给联合的内存能够容纳其最大的成员变量。
如果要声明联合变量,可以在联合定义的时候,在结束的分号之前加上这些变量的名字,也可以在定义联合之后使用如下形式的语句声明联合变量:
union 联合名 变量列表;
编程者应该保证访问联合变量时采用的成员变量和最后一次向其中存入值时采用的成员变量相同。在声明联合变量的时候,可以使用大括号括起来的值初始化联合的一个成员变量,如果联合变量是一个全局变量,那么初始化表达式必须是一个常量表达式。如:
union shared{
long long int i;
long int w[2];
}swap={0xfffffff};
//声明了一个名为swap的联合,并将其成员i初始化为十六进制0xfffffff
如果要初始化其他的成员变量,可以在初始化的时候给出该成员变量的名字,如:
union shared swap2={.w[0]=1,.w[1]=2;}
同类型的联合变量可以用于初始化另一个联合变量,如:
union shared swap2=swap;
(7)枚举变量定义和初始化
定义枚举数据类型的一般格式如下:
enum 枚举名{枚举值1,枚举值2,...}变量列表;
上面的语句形式定义了名为枚举名枚举类型,其枚举值分别为枚举值1、枚举值2等。每一个枚举值应该是一个合法的标识符,或者是一个标识符后面跟上一个等号,再加上一个常量表达式。变量列表本身是可选的,它代表一组该类型的变量(也可以同时初始化)
编译程序将从0开始逐个给枚举值赋值。如果某个枚举值标识符后面跟有等号和常量表达式,那么编译程序就将该常量表达式的值作为该枚举值的值。该枚举值后面的枚举值从这个枚举值开始逐个加1,重新编号。编译程序将枚举值当做常量。
如果要声明一个枚举常量(假定该枚举类型已经在前面定义过),可以采用如下方式:
enum 枚举名 变量列表;
某个枚举变量的值只能是定义时列出的枚举值之一。
(8)存储类型及作用域
存储类型用于描述编译程序为变量分配内存的方式,该术语也可以用于描述某个特定函数的使用范围。C语言一共有4种存储类型:auto、static、extern、register。在声明的时候可以省略存储类型,这时编译程序将使用默认的存储类型。
作用域用于描述某个特定的标识符在程序中的可见范围。定义在任何函数或者语句块外面的标识符可以在同一个文件中随后的任意地方被引用;定义在某个语句块内的标识符只能在该语句块内被引用,因此在该语句块外可以定义同名的标识符。
标号和形式参数在整个语句块中都可以引用。
标号名、结构名和结构成员名、联合与枚举类型的名字以及变量名和函数名只要求在同类中唯一。
(9)函数
当为函数指定存储类型的时候,只能使用关键字static或者extern。
声明为static的函数只能在定义该函数的文件内使用。
声明为extern(如果不指定存储类型,默认为extern)的函数可以在其他文件中使用。
(10)变量
可用于变量声明的存储类型关键字以及各类变量的作用域和初始化方法如下:
存储类型 | 声明方式 | 引用方法 | 初始化 | 说明 |
static | 语句块外部 | 任何位置 | 常量表达式 | 变量在程序开始运行时初始化 |
语句块内部 | 语句块内部 | 变量的值在多次运行进入语句块时保持 变量的初始值为0 | ||
extern | 语句块外部 | 文件的任何位置 | 常量表达式 | 变量必须在某处声明时不使用extern关键字,或者某处使用extern但是进行了初始化 |
语句块内部 | 语句块内部 | |||
auto | 语句块内部 | 语句块内部 | 任何表达式 | 当执行流程进入该语句块的时候执行初始化操作,没有默认值 |
register | 语句块内部 | 语句块内部 | 任何表达式 | 不一定确保放在寄存器中;根据实现不同,对于能够声明的变量类型有各种限制;不能取该类型变量的地址;当执行流程进入该语句块的时候执行初始化操作,没有默认值 |
没有指定 | 语句块外部 | 本文件的任何位置,如果包含了恰当的声明,也可以从其他的文件中引用 | 常量表达式 | 声明只能出现一次,程序开始执行的时候进行初始化,默认值为0 |
语句块内部 | 语句块内部 | 任何表达式 | 当执行流程进入该语句块的时候执行初始化操作,没有默认值 |
(11)typedef语句
typedef语句用于给基本数据类型和导出数据类型定义一个新的名字。这个语句本身并不创造新的数据类型,只是给已经存在的数据类型起一个新的名字。因此,编译程序对使用新名字声明的变量按照使用原来名字声明的变量同样的方式对待。
使用typedef语句的一般形式如下:
typedef 老的变量类型名 新的变量类型名;
例子如下:
typedef struct{
float x;
float y;
}POINT;
//给一个结构类型富裕名字POINT,该结构包含两个分别名为x和y的浮点数成员变量
随后可以使用POINT来声明新的变量,如:
POINT origin{0.0,0.0};
8.函数定义、调用和原型
(1)函数定义
函数定义的一般形式如下:
类型名 函数名(类型名1 形参1,类型名2 形参2,...){
变量声明;
语句;
语句;
...
return 表达式;
}
上面的定义形式中,函数的名字为“函数名”,“类型名”即该函数的返回值类型,函数的形式参数是“形参1”、“形参2”等,其类型分别为“类型名1”、“类型名2”等。
通常在函数开始的地方声明内部使用的局部变量。但是C语言规范并不要求这一点。局部变量可以在函数的任何地方声明,只要这些声明语句出现在使用它们的那些语句之前即可。
①如果函数没有返回值,那么返回类型名为void
②如果在参数列表的括号中指定void,那么函数不接收参数
③如果函数的参数类型为一维数组,那么在参数列表中不需要说明该数组的长度
④如果将多维数组作为参数,那么第一个维度上的长度不需要指定
在函数定义的前面可以加上关键字inline。该关键字指示编译程序将函数的实际代码插到适当的位置,而不是去调用函数,这样可以获得更快的执行速度。
(2)函数调用
函数调用的一般形式如下:
函数名(实参1,实参2,...)
上面的语句形式调用名为“函数名”的函数,并将值“实参1”、“实参2”等作为参数传递给该函数。如果不需要参数的话,应该在后面跟上一对空的小括号。
函数的参数传递按照值引用的方式进行,在函数内部不能修改实际的参数。如果给函数传递一个指针参数,函数内部可以对该指针指向的位置进行修改,但是不能对实际的指针进行修改。
(3)函数原型
如果程序中的函数调用御景城出现在函数定义之前,或者调用另外一个文件的函数,那么应该写出函数的原型声明。函数原型声明的一般形式如下:
类型名 函数名(类型名1 形参1,类型名2 形参2,...);
原型声明语句告诉编译程序该函数的返回值类型、接受的参数个数以及每个参数的类型。如:
double mypower(double x,int n);
该语句告诉编译程序mypower是一个函数的名字,该函数的返回值类型为double。mypower()函数接收两个参数,第一个参数的类型为double,第二个参数的类型为int。原型声明语句中的参数名称没有实际用途,可以省略,如:
double mypower(double,int);
在遇到函数调用语句后,如果前面编译程序已经看到过函数的定义或者原型声明,那么调用语句中每个参数将被自动转化为函数所期待的类型(如果符合转换条件)。
9.预处理命令
预处理器在编译程序编译代码之前对其进行处理。预处理器一般完成如下几类工作:
①将所有以反斜线“\”结尾的行与后面一行合并为同一行
②将程序分解为记号流
③删除所有的注释,并用单个空格替换它们
④处理预处理指令并展开所有的宏
所有的预处理指令都必须以#开始,在#之后有一个或者多个空格和tab字符
(1)#define指令
#define指令的一般形式如下:
#define 宏名 宏定义字符串
上面的语句定义了一个名为“宏名”的宏,并将该宏与其名字后的第一个空格后直到该行结束的字符串等价起来。C语言预处理将用这个字符串替换随后程序中任何位置出现的宏名。
#define指令的另外一种常见形式如下:
#define 宏名(参数1,参数2,...,参数n)宏定义字符串
上面的语句定义了一个名为宏名的宏,该宏接收一组参数,在随后的程序中任何出现宏名的地方,预处理器使用后面的宏定义字符串替换该宏名,并使用实际的参数替换宏定义字符串中的参数。
(2)#if指令
#if指令的一种常用形式如下所示:
#if 常量表达式
程序段
#endif
预处理器将对常量表达式求值,如果结果为非0,那么#if和#endif之间的语句将被处理。否则,预处理器和编译程序都不会处理这些语句。
#if指令后面也可以后#elif指令和#else指令,一般形式如下:
#if 常量表达式1
程序段1
#elif 常量表达式2
程序段2
...
#else
程序段n
#endif
(3)#ifdef指令
#ifdef语句的一般使用形式如下:
#ifdef 标识符
程序段
#endif
如果标识符代表的宏已经被定义过了(可能是通过#define语句,也可能是通过命令行上的-D选项),#ifdef和#endif之间的语句将被编译,否则,这些语句将被忽略。如同#if指令一样,#ifdef指令后面也可以有#elif指令和#else指令。
与#ifdef相对应的还有#ifndef指令,意义与#ifdef相反。
(4)#include指令
#include语句的一般使用形式如下:
#include "文件名"
预处理器将在某些特定目录中寻找指定的文件。一般预处理器将在当前源程序所在的目录中寻找该文件。如果在当前目录中没有找到该文件,那么编译器健在标准目录(某些特定的目录,由编译器实现厂商决定)中寻找该文件。在找到该文件后,文件的内容将被插入到#include指令所在的位置。随后,预处理器将对这些内容进行分析。所以一个被#include指令引入的文件中,还可以包含另外一个#include指令
#include语句的另外一个常见形式如下:
#include<文件名>
这时,预处理器将在标准目录中寻找这些文件。
C语言的预处理指令还有#line、#error和#progma。这些命令主要用于构造C程序。
10.常用标准库函数
C语言程序设计中,大量的功能实现需要库函数的支持,包括最基本的scanf()、printf()函数。虽然库函数不是C语言的一部分,但每一个实用的C语言系统都会根据ANSI C提出的标准库函数,提供这些标准库函数的实现。因此对编程者来说,标准库函数已成为C语言中不可缺少的组成部分。使用的C语言系统一般还会根据其自身特点提供大量相关的库函数,包括图形图像处理函数、输入输出通信函数、计算机系统功能调用函数等,不同的语言系统会有不同的库函数。限于篇幅,这里只提供了ANSI C的一些常用的标准库函数,如需了解其他语言系统的库函数,可查阅相关C语言系统的使用手册。
注:下面各表中所列函数名前的类型说明是函数返回结果的类型,程序中调动这些库函数时不必书写类型。
(1)数学函数
数学函数中,除abs()外,均在math,h中说明,对应的编译预处理命令为:
#include<math.h>
(2)输入输出函数
输入输出函数在头文件stdio.h中说明,对应的编译预处理命令为:
#include<stdio.h>
①格式化输入输出函数:
②字符(串)输入输出函数:
③文件操作函数:
(3)字符判别函数:
字符判别函数在ctype.h中说明,对应的编译预处理命令为:
#include<ctype.h>
(4)字符串操作函数
字符串操作函数在头文件string.h中说明,对应的编译预处理命令为:
#include<string.h>
(5)数值转换函数
把内容为数值的字符串转换成相应数值的函数在头文件stdlib.h中说明,对应的编译预处理命令为:
#include<stdlib.h>
(6)动态内存分配函数
ANSI C的动态内存分配函数共4个,在头文件stdlib.h中说明, 对应的编译预处理命令为:
#include<stdlib.h>
(7)过程控制函数
过程控制函数在头文件process.h中说明,对应的编译预处理命令为:
#include<process.h>
写在最后,
因本系列文章主要为复习,故重点关注C语言基础语法使用,涉及结构、数据类型、指针、函数、文件等知识的详细内容就不再赘述,笔记仅作为参考,若读者发现内容有误请私信指正,谢谢!