前言
C语言具有以下关键字:
这些关键字如下:
关 | 键 | 字 |
---|---|---|
auto | break | case |
char | const | continue |
default | do | double |
else | enum | extern |
float | for | goto |
if | int | long |
register | return | short |
signed | sizeof | static |
struct | switch | typedef |
union | unsigned | void |
volatile | while |
对于这些关键字,大致可以分成以下这几种:
(1)数据类型关键字:char, double, float, int, long, short, signed, unsigned, void - 用于定义不同类型的变量或函数返回类型。
(2)控制流程关键字:break, continue, do, else, for, goto, if, return, switch, while - 用于控制程序的执行流程,实现条件判断、循环和跳转等功能。
(3)存储类关键字:auto, extern, register, static - 用于控制变量的存储方式和生命周期。
(4)修饰符关键字:const, inline, restrict, volatile - 用于修饰变量的性质、函数的行为或指针的限制。
(5)数据结构关键字:enum, struct, union - 用于定义枚举类型、结构体和共用体。
(6)其他关键字:case, default, sizeof, typedef - 用于在特定语法结构中使用,如switch语句、sizeof运算符和类型定义。
(7)扩展关键字:_Alignas, _Alignof, _Atomic, _Bool, _Complex, _Generic, _Imaginary, _Noreturn, _Static_assert, _Thread_local - C11标准中引入的扩展关键字,用于支持新的功能和语法。
一、数据类型关键字
C语言提供了多种数据类型关键字,用于定义不同类型的变量或函数返回类型。下面对每个数据类型关键字进行详细解释:
char:用于表示字符类型数据,占用一个字节(8位),可用于存储ASCII码中的字符或小整数。
double:用于表示双精度浮点数类型数据,占用8个字节(64位),可用于存储较大范围的浮点数。
float:用于表示单精度浮点数类型数据,占用4个字节(32位),可用于存储较小范围的浮点数。
int:用于表示整数类型数据,占用4个字节(32位),可用于存储整数值。
long:用于表示长整数类型数据,占用4个字节(32位)或8个字节(64位),可用于存储较大范围的整数值。
short:用于表示短整数类型数据,占用2个字节(16位),可用于存储较小范围的整数值。
signed:用于表示有符号整数类型,可与int、long、short等类型关键字组合使用,表示该类型的变量可以存储有符号整数。
unsigned:用于表示无符号整数类型,可与int、long、short等类型关键字组合使用,表示该类型的变量可以存储无符号整数。
void:用于表示空类型,表示函数没有返回值或指针没有指向具体的类型。
#include <stdio.h>
int main() {
char c = 'A';
printf("Character: %c\n", c);
double d = 3.14159;
printf("Double: %lf\n", d);
float f = 2.71828;
printf("Float: %f\n", f);
int i = 42;
printf("Integer: %d\n", i);
long l = 999999999;
printf("Long: %ld\n", l);
short s = 32767;
printf("Short: %hd\n", s);
signed int si = -10;
printf("Signed Integer: %d\n", si);
unsigned int ui = 10;
printf("Unsigned Integer: %u\n", ui);
void *ptr = NULL;
printf("Void Pointer: %p\n", ptr);
return 0;
}
二、控制流程关键字
下面对每个控制流程关键字进行详细解释:
break:在循环语句(如for、while、do-while)或switch语句中使用,用于跳出当前循环或switch语句,终止循环或switch的执行。
continue:在循环语句中使用,用于跳过当前循环中continue之后的代码,进入下一次循环的迭代。
do:与while配合使用,构成do-while循环结构,先执行循环体中的代码,然后判断循环条件是否成立,如果成立则继续执行循环,否则结束循环。
else:与if配合使用,构成if-else条件语句,用于在条件不成立时执行的分支。
for:用于构建for循环结构,包括初始化表达式、循环条件和循环迭代部分,通过控制这些部分的执行顺序和条件,实现循环控制。
goto:用于无条件地将控制转移到程序的指定标签处,跳过中间的代码执行,通常不建议滥用goto语句,以免造成程序结构混乱。goto用的比较少,最好可以使用其他关键字代替goto的作用。
if:用于构建条件语句,根据条件的成立与否,选择不同的执行路径。
return:用于函数中,用于返回函数的返回值,并终止函数的执行。
switch:用于构建switch语句,根据表达式的值,选择不同的执行路径,可以用于替代多个if-else语句。
while:用于构建while循环结构,先判断循环条件是否成立,如果成立则执行循环体中的代码,然后再次判断条件,循环继续执行,直到条件不成立时结束循环。
三、存储类关键字(重点)
这里有4个关键字:auto、register、static和extern
1.auto关键字:生命周期仅存在于所在代码块,auto变量存于栈中
在C语言中,auto是默认的存储类关键字,当在函数内部声明变量时,如果没有使用任何存储类关键字修饰,默认为auto。auto关键字的主要作用是用于声明自动变量。
自动变量是在函数内部声明的局部变量,默认情况下,它的存储类为auto。自动变量的生命周期仅限于所在的代码块,当程序执行离开该代码块时,自动变量会被自动销毁。这意味着每次执行代码块时,自动变量都会重新初始化。
下面是一个使用auto关键字的示例:
#include <stdio.h>
void foo() {
auto int num; // 自动变量
num = 10;
printf("Value of num inside foo() is %d\n", num);
}
int main() {
auto int num = 5; // 自动变量
printf("Value of num inside main() is %d\n", num);
foo(); // 调用函数foo()
printf("Value of num inside main() after calling foo() is %d\n", num);
return 0;
}
在这个示例中,我们在main函数内部和foo函数内部都声明了一个auto型变量num。在main函数中,我们将num初始化为5,并打印其值。然后,我们调用foo函数,在foo函数中,我们将num初始化为10,并打印其值。在调用foo函数后,我们再次打印main函数内部的num值。
输出结果为:
Value of num inside main() is 5
Value of num inside foo() is 10
Value of num inside main() after calling foo() is 5
可以看到,num变量在不同的作用域中是独立的,它们的值互不影响。每次进入作用域时,自动变量都会重新初始化,并在离开作用域时被销毁。
需要注意的是,在C语言中,auto关键字通常是可以省略的,因为它是默认的存储类。因此,上述示例中的auto关键字可以省略,代码仍然具有相同的行为。
2.register关键字:提示编译器将变量存入寄存器中提升运行效率
在C语言中,register是一种存储类关键字,用于声明寄存器变量。寄存器变量是建议编译器将其存储在寄存器中的变量,以提高访问速度。使用register关键字并不意味着变量一定会存储在寄存器中,它只是向编译器发出了一个建议。编译器可以选择忽略该建议,因为寄存器的数量是有限的,并且并非所有变量都适合存储在寄存器中。
下面是一个使用register关键字的示例:
#include <stdio.h>
int main() {
register int i; // 声明寄存器变量
int sum = 0;
for (i = 1; i <= 10; i++) {
sum += i;
}
printf("Sum of numbers from 1 to 10 is %d\n", sum);
return 0;
}
在这个示例中,我们使用register关键字声明了一个寄存器变量i。然后,我们使用for循环计算从1到10的所有数字的总和。最后,我们打印总和的结果。
需要注意的是,register关键字在现代编译器中的使用已经不常见。现代编译器通常能够根据代码的优化策略自动选择合适的存储位置,包括寄存器、内存或缓存等。因此,使用register关键字并不能保证变量一定存储在寄存器中。
此外,register关键字有一些限制和注意事项:
不能取寄存器变量的地址:由于寄存器变量存储在寄存器中,而不是内存中,所以不能使用&运算符获取寄存器变量的地址。
不能声明寄存器变量为全局变量:寄存器变量只能在局部作用域内声明,不能作为全局变量。
存储类限定符的优先级高于类型名:当使用存储类关键字和类型名一起声明变量时,存储类关键字的优先级更高。例如,register int* p;声明的是一个指向整型的寄存器变量指针。
尽管register关键字的使用已经不常见,但在某些特定的嵌入式系统或对性能要求非常高的场景中,仍然可以尝试使用register关键字来提高访问速度。
3.static关键字:改变变量和函数的作用域和生命周期,静态变量存于静态区 (重点)
这个改变变量和函数的作用域和生命周期怎么理解呢?
首先要明白什么叫作用域?什么叫生命周期?
(1)什么叫作用域?什么叫生命周期?
作用域(Scope)指的是一个变量或函数的可见范围,即在哪些地方可以访问到该变量或函数。作用域规定了变量或函数的可访问性,它决定了变量或函数在程序中的可用范围。
在C语言中,有以下几种作用域:
块作用域(Block Scope):变量在定义它的块(一对花括号之间)内可见,包括该块内的嵌套块。块作用域适用于在函数内部定义的变量。
函数作用域(Function Scope):变量在整个函数内可见,包括函数内的嵌套块。函数作用域适用于在函数外部定义的变量。
文件作用域(File Scope):变量在整个源文件内可见,即在文件的任何位置都可以访问。文件作用域适用于在函数外部定义的全局变量和函数。
生命周期(Lifespan)指的是一个变量或对象存在的时间范围,即变量或对象的创建和销毁的时间段。生命周期决定了变量或对象的可用时间。
在C语言中,变量的生命周期取决于其定义的位置和存储类别:
自动存储类别(Automatic Storage Class):自动变量的生命周期与其所在的块或函数的执行时间相同。当程序执行离开变量所在的块或函数时,自动变量会被销毁。
静态存储类别(Static Storage Class):静态变量的生命周期在程序的整个运行期间都存在,即使程序执行离开所在的块或函数,静态变量也不会被销毁。
动态存储类别(Dynamic Storage Class):动态变量的生命周期由程序员手动控制。动态变量使用malloc()函数或类似的函数进行动态分配内存,直到程序员显式释放内存时才会被销毁。
这里特别关注静态存储类别:静态变量的生命周期在程序的整个运行期间都存在,即使程序执行离开所在的块或函数,静态变量也不会被销毁。所以静态变量在整个程序运行中都不会被销毁,它始终存放在内存中的静态区域。而static起的就是这个作用,它可以修饰一个变量或者函数,使其成为静态。
(2)内存的四个分区
(3)static的作用:修饰局部变量、修饰全局变量、修饰函数
①修饰局部变量:改变局部变量的存储位置,把变量从栈区变到静态区,从而使得局部变量的生命周期变长。
其本质是:
普通的局部变量创建后是放在栈区中,这种局部变量进入作用域时创建,出了作用域就销毁;
但static修饰后的局部变量则放在静态区中,它改变了局部变量的存储位置,从而使得变量的生命周期延长,延长至程序结束才销毁。
②修饰全局变量:改变全局变量的链接属性,从而使得全局变量的作用域变小
全局变量的作用域十分的广,只要在一个源文件中定义后,这个程序中的所有源文件、对象以及函数都可以调用,生命周期更是贯穿整个程序。文件中的全局变量想要被另一个文件使用时就需要进行外部声明(以下用extern关键字进行声明)。
但是如果A文件定义了一个全局变量x,那么就不能在B文件中使用extern来读取x了,使得静态的全局变量x无法被其他文件调用。
其本质是:
全局变量本身是具有外部链接属性的,在A文件中定义的全局变量,在B文件中可以通过【链接】来使用;
但如果全局变量被static修饰,那这个外部链接属性就会被修改成内部链接属性,此时这个全局变量就只能在自己的源文件中使用;
③修饰函数:与修饰全局变量十分相似,修饰函数时会改变函数的链接属性,从而使得函数的作用域变小
本身A文件中的函数,可以在B文件中用extern声明来使用。
但是当该函数被static静态化后,就不能被B文件调用了。
本质和全局变量很像:
函数本身也是有外部链接属性的;
被static修饰后,函数的外部链接属性被修改成内部链接属性,使得这个函数只能在自己的源文件内被使用,因此函数的作用域就变小了。
4.extern关键字:声明一个变量或者函数为全局作用域
在C语言中,extern是一个关键字,用于声明一个变量或函数是在其他源文件中定义的。它的作用是告诉编译器该变量或函数的定义在其他地方,不需要在当前源文件中定义。
具体来说,extern关键字的作用包括:
声明全局变量:当在一个源文件中使用extern关键字声明一个全局变量时,表示该全局变量是在其他源文件中定义的。这样可以在当前源文件中使用该全局变量而不需要重新定义它。
声明函数:当在一个源文件中使用extern关键字声明一个函数时,表示该函数是在其他源文件中定义的。这样可以在当前源文件中调用该函数而不需要重新定义它。
外部链接:extern关键字还指定了变量或函数的链接属性为外部链接。外部链接意味着该变量或函数可以在其他源文件中访问和使用。
需要注意的是,使用extern关键字声明的变量或函数,编译器不会为其分配内存或生成代码,它只是告诉编译器该变量或函数的定义在其他地方。因此,在使用extern关键字声明的变量或函数之前,需要确保其在其他源文件中有定义。
三、修饰符关键字:const, inline, restrict, volatile
1.const关键字:只读,无法修改,提升程序健壮性
在C语言中,const是一个关键字,用于声明一个常量。
const关键字的作用包括:
声明常量:使用const关键字可以将一个变量声明为常量,表示该变量的值在程序执行期间不可改变。常量在声明时必须同时进行初始化,且不能再次赋值。
类型安全性:通过将变量声明为常量,可以增加程序的类型安全性。如果试图修改const常量的值,编译器会报错。
优化编译器:编译器在处理const常量时,可以进行一些优化,例如将常量直接嵌入到生成的机器代码中,提高程序的执行效率。
防止意外修改:将变量声明为const常量可以防止在程序中意外修改其值,提高程序的可维护性和可读性。
常量可以用于各种数据类型,例如整型、浮点型、字符型等。声明常量的语法形式为:const 数据类型 常量名 = 值;
需要注意的是,const关键字只保证变量的值在程序执行期间不可改变,并不保证变量的地址不可改变。因此,const变量的地址是可以被修改的。
比如这里将函数的两个字符串参数用const修饰,那么就保护了其在这个test函数的作用域内是只读的,无法对齐进行修改。
但是在main函数中是可以对其进行修改的,说明const修饰的变量并不是变成了一个常量,而是单纯无法被修改。
那const修饰的变量存储在内存的哪个区域呢?
保存在只读数据区。只读数据区是一种特殊的内存区域,用于存储常量和只读的全局变量。它通常位于程序的静态存储区,其内容在程序执行期间保持不变。
2.inline关键字:告诉编译器将函数的内容插入到这个函数被调用的地方,以避免函数调用的开销(内联函数)
这句话什么意思:比如test函数被主函数main调用时,函数调用会引入额外的开销,包括函数调用的准备和恢复、参数传递等,所以其实是有一些性能损耗的。用inline修饰函数后,该函数就变成了内联函数,相当于直接将函数代码插入到调用函数的地方,内联展开。
值得注意的是:使用了inline关键字,编译器也不一定保证函数一定被内联展开,它只是提供了一个建议。
那为什么所有的函数都不多多使用inline关键字来减少开销呢?
以下是一些原因:
函数体过大:如果函数体过大,将其内联会导致代码膨胀,增加程序的内存消耗。而且,内联函数的函数体会在每个调用点被复制,如果函数体过大,会导致代码重复的增加,可能会降低缓存命中率,从而影响程序的性能。
递归函数:递归函数无法进行内联,因为递归函数的调用点是在函数内部,而不是在函数外部。
调用频率低:如果一个函数的调用频率很低,将其内联可能得不偿失。因为内联函数的函数体会在每个调用点被复制,如果函数很少被调用,复制函数体的开销可能会超过函数调用的开销。
代码可读性:内联函数的函数体被复制到调用点,会增加代码的长度,可能会降低代码的可读性。如果函数的逻辑比较复杂,使用内联可能会使代码难以理解和维护。
3.restrict关键字:告诉编译器一个指针是独占的,没有别的指针指向相同的内存区域。
4.volatile关键字:确保本条指令不会因编译器的优化而省略,且要求每次直接从内存读值。
举一个例子:
int i = 10;
int main(void){
int a, b;
a = i;
...//伪代码,里面不含有对 a 、 b 以及 i的操作
b = i;
if(a == b){
printf("a = b");
}
else {
printf("a != b");
}
return 0;
}
如上代码,如果选择编译器优化,可能会被编译成如下代码(当然不是在C语言层面上优化,而是在汇编过程优化,只是使用C程序举例):
int i = 10;
int main(void){
int a, b;
a = i;
...//伪代码,里面不含有对 a 、 b 以及 i的操作
b = i;
printf("a = b");
return 0;
}
因为在仅仅从main主函数来看,a == b是必然的,所以编译器就认为a≠b的操作不会发生,便优化掉了。
那么在什么情况,a 和 b不是必然相等呢?
- i 是其他子线程与主线程共享的全局变量,其他子线程有可能修改 i 值;
- i 是中断函数与主函数共享的全局变量,中断函数有可能修改 i 值;
- i 属于硬件寄存器,CPU可能通过硬件直接改变 i 的值(例如寄存器的标志位)
比如当执行a=i后,刚好发生一个中断导致i值改变,那么b=i这条语句后a≠b,如果之前被编译器优化了,那么就会导致无法正确执行相关程序,出现问题。
volatile的意思是“易变的”,用volatile修饰的变量表示这个变量的值随时有可能发生改变,因此提醒编译器编译时对该变量的存取操作不能进行优化,每次存取该变量都要从内存中存取,而不是使用其之前在寄存器中的备份。
要知道,以上面的程序为例,先是a=i,对这个i就要先从内存中读取,保存到寄存器里再赋给a,但是由于这个变量i很容易发生改变,所以如果不加volatile就可能导致内存里i变了但是寄存器保存的还是之前a=i的值,使得b=i读取时发生错误。所以必须加上volatile修饰i,如此即使i的值变化了编译器也能正确读取i的值。
那volatile关键字的应用场合有哪些?
volatile关键字的应用场合有以下几种:
1.多线程共享变量:当多个线程共享某个变量时,使用volatile关键字可以保证变量的可见性。即当一个线程修改了这个变量的值,其他线程能够立即看到最新的值,而不会使用缓存中的旧值。
2.中断:中断服务程序如果修改了其他程序中使用的变量,需要用volatile修饰该变量。
3.状态标志位:在多线程中使用volatile关键字可以保证状态标志位的可见性。例如,一个线程修改了某个标志位的值,其他线程可以立即看到最新的值,从而做出相应的处理。
4.硬件寄存器:比如状态寄存器,它的值有可能随着硬件的工作状态变化而改变,用volatile修饰。
那使用volatile关键字有什么优缺点吗?
四、数据结构关键字:enum, struct, union - 用于定义枚举类型、结构体和共用体。
1.enum关键字:定义一组相关的常量
enum是C语言中的关键字,用于定义枚举类型。
枚举类型是一种用户自定义的数据类型,用于定义一组具有相关性的常量值。枚举类型可以帮助提高代码的可读性和可维护性,使程序员能够更清晰地表示一组相关的常量。
使用enum关键字定义枚举类型的语法形式如下:
enum 枚举类型名 {
枚举常量1,
枚举常量2,
...
};
其中,枚举类型名是用户自定义的标识符,用于表示枚举类型。枚举常量是一组有序的常量值,用逗号分隔。枚举常量可以通过枚举类型名和常量名来引用。
例如,下面的代码定义了一个名为Color的枚举类型,包含三个枚举常量Red、Green和Blue:
enum Color {
Red,
Green,
Blue
};
在程序中,可以使用枚举类型名和枚举常量名来表示枚举常量的值。例如,可以使用Color枚举类型名和Red枚举常量名来表示Red枚举常量的值:
enum Color c = Red;
枚举常量的值默认从0开始依次递增,也可以通过显式赋值来指定枚举常量的值。例如,可以在定义枚举常量时显式指定其值:
enum Weekday {
Monday = 1,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
在这个例子中,Monday的值为1,后续的枚举常量依次递增。
总的来说,enum关键字用于定义枚举类型,在C语言中枚举类型是一种用户自定义的数据类型,用于定义一组相关的常量值。枚举类型可以提高代码的可读性和可维护性,使程序员能够更清晰地表示一组相关的常量。
2.struct关键字:定义多个不同类型的变量
struct是C语言中的关键字,用于定义结构体类型。
结构体是一种用户自定义的数据类型,可以用于组合多个不同类型的变量,形成一个新的数据类型。结构体可以包含多个成员变量,每个成员变量可以有不同的数据类型。
使用struct关键字定义结构体类型的语法形式如下:
struct 结构体类型名 {
成员变量1;
成员变量2;
...
};
其中,结构体类型名是用户自定义的标识符,用于表示结构体类型。成员变量是结构体中的变量,用于存储数据。成员变量可以是任意的数据类型,包括基本类型和其他结构体类型。
例如,下面的代码定义了一个名为Person的结构体类型,包含两个成员变量name和age:
struct Person {
char name[20];
int age;
};
在程序中,可以使用结构体类型名和成员变量名来表示结构体类型的变量。例如,可以定义一个Person类型的变量p,并为其成员变量赋值:
struct Person p;
strcpy(p.name, "Alice");
p.age = 25;
也可以在定义结构体类型的同时创建结构体变量,称为结构体的实例化。例如,可以直接定义一个Person类型的变量并初始化其成员变量:
struct Person {
char name[20];
int age;
} p = {"Bob", 30};
结构体变量的成员变量可以通过点操作符(.)来访问。例如,可以使用p.name和p.age来访问结构体变量p的成员变量。
总的来说,struct关键字用于定义结构体类型,在C语言中结构体是一种用户自定义的数据类型,用于组合多个不同类型的变量,形成一个新的数据类型。结构体可以包含多个成员变量,每个成员变量可以有不同的数据类型。结构体变量的成员变量可以通过点操作符来访问。
3.union关键字:在同一存储空间内定义多个不同类型的变量
union是C语言中的关键字,用于定义联合类型。
联合(union)是一种特殊的数据类型,可以在同一内存空间中存储不同类型的数据。与结构体不同,联合中的成员变量共享同一段内存空间,只能同时存储其中一个成员的值。
使用union关键字定义联合类型的语法形式如下:
union 联合类型名 {
成员变量1;
成员变量2;
...
};
其中,联合类型名是用户自定义的标识符,用于表示联合类型。成员变量是联合中的变量,可以有不同的数据类型。
例如,下面的代码定义了一个名为Data的联合类型,包含两个成员变量i和f,分别表示整型和浮点型:
union Data {
int i;
float f;
};
在程序中,可以使用联合类型名和成员变量名来表示联合类型的变量。例如,可以定义一个Data类型的变量d,并为其成员变量赋值:
union Data d;
d.i = 10;
printf("%d\n", d.i);
d.f = 3.14;
printf("%f\n", d.f);
注意,联合变量的不同成员变量共享同一段内存空间,因此对一个成员变量的赋值会影响到其他成员变量。在上面的示例中,将d.i赋值后,d.f的值也会发生变化。
可以使用sizeof运算符来获取联合变量所占用的内存大小。由于联合中的成员变量共享内存空间,所以联合的大小取决于最大的成员变量的大小。
总的来说,union关键字用于定义联合类型,在C语言中联合是一种特殊的数据类型,可以在同一内存空间中存储不同类型的数据。联合中的成员变量共享同一段内存空间,只能同时存储其中一个成员的值。联合变量的大小取决于最大的成员变量的大小。
五、其他关键字:case, default, sizeof, typedef - 用于在特定语法结构中使用,如switch语句、sizeof运算符和类型定义。
1.case关键字:特用于switch-case语句做分支判断
case关键字是在switch语句中用于指定不同的分支条件。它的语法如下:
switch (expression) {
case value1:
// 执行代码块1
break;
case value2:
// 执行代码块2
break;
...
default:
// 执行默认代码块
break;
}
在switch语句中,expression是一个表达式,用于与每个case后面的值进行比较。如果expression的值与某个case后面的值相等,则执行对应的代码块。如果没有匹配的case值,则执行default代码块。break语句用于跳出switch语句,避免执行其他case分支。
case关键字的特点包括:
case值必须是常量表达式:case后面的值必须是常量表达式,即在编译时就可以确定的值,不能是变量或者运行时计算的表达式。
case分支顺序:switch语句中的case分支是按照出现的顺序进行匹配的。一旦匹配到一个case分支后,会执行该分支的代码块,并且会执行其后的所有分支代码块,除非遇到break语句或者switch语句结束。
default分支:default关键字用于指定默认的分支。如果没有任何case匹配成功,则会执行default代码块。default分支可以放在任意位置,但通常放在最后。
总之,case关键字是用于在switch语句中指定多个不同分支条件的关键字,用于根据不同的条件执行相应的代码块。
2.default关键字:switch-case语句无任何匹配情况再执行default代码
在C语言中,default关键字主要用于switch语句中的默认分支。与Java类似,default关键字在C语言中用于指定没有与任何case匹配的情况下执行的代码块。示例如下:
switch (expression) {
case value1:
// 执行代码块1
break;
case value2:
// 执行代码块2
break;
...
default:
// 执行默认代码块
break;
}
当switch表达式的值与任何一个case值都不匹配时,程序会执行default代码块中的语句。default分支可以放在任意位置,但通常放在最后。
需要注意的是,C语言中的switch语句没有Java中的break语句自动跳出的机制。如果在某个case分支中没有使用break语句,程序会继续执行后续的case分支代码,直到遇到break或者switch语句结束。
总之,在C语言中,default关键字用于指定switch语句中的默认分支,用于处理没有与任何case匹配的情况。
3.sizeof关键字:计算对象的内存占用字节大小
sizeof关键字是C和C++语言中的一个运算符,用于计算对象或类型的大小。它返回一个无符号整数值,表示对象或类型在内存中所占用的字节数。
sizeof关键字的语法如下: sizeof(对象或类型)
其中,对象可以是变量、数组、指针等,类型可以是基本数据类型、结构体、联合体等。
使用sizeof关键字时,可以通过两种方式来计算大小:
计算对象的大小:
对于基本数据类型,sizeof返回该类型在内存中所占用的字节数。例如,sizeof(int)返回4,sizeof(char)返回1。
对于结构体和联合体,sizeof返回该结构体或联合体的所有成员所占用的总字节数。注意,sizeof不会计算填充字节。
对于指针,sizeof返回指针本身的大小,而不是指针指向的对象的大小。
计算类型的大小:
使用sizeof关键字可以计算类型在内存中所占用的字节数。例如,sizeof(int)返回4,sizeof(struct MyStruct)返回结构体MyStruct在内存中的大小。
sizeof关键字在编程中有多种应用,例如:
动态分配内存时,可以使用sizeof来计算所需的内存大小。
在处理数据缓冲区时,可以使用sizeof来计算缓冲区的大小,以确保不会越界访问。
在编写通用代码时,可以使用sizeof来获取对象或类型的大小,以便进行后续的操作。
需要注意的是,sizeof关键字在编译时进行计算,而不是在运行时。因此,它不会对对象进行实际的访问或计算。此外,sizeof关键字返回的是无符号整数值,可以用来进行大小比较、位运算等操作。
4.typedef关键字:自定义新的数据类型
typedef关键字是C和C++语言中的一个关键字,用于定义类型别名。它可以为已有的数据类型或用户自定义的数据类型创建一个新的名称,以方便在代码中使用。
typedef关键字的语法如下: typedef 原类型名 新类型名;
其中,原类型名可以是任何已有的数据类型,包括基本数据类型(如int、char、float等)、结构体、联合体、枚举等。新类型名是用户自定义的类型别名。
通过typedef关键字,可以实现以下功能:
创建类型别名:使用typedef可以为已有的类型创建一个新的名称,使得代码更加易读和易于维护。例如,typedef int Integer; 将int类型定义为Integer类型,以后就可以使用Integer来代替int。
简化复杂类型声明:当声明复杂的数据类型时,使用typedef可以简化代码。例如,typedef int* IntPtr; 将int指针类型定义为IntPtr类型,以后可以直接使用IntPtr来声明指针变量,而不需要每次都写int*。
提高代码可移植性:通过使用typedef定义的类型别名,可以使代码更具可移植性。当需要在不同的平台或编译器上进行移植时,只需修改typedef定义的别名,而无需修改大量的代码。
typedef关键字的使用示例:
typedef int Integer; // 创建类型别名,将int类型定义为Integer类型
typedef int* IntPtr; // 创建类型别名,将int指针类型定义为IntPtr类型
Integer num = 10; // 使用Integer类型别名声明变量
IntPtr ptr = # // 使用IntPtr类型别名声明指针变量
typedef struct {
int x;
int y;
} Point; // 创建类型别名,将结构体定义为Point类型
Point p1 = {2, 3}; // 使用Point类型别名声明结构体变量
需要注意的是,typedef只是为已有的类型创建了一个新的名称,而不是创建了一个新的类型。因此,typedef定义的类型别名与原类型是完全等价的,在编译过程中会被替换为原类型。
六、C99规范新拓展关键字
C99规范引入了一些新的拓展关键字,这些关键字用于扩展C语言的功能和语法。以下是C99规范引入的一些新拓展关键字:
restrict:restrict关键字用于指定指针的限定,表示该指针是访问对象的唯一且最初的方式。它可以帮助编译器进行优化,提高代码的性能。
_Bool:_Bool关键字用于声明布尔类型的变量,可以存储true或false的值。它的取值范围是0和1。
inline:inline关键字用于声明内联函数,指示编译器将函数的代码插入到调用处,以减少函数调用的开销。
_Complex:_Complex关键字用于声明复数类型的变量,可以存储实部和虚部的值。它可以在数学和信号处理等领域中使用。
_Imaginary:_Imaginary关键字用于声明虚数类型的变量,可以存储虚部的值。它是_Complex关键字的一部分。
_Alignas:_Alignas关键字用于指定对齐方式,可以用于声明变量或结构体的对齐方式。
_Alignof:_Alignof关键字用于获取类型的对齐方式,返回对齐方式的大小。
_Static_assert:_Static_assert关键字用于在编译时进行静态断言,用于检查一些编译时的条件和约束。