由ANSI标准定义的C语言关键字共32个:
一、关键字的分类
二、常用的数据类型说明
三、构造数据类型
四、数据的存储类别
首先,C语言的关键字一共有32个,这是C90标准定义的关键字个数。其实后续的C99标准又新增了5个关键字,但是由于目前主流的编译器对C99的支持并不是特别好,所以我们默认是C90标准定义的关键字,即32个。
以下是关键字说明:
关键字 | 说明 |
---|---|
auto | 声明自动变量 |
short | 声明短整型 |
int | 声明整型 |
long | 声明长整型 |
float | 声明单精度浮点型 |
double | 声明双精度浮点型 |
char | 声明字符型 |
struct | 声明结构体类型 |
union | 声明联合(共用)类型 |
enum | 声明枚举类型 |
typedef | 重命名数据类型 |
const | 声明只读变量 |
unsigned | 声明无符号类型 |
signed | 声明有符号类型 |
extern | 声明外部变量 |
register | 声明寄存器变量 |
static | 声明静态变量 |
volatile | 说明变量在程序执行中可隐含的被改变 |
void | 声明函数无返回值或者无参数,声明无类型指针 |
if | 条件语句 |
else | 条件语句否定分支(与if连用) |
switch | 用于开关语句 |
case | 开关语句分支 |
default | 开关语句中默认分支 |
for | 循环语句 |
do | 循环语句循环体 |
while | 循环语句的循环条件 |
continue | 结束当前循环,开启下一轮循环 |
break | 跳出当前循环 |
goto | 无条件跳转语句 |
sizeof | 计算数据类型长度 |
return | 子程序返回语句(可以带参数,也可以不带参数) |
1、基本数据类型: signed
、unsigned
、char
、int
、float
、double
、short long
、void
2、构造数据类型: struct
、union
、enum
3、数据存储类别: auto
、static
、extern
、register
4、数据优化: const
、volatile
5、9条基本语句: if
、else
、switch
、case
、break
、default
、while
、do
、for
、return
、continue
、goto
6、其它: typedef
、sizeof
关键字的作用,可以将关键字分为
数据类型关键字
和流程控制关键字
两大类。
void:声明函数无返回值或无参数,声明无类型指针,显式丢弃运算结果
char:字符型类型数据,属于整型数据的一种
int:整型数据,通常为编译器指定的机器字长
float:单精度浮点型数据,属于浮点数据的一种
double:双精度浮点型数据,属于浮点数据的一种
short:修饰int,短整型数据,可省略被修饰的int
long:修饰int,长整形数据,可省略被修饰的int
signed:修饰整型数据,有符号数据类型
unsigned:修饰整型数据,无符号数据类型
struct:结构体声明
union:共用体声明
enum:枚举声明
typedef:声明类型别名
sizeof:得到特定类型或特定类型变量的大小
auto:指定为自动变量,由编译器自动分配及释放。通常在栈上分配
static:指定为静态变量,分配在静态变量区,修饰函数时,指定函数作用域为文件内部
extern:指定对应变量为外部变量,即在另外的目标文件中定义,可以认为是约定由另外文件声明
const:与volatile合称“cv特性”,指定变量不可被当前线程/进程改变(但有可能被系统或其他线程/进程改变)
return:用在函数体中,返回特定值(或者是void值,即不返回值)
continue:结束当前循环,开始下一轮循环
break:跳出当前循环或switch结构
goto:无条件跳转语句
if:条件语句 else:条件语句否定分支(与if连用)
switch:开关语句(多重分支语句)
case:开关语句中的分支标记
default:开关语句中的“其他”分治,可选
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为循环条件
以上循环语句,当循环条件表达式为真则继续循环,为假则跳出循环。
C语言中常用的数据类型说明,这些数据类型在C语言中用于声明变量的类型。
下面是对每个数据类型的简要说明:
unsigned: 用来声明一个无符号的变量。例如,unsigned char
表示范围在 0 到 255 之间的无符号字符。
signed: 用来声明一个有符号的变量(可以省略不写)。在一般情况下,可以省略 signed
关键字,因为大多数变量默认为有符号类型。例如,signed char
表示范围在 -128 到 127 之间的有符号字符。
char: 用来声明一个字符型变量。占一个字节的空间。
int: 用来声明一个整型变量。在不同的系统和编译器上,int
可能占用不同字节数的空间,通常为两个(C51)或四个字节(ARM)。
float: 用来声明一个浮点型(实型)变量。通常占四个字节空间,最多能表示到 7 个有效数据位。
double: 用来声明一个双精度实型变量。通常占用四个字节空间,但在一些系统中可能占用八个字节。能表示更多的有效数据位,最多到 15~16 位。
short: 用来声明一个短整型变量。在不同系统上,short
可能占用不同字节数的空间,占两个字节。
long: 用来声明一个长整型变量。在不同系统上,long
可能占用不同字节数的空间,占四个字节。
void: 表示一个函数没有返回值,或者无形参。通常用于函数声明,如 void function(void);
,表示 function
是一个没有参数且没有返回值的函数。
这些数据类型是C语言中的基本数据类型,用于定义不同类型的变量。不同的数据类型具有不同的存储大小和表示范围。
C语言中的结构体(struct
)、共用体(union
)和枚举(enum
)的声明示例。下面是对每个概念的简要说明:
struct: 用来声明一种结构体类型。结构体是一种自定义的数据类型,允许在一个变量中存储多个不同类型的成员。在你的示例中,struct stu
定义了一个包含性别、年龄、分数和指向下一个结构体的指针的结构体类型。通过 struct stu var;
创建了一个名为 var
的结构体变量。
struct stu {
char sex;
int age;
float score;
struct stu *Next;
};
struct stu var;
union: 用来声明一种共用体类型。共用体允许在同一内存位置存储不同类型的数据,但只能同时存储其中的一个。共用体的大小等于它的最大成员的大小。在你的示例中,union non
定义了一个包含性别、年龄和分数的共用体类型。通过 union non var;
创建了一个名为 var
的共用体变量。
union non {
char sex;
int age;
float score;
};
union non var;
enum: 用来声明一种枚举类型。枚举类型定义了一组命名的整数常量,这些常量被称为枚举成员。在你的示例中,enum em
定义了一个包含 a、b、c、d 和 e 的枚举类型。枚举成员的值可以手动指定,如果没有指定,默认从上一个枚举成员的值递增。通过 enum em var;
创建了一个名为 var
的枚举类型变量。
enum em {a = 23, b, c, d = 56, e};
enum em var;
这些概念允许程序员更灵活地组织和使用数据,特别是当数据具有复杂的结构或需要进行分类时。
在C语言中,auto
关键字用于声明一个局部变量,并且通常可以省略不写。在C语言中,变量的默认存储类别就是auto
。auto
变量的特点是在其所在的函数或代码块的执行过程中分配内存,当函数或代码块执行结束时,这些变量会被销毁,所占用的内存也会被释放。
以下是一个使用 auto
关键字的简单示例:
#include <stdio.h>
int main() {
auto int x = 10; // auto 关键字通常可以省略不写
auto float y = 3.14;
printf("x: %d\n", x);
printf("y: %f\n", y);
return 0;
}
在上述示例中,auto
关键字用于声明局部变量 x
和 y
,但它通常可以省略不写。在实际编码中,程序员很少显式地使用 auto
,因为在 C 语言中,默认的变量存储类别就是 auto
。因此,上述代码可以简化为:
#include <stdio.h>
int main() {
int x = 10;
float y = 3.14;
printf("x: %d\n", x);
printf("y: %f\n", y);
return 0;
}
在 C 语言中,auto
关键字已经变得不太常见,而且在一些编译器中可能会被忽略。变量的存储类别由默认规则确定,无需显式指定 auto
。
static
在 C 语言中用于修饰变量和函数,具有一些特定的作用和用途。
对于局部变量来说,其生命周期是在定义的代码块内部。但是如果用static修饰局部变量。其生命周期就会变成全局变量的生命周期。但是要注意,虽然其生命周期发生改变,但是其作用域却不发生改变。
void exampleFunction() {
// 定义一个局部变量,在定义时只进行一次初始化
// 每次调用函数时,变量的值保持上一次调用的结果
static int counter = 0;
counter++;
printf("Counter: %d\n", counter);
}
int main() {
exampleFunction(); // Counter: 1
exampleFunction(); // Counter: 2
exampleFunction(); // Counter: 3
return 0;
}
对于全局变量来说,定义了一个全局之后,就可以通过使用extern声明不在本文件内部的全局变量来进行使用。所以全局变量是可以跨文件被访问的。
而static修饰的全局变量只能够在本文件内部被使用。
// 被 static 修饰的全局变量,只能在它所在的 C 源文件中使用
// 其他文件不能调用,称为内部全局变量
static int globalVar = 10;
没有被static修饰的函数是可以在被别的文件调用的。但是一但加了static修饰,就只能够在本文件内部使用。这个功能常用来隐藏一些不想被其他人看到的函数细节。
// 被 static 修饰的函数,只能在该函数所在的 C 源文件中被调用
// 其他文件不能调用,称为内部函数
static void internalFunction() {
printf("Internal Function\n");
}
static
的作用主要有两个方面:
static
修饰的局部变量,使其在函数调用之间保持状态,因为它在定义时只初始化一次。extern
用于声明一个变量或函数的外部链接性,以便在其他源文件中使用。以下是对 extern
的用法的进一步说明:
// 在一个 C 源文件中声明一个全局变量
// 这个全局变量可以在其他源文件中使用
// (前提是这个全局变量没有被 static 修饰)
char var;
// 在其他源文件中使用声明的外部变量
extern char var;
int main() {
// 在这里可以使用 var 变量
return 0;
}
// 在一个 C 源文件中声明一个函数
// 这个函数可以在其他源文件中使用
// (前提是这个函数没有被 static 修饰)
void function();
// 在其他源文件中使用声明的外部函数
extern void function();
int main() {
// 在这里可以调用 function 函数
function();
return 0;
}
通过 extern
,你可以在一个源文件中声明一个全局变量或函数,然后在其他源文件中使用它,实现了模块之间的信息共享。需要注意的是,extern
声明的变量或函数在声明它的源文件中不分配内存,而是在使用它的源文件中分配内存。
register
用于向编译器建议将指定的变量存储在CPU的寄存器中,以提高程序的执行效率。以下是对 register
的用法的进一步说明:
#include <stdio.h>
int main() {
// 被 register 修饰的变量,建议编译器将其存储在CPU内部寄存器中
// 提高执行效率,但是具体是否放入寄存器由编译器决定
register long i = 30000;
// 在使用时才分配内存,用完即释放
printf("Value of i: %ld\n", i);
return 0;
}
需要注意的是,register
关键字只是对编译器的建议,具体是否将变量放入寄存器中取决于编译器的实现和对程序的优化策略。现代编译器通常会根据代码的复杂性和执行频率来自动选择是否使用寄存器。
在实际编程中,使用 register
关键字的场景相对有限。通常,编译器会根据代码的优化级别和上下文自动决定是否将变量存储在寄存器中。在大多数情况下,现代编译器已经足够智能,无需手动指定 register
关键字。
其实,CPU是负责进行运算的硬件单元,如果要进行运算,就需要将数据读取到CPU中去,再进行运算。但是各种硬件的数据处理速度是不同的,而根据处理数据速度的不同可以对硬件进行分级:
这种存储分级自上而下速度越来越慢,而存储容量缺越来越大,这很好理解,因为速度变慢,单位成本也会降低不少,所以其存储容量也会随之变大。而这样分级的原因主要是为了能充分利用CPU的优良快速的性能。
所以,相较于内存而言,寄存器的访问速度相较于CPU是很快的,而内存就要比寄存器慢了不少,所以说,将数据存储在寄存器中会大大提高计算机的运行速度。
同时,register修饰变量会尽量将其放在寄存器中,因为如果寄存器都占用了或者编译器不同意你放进寄存器中,那就无法将想要放进寄存器的变量放入寄存器中。同时register变量是不能被取地址的。因为取地址这种寻址操作只能在内存中进行。
代码如下:
int main()
{
register int a = 0;
printf("&a = %p\n", &a);
//编译器报错:错误 1 error C2103: 寄存器变量上的“&”
system("pause");
return 0;
}
同时,使用寄存器变量时应该注意以下几点:
1.应当使用局部变量,因为全局变量会导致寄存器被长时间占用,可能会对计算机的状态造成影响。
2.应当在需要高频使用的时候用register关键字修饰局部变量。
3.如果要使用,也应当注意不能过多使用,因为寄存器数量是有限的,同时大都用来存储一些重要的状态信息。
const
修饰的变量,不可直接被修改。但是却可以通过指针的解引用来进行修改。
const
用于声明常量。以下是对 const
的用法的进一步说明:
// 被 const 关键字声明的变量是常变量,其值不能被改变
const char var = 100;
常变量在声明时必须初始化,且其值不能在后续的代码中被修改。这种特性用于确保数据的安全性,防止意外的修改。
char arr[var]; // 在一些编译环境中可以使用常变量声明数组的长度
在一些编译环境中,允许使用常变量来声明数组的长度。这在一定程度上提高了代码的灵活性。
char *const p;
指针变量 p
是常量指针,它的值(即指向的地址)不能被改变,但是它指向的地址里面的值是可以变的。
const char *p; // 或 char const *p;
指针变量 p
指向常量,它可以改变指向的地址,但是指向的地址里面的值不能改变。
const char * const p;
指针变量 p
是常量指针,它的值和它所指向的地址都不能被改变。
const
关键字是用于增强代码的可读性和可维护性,同时提供了一定程度的数据安全性。
volatile
关键字和 const
一样是一种类型修饰符,用它修饰的变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
volatile
用于告诉编译器,被 volatile
修饰的变量的值可能会在意想不到的时候被意外地改变。以下是对 volatile
的用法的进一步说明:
硬件影响: 某些变量或寄存器,例如状态寄存器,可能会受硬件影响而在没有明显的代码修改的情况下发生改变。
多线程环境: 在多线程任务中,如果一个变量是共享的,而且被其他线程修改,那么可以使用 volatile
来确保每次访问都从内存中获取最新的值。
中断服务中的全局变量: 在中断服务程序中使用到的全局变量可能会在中断中被修改,使用 volatile
可以确保这些变量的值在被使用时是最新的。
编译器优化: 编译器为了提高程序执行效率,可能会对一些代码进行优化,例如去掉一些看似无效的读取操作。使用 volatile
可以阻止编译器进行这样的优化。
下面是一个使用 volatile
的简单示例:
int main() {
int *volatile p;
*p = 1;
*p = 2; // 防止编译器优化,确保每次都从内存中获取最新的值
return 0;
}
在上述示例中,使用 volatile
修饰指针变量 p
,以确保编译器不会对 *p = 2;
这样的写操作进行优化,从而保证每次写操作都真正地发生。在一些特定的情况下,使用 volatile
是确保程序正确执行的一种方式。
关键字 | 描述 | 用途 |
---|---|---|
auto | 自动的 | 用于声明局部变量,在函数内部自动分配内存。在函数执行结束时被销毁。 |
static | 静态的 |
|
extern | 外部的 |
|
register | 寄存器的 | 建议编译器将变量放到 CPU 内部寄存器中,以提高执行效率。通常在循环中使用,用于频繁访问的变量。 |
const | 常量 | 用于声明常变量,其值不能被改变。常常用于定义程序中不应该被修改的常量值。 |
volatile | 随时会改变的 | 用于修饰变量,表示变量的值可能会在意料之外的情况下改变,防止编译器进行优化。通常用于中断服务程序、多线程程序中。 |
if-else
结构是一种条件控制结构,用于根据条件的真假执行不同的代码块。以下是 if-else
结构的基本形式:
if (条件) {
// 条件为真时执行的语句块
语句1;
} else {
// 条件为假时执行的语句块
语句2;
}
在这个结构中,首先判断 if
后面的条件,如果条件为真,则执行 if
后面的语句块,否则执行 else
后面的语句块。
#include <stdio.h>
int main() {
int num = 10;
if (num > 0) {
printf("The number is positive.\n");
} else {
printf("The number is non-positive.\n");
}
return 0;
}
在上述例子中,如果 num
的值大于 0,则输出 “The number is positive.”,否则输出 “The number is non-positive.”。
如果有多个条件需要判断,可以使用 else if
结构:
if (条件1) {
// 条件1 为真时执行的语句块
语句1;
} else if (条件2) {
// 条件2 为真时执行的语句块
语句2;
} else {
// 所有条件都为假时执行的语句块
语句3;
}
这样可以依次判断多个条件,执行满足条件的语句块。
switch-case-break
结构是 C 语言中的一种多重条件判断结构,通常用于处理多个可能的取值情况。以下是 switch-case-break
结构的主要组成部分:
switch
语句:switch
语句后面跟随一个表达式,通常是一个整数或字符型的值。switch
语句将会根据表达式的值选择匹配的 case
分支。
switch (表达式) {
// case 分支
case 常量1:
语句1;
break;
// 其他 case 分支
case 常量2:
语句2;
break;
// 更多 case 分支
case 常量3:
语句3;
break;
// 可选的 default 分支
default:
默认语句;
}
case
分支:每个 case
后面跟随一个常量,表示匹配的值。如果 switch
后的表达式的值等于某个 case
后面的常量,程序将会执行该 case
后面的语句,直到遇到 break
或者 switch
结束。
break
语句:break
用于结束 switch
语句块。一旦执行到 break
,程序将跳出 switch
结构,继续执行 switch
语句后面的代码。
default
分支:default
是可选的分支,用于处理 switch
后的表达式没有匹配到任何 case
的情况。default
后面的语句将在没有匹配的情况下执行。如果没有 default
,程序将在没有匹配的情况下跳过 switch
语句。
下面是一个简单的例子:
#include <stdio.h>
int main() {
int num = 2;
switch (num) {
case 1:
printf("The number is 1\n");
break;
case 2:
printf("The number is 2\n");
break;
case 3:
printf("The number is 3\n");
break;
default:
printf("The number is not 1, 2, or 3\n");
}
return 0;
}
在这个例子中,switch
语句根据 num
的值选择相应的 case
分支进行执行。在这里,num
的值是 2,因此执行了第二个 case
后面的语句,输出 “The number is 2”。
for
循环是一种常见的循环结构,用于多次执行一段代码块。以下是 for
循环的基本形式:
for (初始化; 条件; 更新) {
// 循环体,当条件为真时执行
语句;
}
#include <stdio.h>
int main() {
// 使用 for 循环打印 0 到 4 的数字
for (int i = 0; i < 5; i++) {
printf("%d\n", i);
}
return 0;
}
在上述例子中,for
循环通过初始化 i
为 0,设置条件 i < 5
,以及在每次循环迭代后更新 i
的值来打印数字 0 到 4。
for
循环也可以用于遍历数组或其他数据结构:
#include <stdio.h>
int main() {
// 使用 for 循环遍历数组
int arr[] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
printf("%d\n", arr[i]);
}
return 0;
}
在这个例子中,for
循环用于遍历数组 arr
中的元素,并将每个元素打印出来。 for
循环是一个非常灵活的循环结构,适用于多种循环场景。
while
循环是一种常见的循环结构,用于根据条件反复执行一段代码块。以下是 while
循环的基本形式:
while (条件) {
// 循环体,当条件为真时执行
语句;
}
#include <stdio.h>
int main() {
// 使用 while 循环打印 0 到 4 的数字
int i = 0;
while (i < 5) {
printf("%d\n", i);
i++; // 更新循环控制变量
}
return 0;
}
在上述例子中,while
循环通过检查条件 i < 5
来确定是否继续执行循环体。在每次循环迭代时,会打印当前的 i
值,并通过 i++
更新循环控制变量 i
。循环将一直执行,直到条件为假。
与 for
循环不同,while
循环没有明确的初始化和更新步骤。初始化和更新通常在 while
循环外部完成,但也可以在循环体内部完成。
#include <stdio.h>
int main() {
// 使用 while 循环遍历数组
int arr[] = {1, 2, 3, 4, 5};
int i = 0; // 初始化循环控制变量
while (i < 5) {
printf("%d\n", arr[i]);
i++; // 更新循环控制变量
}
return 0;
}
在这个例子中,while
循环用于遍历数组 arr
中的元素,通过 i
来访问数组的不同位置。while
循环是一种灵活的循环结构,适用于各种不同的循环需求。
do-while
循环是一种与 while
循环类似的循环结构,但有一个重要的区别:do-while
循环先执行一次循环体,然后检查条件是否为真。以下是 do-while
循环的基本形式:
do {
// 循环体,至少执行一次
语句;
} while (条件);
#include <stdio.h>
int main() {
// 使用 do-while 循环打印 0 到 4 的数字
int i = 0;
do {
printf("%d\n", i);
i++; // 更新循环控制变量
} while (i < 5);
return 0;
}
在上述例子中,do-while
循环首先执行循环体,然后检查条件 i < 5
是否为真。即使条件一开始为假,循环体至少会被执行一次。
与 while
循环一样,do-while
循环通常用于根据条件反复执行一段代码块。使用 do-while
循环时,确保循环体内的语句至少执行一次,因为条件检查是在循环体执行之后进行的。
#include <stdio.h>
int main() {
// 使用 do-while 循环遍历数组
int arr[] = {1, 2, 3, 4, 5};
int i = 0; // 初始化循环控制变量
do {
printf("%d\n", arr[i]);
i++; // 更新循环控制变量
} while (i < 5);
return 0;
}
在这个例子中,do-while
循环用于遍历数组 arr
中的元素,与 while
循环一样,通过 i
来访问数组的不同位置。
continue
是在循环结构中使用的关键字,它用于跳过循环体内部的剩余代码,直接进入下一次循环迭代。在 for
、while
和 do-while
循环中,continue
的作用是终止当前迭代,直接跳到下一次迭代。
以下是 continue
的基本语法:
for (初始化; 条件; 更新) {
// 循环体开始
if (某条件) {
// 使用 continue 关键字
continue;
}
// 循环体的其他代码
}
while (条件) {
// 循环体开始
if (某条件) {
// 使用 continue 关键字
continue;
}
// 循环体的其他代码
}
do {
// 循环体开始
if (某条件) {
// 使用 continue 关键字
continue;
}
// 循环体的其他代码
} while (条件);
在上述结构中,如果某个条件满足,continue
将会跳过当前迭代中 continue
之后的代码,直接进行下一次迭代。
下面是一个使用 continue
的示例:
#include <stdio.h>
int main() {
// 使用 continue 在循环中跳过偶数
for (int i = 0; i < 5; i++) {
if (i % 2 == 0) {
// 跳过偶数
continue;
}
printf("%d\n", i);
}
return 0;
}
在这个例子中,for
循环用于遍历 0 到 4 的数字,当数字为偶数时,使用 continue
跳过偶数,直接进行下一次迭代。所以输出是奇数 1
、3
。
break
是在循环或者 switch
结构中使用的关键字,用于提前终止循环或者 switch
结构的执行。在循环中,break
用于跳出整个循环,而在 switch
结构中,它用于跳出 switch
结构。以下是 break
的基本用法:
break
:for (初始化; 条件; 更新) {
// 循环体开始
if (某条件) {
// 使用 break 关键字
break;
}
// 循环体的其他代码
}
while (条件) {
// 循环体开始
if (某条件) {
// 使用 break 关键字
break;
}
// 循环体的其他代码
}
do {
// 循环体开始
if (某条件) {
// 使用 break 关键字
break;
}
// 循环体的其他代码
} while (条件);
switch
结构中使用 break
:switch (表达式) {
case 常量1:
// 语句1
break;
case 常量2:
// 语句2
break;
// 更多 case 分支
default:
// 默认情况的语句
break;
}
在循环中,break
语句用于跳出整个循环,而在 switch
结构中,break
语句用于跳出 switch
结构。 break
语句结束当前的循环或 switch
结构,将控制流传递到循环或 switch
结构之后的代码。
以下是一个使用 break
在循环中提前结束的示例:
#include <stdio.h>
int main() {
// 使用 break 在循环中提前结束
for (int i = 0; i < 5; i++) {
if (i == 3) {
// 当 i 等于 3 时提前结束循环
break;
}
printf("%d\n", i);
}
return 0;
}
在这个例子中,for
循环将打印数字 0
、1
、2
,当 i
等于 3
时,使用 break
提前结束循环。
return
是一个在函数中使用的关键字,用于结束函数的执行并返回一个值。以下是 return
的基本用法:
返回类型 函数名(参数列表) {
// 函数体开始
// ...
return 表达式; // 返回一个值并结束函数
// ...
}
#include <stdio.h>
// 函数示例,计算两个整数的和
int add(int a, int b) {
int sum = a + b;
return sum; // 返回两数之和并结束函数
}
int main() {
// 调用 add 函数
int result = add(3, 4);
printf("Sum: %d\n", result);
return 0;
}
在上述例子中,add
函数接受两个整数参数 a
和 b
,计算它们的和,并使用 return
返回结果。在 main
函数中调用 add
函数,将返回值存储在 result
变量中,并打印出来。
需要注意的是,return
不仅用于返回值,还可以用于提前结束函数的执行,即在函数体中的任何位置使用 return
都可以结束函数的执行。如果函数的返回类型是 void
(即没有返回值),可以省略 return
,或者使用 return;
来结束函数。
#include <stdio.h>
// 无返回值的函数示例
void printMessage() {
printf("Hello, World!\n");
return; // 可以省略,或者写成 return;
}
int main() {
// 调用 printMessage 函数
printMessage();
return 0;
}
在上述例子中,printMessage
函数没有返回值,因此可以省略 return
或者使用 return;
来结束函数。
goto
是 C 语言中的一个关键字,它允许程序跳转到程序中的标签位置。使用 goto
通常被认为是一种不推荐的编程实践,因为它可能导致代码变得难以理解和维护,尤其是在大型程序中。通常,使用结构化的控制流程(如 if
、while
、for
等)可以更清晰地表达程序的逻辑。
以下是 goto
的基本语法:
goto 标签;
// ...
标签:
// 标签位置的代码
以下是一个简单的使用 goto
的例子:
#include <stdio.h>
int main() {
int i = 0;
loop_start:
if (i < 5) {
printf("%d\n", i);
i++;
goto loop_start; // 跳转到 loop_start 标签位置
}
return 0;
}
在上述例子中,程序使用 goto
实现了一个简单的循环。这个例子中的循环可以用更常见的 for
循环来代替,但它演示了如何使用 goto
。
虽然 goto
在一些情况下可能是有效的,但通常应该避免使用它,因为它容易导致代码混乱,不易理解,并且可能引入难以调试的问题。在现代编程中,结构化的控制流程通常更受推荐。
关键字 | 主要用途 | 描述 |
---|---|---|
return | 函数中 | 返回特定值(或void表示不返回值),结束函数执行 |
continue | 循环中 | 结束当前循环的当前迭代,开始下一轮循环 |
break | 循环或switch结构中 | 跳出当前循环或switch结构,结束其执行 |
goto | 无条件跳转 | 导致无条件跳转到标记的语句,应慎用以避免复杂的控制流 |
typedef
是 C 语言中用于为数据类型创建新的名称的关键字。通过 typedef
,可以为现有的数据类型定义新的名称,使代码更易读、更模块化。下面是一个关于 typedef
的简单示例:
#include <stdio.h>
// 使用 typedef 为 int 定义新的名称
typedef int Integer;
int main() {
// 使用新的名称 Integer 替代 int
Integer x = 42;
// 打印值
printf("Value: %d\n", x);
return 0;
}
在这个例子中,typedef
被用来为 int
定义了一个新的名称 Integer
。现在,Integer
可以用来声明变量,它等价于 int
。
需要注意的是,typedef
并不会创建新的数据类型,它只是为已存在的数据类型定义了一个新的名称。这样做的好处之一是可以提高代码的可读性和可维护性。
然而,当使用 typedef
时,要注意不要引起混淆或增加代码的复杂性。选择清晰而有意义的名称是很重要的。例如,不要使用过于简单或容易混淆的名称。
#define
是 C 语言中用于创建宏定义的预处理指令。宏定义允许在代码中使用符号名称来表示常量值、代码片段或函数。下面是一个关于 #define
宏定义的简单示例:
#include <stdio.h>
// 使用 #define 定义常量
#define PI 3.14159
// 使用 #define 定义宏函数
#define SQUARE(x) ((x) * (x))
int main() {
// 使用定义的常量
double radius = 5.0;
double area = PI * SQUARE(radius);
// 打印计算结果
printf("Area: %f\n", area);
return 0;
}
在这个例子中,#define
用于定义常量 PI
和宏函数 SQUARE
。宏函数 SQUARE
接受一个参数 x
,并返回其平方的值。
需要注意的是,在宏定义中,最好使用括号确保表达式的正确性,尤其是在宏函数中。例如,SQUARE(x)
中的括号是为了防止在使用时引起不期望的优先级问题。
#define
还可以用于简单的文本替换,例如:
#define MAX_SIZE 100
// 使用 MAX_SIZE 替代 100
int array[MAX_SIZE];
这样在代码中使用 MAX_SIZE
就相当于使用 100
。
虽然宏定义具有一定的灵活性,但也需要谨慎使用,以避免引入难以排查的错误。在现代 C 语言中,常常使用 const
关键字来定义常量,而使用函数来代替宏函数,以提高代码的可读性和可维护性。
typedef
和 #define
宏定义是在 C 和 C++ 编程中用来创建别名和符号常量的两种不同方法,它们有一些重要的区别。
用途:
typedef
:typedef
用于创建新的类型别名,使得一个已存在的数据类型可以使用一个不同的名字来表示,从而增加代码的可读性和可维护性。
#define
:#define
用于创建预处理器宏,可以用来定义符号常量、宏函数以及字符串替换等,用于在编译时进行文本替换。
编译时 vs. 运行时:
typedef
是在编译时处理的,它仅仅是为了增加代码的可读性,不会引入额外的代码。它创建了一个新的类型名,但不创建实际的变量或常量。
#define
宏定义是在预处理阶段处理的,它是通过文本替换来工作的,可以引入新的符号常量或代码。这些宏会在编译时替换为它们的定义。
适用范围:
typedef
通常用于定义用户自定义的类型别名,例如 typedef int MyInt;
将 MyInt
视为 int
的别名。
#define
用于定义符号常量和宏函数,例如 #define PI 3.14159
可以用于在代码中将 PI
替换为 3.14159
。
类型安全:
typedef
是类型安全的,因为它只是为已有类型创建别名。类型检查仍然适用。
#define
可能不是类型安全的,因为它只是简单的文本替换,不进行类型检查。这可能导致潜在的错误,特别是在复杂的宏定义中。
下面是一些示例,以展示两者的不同用法:
使用 typedef
创建类型别名:
typedef int MyInt;
MyInt x = 5;
使用 #define
创建符号常量:
#define PI 3.14159
double circleArea = 2 * PI * radius;
总之,typedef
和 #define
宏定义都有其自己的用途,你应该根据具体的需求选择适当的方式。 typedef
更适用于创建类型别名,而 #define
更适用于创建符号常量和宏函数。
char :声明字符型变量或函数
short :声明短整型变量或函数
int : 声明整型变量或函数
long :声明长整型变量或函数
signed :声明有符号类型变量或函数
unsigned :声明无符号类型变量或函数
float :声明浮点型变量或函数
double :声明双精度变量或函数
struct :声明结构体变量或函数
union :声明共用体(联合)数据类型
enum :声明枚举类型
void :声明函数无返回值或无参数,声明无类型指针
auto :声明自动变量,一般不使用
extern :声明变量是在其他文件中声明
register :声明寄存器变量
static :声明静态变量
typedef :用以给数据类型取别名(但是该关键字被分到存储关键字分类中,虽然看起来没什么相关性)
注意:在C语言中存储类型关键字在使用时只能存在一个,不能共存
const :声明只读变量
sizeof :计算数据类型长度
volatile :说明变量在程序执行中可被隐含地改变