由ASCII标准定义的C语言关键字共32个:
数据关键字12个:char,double,float,enum,unsigned ,int,long,short,signed,struct,union,void
控制语句关键字12个:for,do,while,break,continue,if,else,goto,switch,case,default,return
存储类型关键字4个:auto,extern,regsiter,static
其他关键字4个:const,sizeof,typedef,volatile
一、数据关键字(12个):
- char :声明字符型变量或函数
char p = 'a'; //注意使用单引号 char getSigleChar(); //函数返回值为char类型 char *p = 'abcd'; //也可以使用双引号 char* getCharArray(); //函数返回值为char*类型 /* char* getCharArray() { char* str = NULL; char x[127]; scanf("%s", &x); str = x; return str; } */
- short :声明短整型变量或函数
short i = 123; //short的范围是范围-32768~+32767(总范围2的16次方),标准定义不得低于16位,两个字节 signed short i = 123; //i可以表示-32768~+32767 unsigned short i = 123; //只取整数,i可以表示0~65535 short getShortNum(); //函数返回值是short类型 signed short getSignedShortNum(); //函数返回值是signed short类型 unsigned short getUnsignedShortNum(); //函数返回值是unsigned short类型
- int: 声明整型变量或函数
int i = 123; //int的数据范围为-2147483648~2147483647(总范围是2的32次方),占用4个字节。注意:之前的微型机中2的16次方,数据范围为-32768~32767 signed int i = 123; //signed int的数据范围是:-2147483648~2147483647 unsigned int i = 123; //unsigned int的数据范围是:0~4294967295 int getIntNum(); //函数返回值为int类型 signed int getSignedIntNum(); //函数返回值为signed int类型 unsigned int getUnisgnedIntNum(); //函数返回值为unsigned int类型 const int i= 123; //注意当const和int一起使用时一定一定一定要给i赋值,const int i;是错误的
- long :声明长整型变量或函数
long i = 123; //long的数字范围是-2147483648~2147483647(总数范围是2的32次方)占4个字节 signed long i = 123; //signed long的范围是-2147483648~2147483647 unsigned long long i = 123; //unsigned long long的范围是0~4294967295 long getLongNum(); //函数返回值是long类型 signed getSignedNum(); //函数返回值是signed long类型 unsigned getUnsignedNum(); //函数返回值是unsigned long类型 long long i = 123; //long long的范围-9223372036854775808~9223372036854775807(总数范围是2的64次方)占8个字节 signed long long i = 123; //signed long long的范围-9223372036854775808~9223372036854775807 unsigned long long i = 123; //unsigned long long的范围是0~1.844674407×10¹⁹ long long getLongLongNum(); //函数返回值是long long类型 signed long long getSignedLongLongNum(); //函数返回值是signed long long类型 unsigned long long getUnsignedLongLongNum(); //函数返回值是unsigned long long类型
- float:声明浮点型变量或函数
float a = 1.23; //float的范围是1.175494351E–38到3.402823466E+38,有效位是6~7位,占4个字节,单精度浮点型 float getFloatNum(); //函数返回值是float类型
- double :声明双精度变量或函数
double i = 1.23; //double的范围是2.2250738585072014E–308~1.7976931348623158E+308,有效位是15~16个,占8个字节,双精度浮点型 double getDoubleNum(); //函数返回值是double类型
- enum :声明枚举类型
enum weekday{sun,mon,tue,wed,thu,fri,sat}; //枚举元素从零开始,定义为0,1,2,...。sun的值是0,sta的值是6。 enum weekday a,b,c; a=(enum weekday)2; //只能把枚举值赋予枚举变量,不能把元素的数值直接赋予枚举变量。如果一定要把数值赋予枚举变量,则必须使用强制类型转换。
- signed:声明有符号类型变量或函数
signed char a = 123; //char类型能存储的范围是-128~127 signed char getSignedChar(); //函数返回值是char类型
- unsigned:声明无符号类型变量或函数
unsigned char a = 233; //unsigned char的范围是0~255 unsigned char getUnsignedChar(); //函数返回值是unsigned char类型
- struct:声明结构体变量或函数
struct student { int num; char name[20]; char sex; float score; }; //注意结尾的分号
- union:声明共用体(联合)数据类型
union foo{ int i; char c; double k; }; //注意最后的分号不要忘记
- void :声明函数无返回值或无参数,声明无类型指针(基本上就这三个作用)
void getNothingFromFunc(); //作用一: 对函数返回的限定,函数无返回值 int function(void); //作用二:对函数参数的限定,函数没有入参 void* memcpy(void* dest, const void* src, size_t len); //如果函数的参数可以是任意类型指针,那么应声明其参数为void *
注意:
- 共用体和结构体都是由多个不同的数据类型成员组成,但在任何同一时刻,共用体只存放一个被选中的成员,而结构体的所有成员都存在。
- 对于共用体的不同成员赋值,将会对其他成员重写,原来成员的值就不存在了,而对于结构体的不同成员赋值是相互不影响的。
二、控制语句关键字(12个):
1、循环语句 (5个)
- for:一种循环语句(可意会不可言传)
for(int i = 0; i < 10; i++) //起始条件; 判断条件; 循环执行后执行的语句 { printf("i = %d\n", i); }
- do :循环语句的循环体
int i = 0; do{ i++; printf("Hello") }while(i < 10); //判断条件,执行10次 //先执行循环,后判断条件
- while :循环语句的循环条件
int i = 0; while(i < 10) { i++; printf("%d\n",i) } //判断条件,循环10次 //先判断条件,再执行循环
- break:跳出当前循环
for(int i = 0; i < 10; i++) //起始条件; 判断条件; 循环执行后执行的语句 { if(i > 5) { break; //break经常和if判断一起使用, } printf("i = %d\n", i); }
- continue:结束当前循环,开始下一轮循环
for(int i = 0; i < 10; i++) //起始条件; 判断条件; 循环执行后执行的语句 { if(i > 5) { continue; //continue经常和if判断一起使用, } printf("i = %d\n", i); }
2、条件语句 (3个)
- if: 条件语句
int i = 10; if(i < 11) { printf("%d\n", i); }
- else :条件语句否定分支(与 if 连用)
int i = 10; if(i < 11) { printf("%d\n", i); }else{ printf("hello"); }
- goto:无条件跳转语句
int main() { int a = 2, b = 3; if(a > b) //条件不成立 { goto aa; } printf("hello"); aa::printf("s"); //需要设置label标签, return 0; } //此时,结果结果是打印“hellos” int main() { int a = 2, b = 3; if(a < b) //条件成立 { goto aa; } printf("hello"); aa::printf("s"); //需要设置label标签, return 0; } //此时,结果结果是打印“s”
3、开关语句 (3个)
- switch :用于开关语句
- case:开关语句分支
- default:开关语句中的“其他”分支
double score; printf("请输入分数:\n"); scanf("%lf",&score); switch((int)(score/10)) //switch()的参数类型不能为实型,可以是整型和字符型,这里强制类型转化为int { case 10: case 9: printf("A(最好)\n"); break; case 8: printf("B(优秀)\n"); break; case 7: printf("C(良好)\n"); break; case 6: printf("D(及格)\n"); break; case 5: case 4: case 3: case 2: case 1: case 0: printf("E(不及格)\n"); break; default: printf("Error!\n"); }
4、返回语句(1个)
- return :子程序返回语句(可以带参数,也看不带参数)
int function1() { int i = 1; return 1; //return(i); //这样也可以 } void function2() { int i = 1; //return; //这样也可以,也可以去掉这一句 }
注意:很多教程都提到了void main(),但是这样写是完全错误的,因为在C++之父Bjarne Stroustrup (已经仙逝)在他的主页上的 FAQ 中明确地写着 “The definition void main( ) { /* ... */ } is not and never has been C++, nor has it even been C”。所以这种定义基本是不存在的,至于为什么能通过编译,这就和编译器(有些会自动添加return 0)有关系了。有兴趣的可以看看C99关于main函数的定义标准(相比之前,标准定义只有从int main(void)变为int main(),其他基本没有修改)。
三、存储类型关键字(4个)
- auto :声明自动变量 一般不使用 。
auto double a=3.7; //表示a为一个自动存储的临时变量。 /* 在C++11标准的语法中,auto被定义为自动推断变量的类型。 auto x = 5.2;//这里的x被auto推断为double类型 不过C++11的auto关键字时有一个限定条件,那就是必须给申明的变量赋予一个初始值,否则编译器在编译阶段将会报错。 */
- extern:声明变量或函数是在其他文件中声明(也可以看做是引用变量)
//A.cpp extern int i; int main() { i=100;//试图使用B中定义的全局变量 } //B.cpp int i; extern int f(); //如果函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义,没有其它作用。即下述两个函数声明没有明显的区别和int f();是一样的。 //当然,这样的用处还是有的,就是在程序中取代include “*.h”来声明函数,在一些复杂的项目中,最好所有的函数声明前添加extern修饰。 /* 在C++环境下使用C函数的时候,常常会出现编译器无法找到obj模块中的C函数定义,从而导致链接失败的情况,C++语言在编译的时候为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,因此会造成链接时找不到对应函数的情况,此时C函数就需要使用extern "C"进行链接指定,这告诉编译器,请保持我的名称,不要给我生成用于链接的中间函数名。 */ 下面是一个标准的写法: //在.h文件的头上 #ifdef __cplusplus #if__cplusplus extern "C"{ #endif #endif/*__cplusplus*/ ... ... //在.h文件结束的地方 #ifdef__cplusplus #if__cplusplus } #endif #endif/*__cplusplus*/
- register:声明积存器变量
/* resgister修饰符暗示编译程序相应的变量将频繁的使用,如果可能的话,应将其保存在CPU的寄存器中,以加快存储速度。 */ #ifdef NOSTRUCTASSIGN memcpy(d, s, i) { register char *d; register int i; } #endif //register变量必须是一个单个的值,并且长度应该小于或者等于整型的长度。 //因为register变量可能不存放在内存中,所以不能用“&”来获取register变量的地址。 //实际上,许多编译程序都会忽略register修饰符,因为尽管它完全合法,但它仅仅是暗示而不是命令。
- static :声明静态函数
static char str[10]; //static定义的变量默认初始化为0 //static变量存放在静态存储区,所以它具备持久性和默认值0。 /* 函数分为内部函数和外部函数 当一个源程序由多个源文件组成时,C语言根据函数能否被其它源文件中的函数调用,将函数分为内部函数和外部函数。 */ static void calledInSameFile(); //内部函数(静态函数) //内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。 void calledInDiffFile(); //或者下面一种 extern void calledInDiffFile(); //外部函数(非静态函数)
注意:static在C++中的含义会不一样,因为C++是面向对象语言,所以在类中的static会有额外的含义。
#include<iostream> using namespace std; class Myclass { public: Myclass(int a, int b, int c); void GetSum(); private: int a; int b; int c; static int Sum; //声明静态数据成员 }; int Myclass::Sum = 0; //初始化静态数据成员 Myclass::Myclass(int a, int b, int c) { this->a = a; this->b = b; this->c = c; Sum += a + b + c; } void Myclass::GetSum() { cout<<"Sum = "<<Sum<<endl; } int main() { Myclass M(1, 2, 3); M.GetSum(); Myclass N(4, 5, 6); N.GetSum(); M.GetSum(); } /*输出结果是:(1+2+3 = 6; 6+4+5+6 = 21) Sum = 6 Sum = 21 Sum = 21 这个说明了静态数据成员在程序中只有一份拷贝, 由该类型的所有对象共享访问,也就是说,静态数据成员的值对每个对象都是一样的,它的值可以更新。 */ //体内定义,体外初始化,且必须初始化,子类为主,屏蔽父类 //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- #include<iostream> using namespace std; class Myclass { public: Myclass(int a, int b, int c); static void GetSum(); //声明静态成员函数 private: int a, b, c; static int Sum; //声明静态数据成员 } int Myclass::Sum = 0; //初始化静态数据成员 Myclass::Myclass(int a, int b, int c) { this->a = a; this->b = b; this->c = c; Sum+= a + b + c; } void Myclass::GetSum() { cout<<"Sum = "<<endl; } int main() { Myclass M(1, 2, 3); M.GetSum(); Myclass N(4, 5, 6); N.GetSum(); Myclass::GetSum(); } //结果不变,但是注意,静态成员函数无法访问数于对象的非静态数据成员,也无法访问非静态成员函数,只能调用其余的静态数据成员和静态成员函数。 //体内定义,体外实现,且必须实现,不能为虚函数,子类为主,屏蔽父类
四、其它关键字(4个)
- const :声明只读变量
//const 在前面 const int nValue; //const 修饰的是int,整型固定,但是nValue的值是也不变的 const char* pContent; //const 修饰的是char*, 指针类型固定,但是pContent是可以变的 const char* const pContent; //pContent和*pContent都被const修饰,都不可变。 //const 在后面 int const nValue; //const修饰的是nValue,在一定程度上和const int nValue相同,nValue的值不能修改 char const *pContent; //const修饰的是*,pContent是可以改变的 char* const pContent; //const修饰的是pContent,*pContent是可以改变的 char const * const pContent; //pContent和*pContent都被const修饰, 都不可以改 //const推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。
- sizeof:计算数据类型长度
//在C语言中要注意sizeof是运算符,而不是函数 int i; sizeof(i); //sizeof(对象i的类型) sizeof i; //sizeof 对象 sizeof(int); //sizeof(类型) //同种类型的不同对象其sizeof值都是一致的 char foo() { printf("foo() has been called.\n"); return 'a'; } int main() { size_tsz = sizeof(foo()); // foo()的返回值类型是char,所以sz = sizeof(char), foo()并不会被调用 printf("sizeof(foo()) = %d\n", sz); } //注意C99标准规定,函数、不能确定类型的表达式以及位域成员不能被计算sizeof值 //最新的C99标准规定sizeof也可以在运行时刻进行计算,但是最好还是认为sizeof是在编译期执行的,这样能让程序的可移植性强些,因为有些编译器没有完全实现C99标准。 //所以在32位计算机中,一个指针函数的返回值是4,但是在64位系统中结果是8。
- typedef:用以给数据类型取别名(当然还有其他作用)
typedef int size; //size是int的别名 typedef char Line[10]; //Line是具有10个char元素的数组 typedef char* pstr; //pstr是字符指针 typedef struct tagNode { char* pItem; struct tagNode* pNext; //注意这里不能写下面的别称pNode替换struct tagNode*,因为此时还没定义 }*pNode; //还可以这样做,更规范一些 struct tagNode { char* pItem; struct tagNode* pNext; }; typedef tagNode* pNode;
typedef和#define的区别:
//#define属于在预编译期的单纯的拷贝 typedef char* pStr; char string[4] = "abc"; const char* p1 = string; const pStr p2 = string; p1++; p2++; //错误,原因是指针的星号*是优先向右结合的,在*p1中const修饰的是char,在p2中const修饰的是char*,所以,p1可以++,但是p2不可以++
- volatile:说明变量在程序执行中可被隐含地改变
volatile int i=10; //当要求使用volatile声明的变量的时候,读取的数就会被立即保存起来,下次使用int a=i;时不要改变i的值(如果是指针有可能会发生变化)。 //尤其是多线程中,无法判定何时这个变量会发生变化。加上volatile修饰这个变量表示这个变量是容易被修改的 /* 一般来说,volatile用在如下的地方: 1、中断服务程序中修改的供其他程序检测的变量需要加volatile 2、多任务环境下各任务间共享的标志应该加volatile 3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都有可能有不同的意义 */
----------------------------------------------------------------------------------------------------------------------
一般说C语言的32个关键字就是指以上的这些,但是除此之外,在后来的C&C++语言的新标准中增加了一些关键字
----------------------------------------------------------------------------------------------------------------------
C99增加的关键字(5个)
- inline:内联函数
关键字inline必须与函数实现放在一起才能使函数成为内联,仅仅将inline放在函数声明前面不起任何作用。 - restrict:一种类型限定符
用于限定和约束指针,并表明指针是访问一个数据对象的唯一且初始的方式,即它告诉编译器,所有修改该指针所指向内存中内容的操作都必须通过该指针来修改,而不能通过其它途径(其它变量或指针)来修改。目的是帮助编译器进行更好的优化代码,生成更有效率的汇编代码。 - _Bool:布尔型
bool为布尔型用作逻辑判断;BOOL在<windef.h>typedef int BOOL,在<wtypes.h>typedef long BOOL;_Bool类型只有0和1这两个值。 - _Complex:新增的数据类型,用来表示复数
#include<complex.h> #include<stdio.h> int main() { double complex x = 5.2; double complex y = 5.0 * I; 大写I表示虚数单位,也就是-1的平方根 double complex z = x + y; printf("z = %f\n", z); //不知道为啥只能显示实部,无法显示虚部。以后找到解决办法再贴上 return 0; }
- _Imaginary:虚数类型,没有实部的(目前已经和_Complex合并)
#include<complex.h> double imaginary x = 5.0 * I; //大写I表示虚数单位,即-1的平方根 //现在已经无法像上面这么定义,只能通过下面的方式定义了 double complex x = 5.0 * I;
关于复数的方法,C++有关于类的实现:
#include<iosteam> #include<complex> using namespace std; int main() { complex<double> x(1, 2.1); complex<double> y(0, 5.0); complex<double> z; z = x + y; std::cout<<"x = "<<endl; std::cout<<"y = "<<endl; std::cout<<"z = "<<endl; return 0; } /*最后的结果可以实现实部和虚部的组合: x = (1,2.1) z = (0,5) y = (1,7.1) */
C11增加的关键字(7个)(下面的代码未进行测试。。。)
- _Alignas:修改声明对象对齐要求的指定符,出现在声明的语法中。
#include<stdalign.h> #inlcude<stdio.h> //每一个struct see_t 类型的对象会在16字节边界对齐 //(注意:需要注意支持 DR 444) struct sse_t { alignas(16) float sse_data[4]; }; //这种struct data的每一个对象都会在128字节边界对齐 struct data { char x; alignas(128) char cacheline[128]; //过对齐的char数组对象,不是过对齐的char对象的数组 }; int main() { printf("sizeof(data) = %zu(1 byte + 127 bytes padding + 128-byte array)\n", sizeof(struct data)); printf("alignment of sse_t is %zu\n", alignof(struct sse_t)); alignas(2048) struct data d; }
- _Alignof:返回由类型标识符所指定的类型的任何实例所要求的对齐字节数
#include <stdio.h> #include <stddef.h> #include <stdalign.h> int main(void) { printf("Alignment of char = %zu\n", alignof(char)); printf("Alignment of max_align_t = %zu\n", alignof(max_align_t)); printf("alignof(float[10]) = %zu\n", alignof(float[10])); printf("alignof(struct{char c; int n;}) = %zu\n", alignof(struct {char c; int n;})); }
- _Atomic:原子类型指定符及限定符
#include <stdio.h> #include <threads.h> #include <stdatomic.h> atomic_int acnt; int cnt; int f(void* thr_data) { for(int n = 0; n < 1000; ++n) { ++cnt; ++acnt; // 对于此例,宽松内存顺序是足够的,例如 // atomic_fetch_add_explicit(&acnt, 1, memory_order_relaxed); } return 0; } int main(void) { thrd_t thr[10]; for(int n = 0; n < 10; ++n) thrd_create(&thr[n], f, NULL); for(int n = 0; n < 10; ++n) thrd_join(thr[n], NULL); printf("The atomic counter is %u\n", acnt); printf("The non-atomic counter is %u\n", cnt); }
- _Static_assert:静态断言
#include <assert.h> int main(void) { // 测试数学是否正常工作 static_assert(2 + 2 == 4, "Whoa dude!"); // 或 _Static_assert(... // 这会在编译时产生错误。 static_assert(sizeof(int) < sizeof(char),"this program requires that int is less than char"); }
- _Noreturn:指定函数不会由于执行到
return
语句或抵达函数体结尾而返回
#include <stdlib.h> #include <stdio.h> #include <stdnoreturn.h> // 在 i <= 0 时导致未定义行为 // 在 i > 0 时退出 noreturn void stop_now(int i) // 或 _Noreturn void stop_now(int i) { if (i > 0) exit(i); } int main(void) { puts("Preparing to stop..."); stop_now(2); puts("This code is never executed."); }
- _Thread_local:线程存储类限定符
//库接口: // flib.h #ifndef FLIB_H #define FLIB_H void f(void); // 带外部链接的函数声明 extern int state; // 带外部链接的对象声明 static const int size = 5; // 带内部链接的只读对象定义 enum { MAX = 10 }; // 常量定义 inline int sum (int a, int b) { return a+b; } // inline 函数定义 #endif // FLIB_H //库实现: // flib.c #include "flib.h" static void local_f(int s) {} // 带内部链接的定义(只用于此文件) static int local_state; // 带内部链接的定义(只用于此文件) int state; // 带外部链接的定义( main.c 使用) void f(void) {local_f(state);} // 带外部链接的定义( main.c 使用) 应用代码: // main.c #include "flib.h" int main(void) { int x[MAX] = {size}; // 使用常量和只读变量 state = 7; // 修改 flib.c 中的 state f(); // 调用 flib.c 中的 f() return 0; }
- _Generic:泛型表达式
#include <stdio.h> #include <math.h> // tgmath.h 宏 cbrt 的可能实现 #define cbrt(X) _Generic((X), \ long double: cbrtl, \ default: cbrt, \ float: cbrtf \ )(X) int main(void) { double x = 8.0; const float y = 3.375; printf("cbrt(8.0) = %f\n", cbrt(x)); // 选择默认的 cbrt printf("cbrtf(3.375) = %f\n", cbrt(y)); // 将 const float 转换成 float, // 然后选择 cbrtf return 0; }