z=x>y? x:y // 把x和y中的大者赋给z
1.关键字变更历史
1999年12月16日,ISO推出了C99标准,该标准新增了5个C语言关键字:
inline restrict _Bool _Complex _Imaginary(注意bool 从来不是C语言的关键字)
2011年12月8日,ISO发布C语言的新标准C11,该标准新增了7个C语言关键字:
_Alignas _Alignof _Atomic _Static_assert _Noreturn _Thread_local _Generic
2.关键字列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
根据关键字的作用,可以将关键字分为数据类型关键字和流程控制关键字两大类。
1 数据类型关键字
A基本数据类型(5个)
void :声明函数无返回值或无参数,声明无类型指针,显式丢弃运算结果
char :字符型类型数据,属于整型数据的一种
int :整型数据,通常为编译器指定的机器字长
float :单精度浮点型数据,属于浮点数据的一种
double :双精度浮点型数据,属于浮点数据的一种
B 类型修饰关键字(4个)
short :修饰int,短整型数据,可省略被修饰的int。
long :修饰int,长整形数据,可省略被修饰的int。
signed :修饰整型数据,有符号数据类型
unsigned :修饰整型数据,无符号数据类型
C 复杂类型关键字(5个)
struct :结构体声明
结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。而共用体变量所占的内存长度等于最长的成员的长度。
例:
一,先声明结构体类型,再定义该类型的变量
声明结构体类型:
struct Student
{
int num; / 学号为整数
char name[20]; / 姓名为字符串
char sex; / 性别为字符串
int age; / 年龄为整型
float score; / 成绩为实型
char addr[30]; / 地址为字符串
}; / 注意最后有一个分号
上面只是声明了一个结构体类型,它相当于一个模型,并没有定义变量。
定义上面的变量:
struct Student student1,student2;
二,在声明结构体的同时定义变量
struct Student
{
int num; / 学号为整数
char name[20]; / 姓名为字符串
char sex; / 性别为字符串
int age; / 年龄为整型
float score; / 成绩为实型
char addr[30]; / 地址为字符串
}student1,student2;
三,不指定类型名而直接定义结构体类型变量
struct
{
int num; / 学号为整数
char name[20]; / 姓名为字符串
char sex; / 性别为字符串
int age; / 年龄为整型
float score; / 成绩为实型
char addr[30]; / 地址为字符串
}student1,student2;
初始化和引用:
student1.num = 10010;
student1.birthday.month = 06; 如成员本身又属于一个结构体类型
结构体数组:
声明结构体:
struct person
{
char name[20];
int count;
};
定义结构体数组并初始化:
struct person a[3] = { {"Ling",0},{"Zhang",0},{"Xia",0}};
name = a[1].name; 即name为 zhang
结构体指针:
#include<stdio.h>
#include<string.h>
int main()
{
struct Student / 声明结构体变量
{
long num;
char name[20];
char sex;
float score;
};
struct Student stu_1; / 定义struct Stdent类型的变量stu_1
struct Student *p; / 定义指向struct Student类型数据的指针变量p
p = &st_1; / p指向stu_1
stu_1.num = 10101; / 对结构体变量的成员赋值
strcpy(stu_1.name, "Li Lin"); / 用字符串复制函数给stu_1.name赋值
stu_1.sex = 'M';
stu_1.score = 89.5;
/ 以下三种输出方法等价,(*p)必须有括号,-> 为指向运算符
printf("No:%ld\n name:%s\n sex:%c\n score:%5.1f\n" , stu_1.num ,stu_1.name , stu_1.sex, stu_1.score);
printf("No:%ld\n name:%s\n sex:%c\n score:%5.1f\n" , (*p).num , (*p).name , (*p).sex, (*p).score);
printf("No:%ld\n name:%s\n sex:%c\n score:%5.1f\n" , p->num ,p->name , p->sex, p->score);
}
union :共用体声明
用同一段内存单元存放不同类型的变量。比如把一个短整型,一个字符型和一个实型变量放在同一个地址开始的内存单元中,3个变量在内存中占的字节数不同,但都从同一地址开始存放,也就是使用覆盖技术,后一个数据覆盖了前面一个数据。结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。而共用体变量所占的内存长度等于最长的成员的长度。
例如:
一起声明和定义共用体:
union Data
{
int i; / 表示不同类型的变量 i,ch,f可以存放到用一段存储单元中
char ch;
float f;
}a,b,c; / 在声明类型同时定义变量
也可以类型声明与定义分开:
union Data / 声明共用体类型
{
int i;
char ch;
float f;
};
union Data a,b,c; / 用共用体类型定义变量
引用:
a.i (引用共用体变量中的整型变量i)
a.ch (引用共用体变量中的字符变量ch)
a.f (引用共用体变量中的实型变量f)
如:
union Data
{
int i;
char ch;
float f;
}a;
a.i = 97;
printf("%d",a.i); (输出整数97)
printf("%c",a.ch); (输出字符‘a’)
printf("%f",a.f); (输出实数0.000000)
因为97是赋给a.i的,因此按整数形式存储在变量单元中,最后一个字节是“01100001”,
如果用%d输出整数97,用%c格式输出a.ch ,就会输出字符a。如果用%f格式输出a.f ,
系统就会按浮点数形式输出数值部分0,故输出0.000000.
(1)同一内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一个成员,而不是同时存放几个。
(2)可以对共用体变量初始化,但初始化表中只能有一个常量。
(3)共用体变量中起作用的成员是最后一次赋值的成员,在对共用体变量中的一个成员赋值后,原有变量存储单元中的值就取代。
(4)共用体变量的地址和它的各成员的地址都是同一地址。
(5)不能对共用体变量名赋值,也不能企图引用变量名来得到一个值。
enum :枚举声明
声明枚举变量:
enum Weekday
{
sun,mon,tue,wed,thu,fri,sat
};
定义枚举变量:
enum Weekday workday,weekend;
也可以不声明有名字的枚举类型,而直接定义枚举变量:
enum
{
sun = 7,mon = 1,tue,wed,thu,fri,sat
}workday,weekend;
可以指定枚举元素的值sun=7,mon =1, 最后的sat会变为6,里面的元素自动加1
可用来作判断比较:
if(workday==mon)...
if(workday>sun)...
(1) 枚举常量,不能对它们赋值;
(2)每一个枚举元素都代表一个整数,C语言编译按定义时的顺序默认它们的值为0,1,2,3,4...;
(3)也可以人为地指定枚举元素的数值,在定义枚举类型时显式地指定。
(4)枚举元素可以用来作判断比较。
typedef :声明类型别名
sizeof :得到特定类型或特定类型变量的大小。获取了数据在内存中所占用的存储空间,以字节为单位来计数。
D 存储级别关键字(6个)
auto : 指定为自动变量,由编译器自动分配及释放。通常在栈上分配。在调用该函数时,系统会给这些变量分配存储空间,在函数调用结束是自动释放这些空间。程序中大多数变量属于自动变量,auto可以省略,如auto int a; 与int a;等价。属于动态存储空间。
static : 指定为静态变量,分配在静态变量区,修饰函数时,指定函数作用域为文件内部。属于静态存储类别,静态局部变量的值在函 数调用完后占用存储单元不释放,保持上次调用结束时的值。属于静态存储区。
如: int f(int a)
{ int b=0;
static c=3;
b=b+1; //每次调用该函数,b的值都为0+1
c=c+1; // 每次调用为c+1,如第一次赋值3,第二次调用为4=3+1,第三次调用为5=4+1
return(a+b+c);
} 静态局部变量在编译时赋初值的只赋值一次,每次调用不再赋初值而只是保留上次调用结束时的值。如未明确初始化,它的字节都被设定为0。当调用次数多时往往弄不清楚静态变量的值是多少。而自动变量(上auto)每次调用函数时重新给一次初值。
在定义内部函数时,在函数名和函数类型的前面加static,如:static int fun(int a, int b); 表示fun 是一个内部函数,不能被其他文件调用,使函数的作用域仅限于所在文件,这样,在不同的文件中即使有相同名字的函数也互不干扰,这样就不必担心跟别人写的文件模块有相同函数名。
register : 指定为寄存器变量,建议编译器将变量存储到寄存器中使用,也可以修饰函数形参,建议编译器通过寄存器而不是 堆栈传递参数。 一般情况下,变量的值是放在内存中的,当程序中用到变量值时,由控制器发出指令将内存中该变量的值送到运算器中,或存数时由运算器到内存(运算器<=>内存)。因寄存器的存取速度远高于对内存的存取速度,为提高执行效率,用register 声明,如: register int f; 寄存器存储在CPU中的寄存器。
extern : 指定对应变量为外部变量,即在另外的目标文件中定义,可以认为是约定由另外文件声明的。表示把该变量的作用域 扩展到此位置,声明extern时,类型名可写可不写,因为它不是定义变量,可以不指定类型,如:在file1.c 中定义外部变量 int a ; 然后在file2.c中用extern int A; 或extern A; 把外部变量A作用域扩展到从此处开始。
如在file1.c中定义全局变量A时,使用了static声明,该变量只能用于本文件,再在file2.c中用extern A;就会发生错误。 可以在多人设计独立的模块程序时加入static声明,即使在多个文件中使用相同的外部变量名也不相干,可为程序的模 块化和通用性提供方便。
C语言规定,如果定义函数时省略extern,则默认为外部函数。比如可以在#nclude 的头文件中直接声明函数原型,而不用extern,然后在其他 文件中就可以调用。
const : 与volatile合称“cv特性”,指定变量不可被当前线程/进程改变(但有可能被系统或其他线程/进程改变)。有类型,占存储空间,只是不允许改变其值。如const float pi=3.1416; //行末有分号
volatile : 与const合称“cv特性”,指定变量的值有可能会被系统或其他进程/线程改变,强制编译器每次从内存中取得该变量的值。(下面有详细解释)
2 流程控制关键字
A 跳转结构(4个)
return :用在函数体中,返回特定值(或者是void值,即不返回值)。跳出循环并且跳出函数,同时返回函数值。
continue :结束当前循环(本次循环),开始下一轮循环,不是整个循环
break :跳出当前循环(整个循环)或switch结构
goto :把控制无条件转移到同一函数内的被标记的语句。不建议使用 goto 语句。因为它使得程序的控制流难以跟踪,使程序难以理解和难以修改。
如:
goto label;
...
label: 下面执行函数
在这里,label 可以是任何除 C 关键字以外的纯文本,它可以设置在 C 程序中 goto 语句的前面或者后面
B 分支结构(5个)
if :条件语句
else :条件语句否定分支(与if连用)
switch :开关语句(多重分支语句)
case :开关语句中的分支标记
default :开关语句中的“其他”分治,可选。
C 循环结构(3个)
for :for循环结构,for(1;2;3)4;的执行顺序为1->2->4->3->2...循环,其中2为循环条件
do :do循环结构,do 1 while(2); 的执行顺序是 1->2->1...循环,2为循环条件
while :while循环结构,while(1) 2; 的执行顺序是1->2->1...循环,1为循环条件
以上循环语句,当循环条件表达式为真则继续循环,为假则跳出循环。
这里可以提下volatile关键字
更详细可参考:https://blog.csdn.net/qq_29350001/article/details/54024070
volatile总是与优化有关,编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以消除一些代码。但有时这些优化不是程序所需要的,这时可以用volatile关键字禁止做这些优化。
volatile的本意是“易变的” 因为访问寄存器要比访问内存单元快的多,所以编译器一般都会作减少存取内存的优化,但有可能会读取数据。当要求使用volatile声明变量值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。精确地说就是,遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问;如果不使用valatile,则编译器将对所声明的语句进行优化。(简洁的说就是:volatile关键词影响编译器编译的结果,用volatile声明的变量表示该变量随时可能发生变化,与该变量有关的运算,不要进行编译优化,以免出错)
变量如果加了 volatile 修饰,则会从内存重新装载内容,而不是直接从寄存器拷贝内容。
看两个事例:
1> 告诉compiler不能做任何优化
1 2 3 4 5 6 7 8 9 10 11 12 |
|
2> 用volatile定义的变量会在程序外被改变,每次都必须从内存中读取,而不能重复使用放在cache或寄存器中的备份
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|