C语言(一):32个关键字及其用法

C语言(一):32个关键字及其用法

由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

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 = "<

}

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

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 = "<

}

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在typedef int BOOL,在typedef long BOOL;_Bool类型只有0和1这两个值。_Complex:新增的数据类型,用来表示复数

#include

#include

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

double imaginary x = 5.0 * I; //大写I表示虚数单位,即-1的平方根

//现在已经无法像上面这么定义,只能通过下面的方式定义了

double complex x = 5.0 * I;

关于复数的方法,C++有关于类的实现:

#include

#include

using namespace std;

int main()

{

complex x(1, 2.1);

complex y(0, 5.0);

complex z;

z = x + y;

std::cout<<"x = "<

std::cout<<"y = "<

std::cout<<"z = "<

return 0;

}

/*最后的结果可以实现实部和虚部的组合:

x = (1,2.1)

z = (0,5)

y = (1,7.1)

*/

C11增加的关键字(7个)(下面的代码未进行测试。。。)

_Alignas:修改声明对象对齐要求的指定符,出现在声明的语法中。

#include

#inlcude

//每一个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

#include

#include

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

#include

#include

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

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

#include

#include

// 在 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

#include

// 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;

}

相关推荐