本篇文章属于C语言进阶篇的“C语言关键字”,旨在分享我对C语言关键字的深度学习和了解。同时带领大家深入浅出的走进C语言进阶知识——关键字篇!
目录
一、前言
当你阅读到这篇文章时,我想问你几个问题?你知道C语言的关键字有多少个吗?我随便举个关键字,比如sizeof,它的用法是什么?你会不会觉得它是函数?
auto:
- 用于声明自动变量,通常在函数内部使用。自动变量的生命周期与其所在的块(block)相同。
break:
- 用于终止循环(for、while、do-while)或 switch 语句中的执行,并跳转到循环或 switch 结构后面的语句。
case:
- 用于定义 switch 语句中各种可能的情况。
char:
- 用于声明一个字符型变量或定义一个字符型数组。
const:
- 用于声明一个常量,一旦被赋值后不能再修改。
continue:
- 用于终止当前循环的剩余部分,并继续下一次循环。
default:
- 在 switch 语句中,用于标记没有与任何 case 标签匹配的情况。
do:
- 用于创建一个 do-while 循环结构,即先执行一次循环体,然后根据条件继续执行。
double:
- 用于声明双精度浮点数变量或定义双精度浮点数数组。
else:
- 用于在 if 条件语句不成立时执行的代码块。
enum:
- 用于定义枚举类型及其取值。
extern:
- 用于声明一个全局变量,通常用于引用在别处定义的全局变量。
float:
- 用于声明单精度浮点数变量或定义单精度浮点数数组。
for:
- 用于创建一个 for 循环结构,控制循环次数及条件。
goto:
- 用于无条件转移控制到程序中的另一位置。
if:
- 用于创建一个条件语句,根据条件判断是否执行后续的语句块。
int:
- 用于声明整型变量或定义整型数组。
long:
- 用于声明长整型变量或定义长整型数组。
register:
- 用于声明寄存器变量,建议编译器将该变量存储在CPU寄存器中,以提高访问速度。
return:
- 用于从函数中返回一个值,并可选择性地返回一个值给调用者。
short:
- 用于声明短整型变量或定义短整型数组。
signed:
- 用于声明有符号类型变量。
sizeof:
- 用于获取数据类型或变量的长度(以字节为单位)。
static:
- 用于声明静态变量、静态函数或限制函数的作用域。
struct:
- 用于定义结构体类型及其成员。
switch:
- 用于创建一个 switch 语句,根据表达式的值选择执行不同的代码分支。
typedef:
- 用于创建新的数据类型名称。
union:
- 用于定义共用体类型及其成员,共用体的所有成员共享同一块内存。
unsigned:
- 用于声明无符号类型变量。
void:
- 用于指定函数没有返回值或指针没有类型。
volatile:
- 用于告诉编译器,变量的值可能会在程序的控制之外被改变。
while:
- 用于创建一个 while 循环结构,根据条件重复执行语句块。
二、 正文
(一)、什么是定义?
在C语言中,"定义"通常指的是声明一个变量、函数或类型,并且在需要时为其分配存储空间或实现其功能。具体来说:
1、变量定义:变量定义是指在程序中为一个变量分配内存空间,并且可以给变量一个初始值。例如:
int age = 30; // 定义一个整型变量age,并赋初值30
2、函数定义:函数定义是指实现函数的具体代码,包括函数的返回类型、函数名、参数列表和函数体。例如:
int add(int a, int b) { // 定义一个函数add,计算两个整数的和
return a + b;
}
3、类型定义:类型定义是使用typedef
关键字为一个现有的数据类型(如结构体、枚举、数组等)创建一个新的名称。例如:
typedef struct {
char name[50];
int age;
} Person; // 定义一个新类型Person,表示一个人的信息
(二)、什么是声明?
在C语言中,"声明"是指告知编译器有关变量、函数或类型的信息,但不分配存储空间或提供具体实现。声明提供了编译器需要的基本信息,以便正确地编译程序。具体来说:
1、变量声明:变量声明告诉编译器变量的名称及其类型,以便在程序中使用这个变量。例如:
extern int x; // 声明一个整型变量x,告诉编译器x存在但不提供其具体的定义
2、函数声明:函数声明告知编译器有关函数的名称、返回类型、参数列表和函数的约定(如调用规则、返回值)。函数声明通常出现在函数的调用点之前,以确保编译器正确处理函数调用。例如:
int add(int a, int b); // 声明一个函数add,告诉编译器有这样一个函数,但不提供具体实现
3、类型声明:类型声明使用typedef
关键字为一个现有的数据类型创建一个新的名称。这种声明定义了一个新类型的别名,可以帮助提高代码的可读性和可维护性。例如:
typedef unsigned int uint32_t; // 声明一个新类型uint32_t,作为unsigned int的别名
声明的作用在于允许编译器在编译过程中识别和验证变量、函数和类型的使用,而不需要具体的实现细节。编译器可以在后续的编译阶段找到实际的定义,以便生成最终的可执行代码。
(三)、关键字详解
1、auto
自动变量声明: 在函数内部声明的变量默认情况下就是自动变量,也就是说它们的存储期限只限于它们所在的块(函数或复合语句)。使用
auto
关键字显式声明变量为自动变量在现代C语言中已经不常见,因为所有在函数内部声明的变量默认就是自动变量。
auto int x; // 显式声明x为自动变量,但这种用法在现代C语言中已经不推荐使用
C++中的auto关键字: 在C++中,
auto
关键字用于推断变量的类型,使得变量的类型声明更加简洁和灵活。这种用法在C++11标准中引入,并被广泛使用。
auto x = 5; // 推断x的类型为int
auto y = 3.14; // 推断y的类型为double
2、register
在C语言中,
register
用于向编译器建议将变量存储在寄存器中,以便提高访问速度。然而,现代编译器通常会忽略这种建议,因为它们能够更好地优化变量的存储和访问。
声明寄存器变量:使用
register
关键字声明的变量通常被放置在寄存器中,以提高对变量的访问速度。但是,编译器可以忽略这个建议,特别是当寄存器空间有限或者对变量的频繁访问无法优化时。
register int x; // 声明一个整型变量x,建议编译器将其存储在寄存器中
不能对寄存器变量取地址:
由于寄存器变量存储在寄存器中,而不是内存中,因此不能获取寄存器变量的地址。因此,以下代码会导致编译错误。
register int x;
int *ptr = &x; // 错误,不能对寄存器变量取地址
建议性质:
register
关键字仅仅是对编译器的建议,而不是强制要求。编译器可以根据自身的优化策略决定是否将变量存储在寄存器中,或者在某些情况下,编译器可能会完全忽略register
关键字。
限制:
register
关键字只能用于自动变量和函数参数。不推荐在现代C编程中频繁使用register
,因为大多数现代编译器已经足够智能以有效地管理变量的存储和访问。在现代编程实践中,程序员通常不必显式使用
register
关键字来优化变量的存储,而是应该依赖编译器自动进行优化。因此,register
关键字在实际开发中并不常见。
3、static
用于控制变量和函数的作用域、生命周期和存储位置。
第一个作用:修饰变量。变量又分为局部和全局变量,但它们都存在内存的静态区。
静态全局变量,作用域仅限于变量被定义的文件中,其他文件即使用extern声明也没法使用他。准确地说作用域是从定义之处开始,到文件结尾处结束,在定义之处前面的那些代码行也不能使用它。想要使用就得在前面再加extern***。恶心吧?要想不恶心,很简单,直接在文件顶端定义不就得了。
静态局部变量,在函数体里面定义的,就只能在这个函数里用了,同一个文档中的其他函数也用不了。由于被static修饰的变量总是存在内存的静态区,所以即使这个函数运行结束,这个静态变量的值还是不会被销毁,函数下次使用时仍然能用到这个值。
1. 静态全局变量
静态全局变量具有文件作用域,只能在声明它的文件内访问,不会被其他文件所见。
// 文件1.c
#include <stdio.h>
static int count = 0; // 静态全局变量
void increment_count(void) {
count++;
}
void print_count(void) {
printf("Count: %d\n", count);
}
// 文件2.c
#include <stdio.h>
extern void increment_count(void);
extern void print_count(void);
int main() {
increment_count();
print_count();
return 0;
}
count
是一个静态全局变量,它只能在1.c
文件中访问。2.c
文件通过函数调用间接访问和修改count
的值
2. 静态局部变量
静态局部变量具有静态存储期限,生命周期延长到整个程序执行期间,但只能在声明它的函数内部访问。
#include <stdio.h>
void function() {
static int x = 0; // 静态局部变量
x++;
printf("x: %d\n", x);
}
int main() {
function(); // 输出 x: 1
function(); // 输出 x: 2
return 0;
}
第二个作用:修饰函数。
函数前加 static使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。
关键字static有着不寻常的历史。起初,在C中引入关键字static是为了表示退出一个块后仍然存在的局部变量。随后,static在C中有了第二种含义:用来表示不能被其它文件访问的全局变量和函数。为了避免引入新的关键字,所以仍使用static关键字来表示这第二种含义。
3. 静态函数
在C语言中,使用
static
关键字可以声明静态函数,这使得函数只能在声明它的文件内部调用,无法被其他文件所使用。
// file1.c
#include <stdio.h>
static void static_function() {
printf("This is a static function.\n");
}
// file2.c
#include <stdio.h>
extern void static_function(); // 在其他文件中声明静态函数,编译器会报错
int main() {
static_function(); // 在同一文件内调用静态函数
return 0;
}
static_function()
是一个静态函数,只能在file1.c
文件中调用。如果在其他文件中尝试声明该函数,则编译器会报错。
static
关键字在C语言和C++中的用法多样,但其主要功能是控制变量和函数的作用域、生命周期和存储位置。静态变量有助于实现数据的持久化或限制对特定函数和变量的访问范围。
4、基本数据类型
1. 整型(Integer Types)
整型用于存储整数值,可以分为有符号和无符号类型。在C语言中,有以下几种整型:
- char:用于存储字符数据,通常是一个字节大小。可以是有符号或无符号的,具体取决于编译器实现。
char ch = 'A'; // 声明一个字符变量,并初始化为字符 'A'
char ch2 = 65; // ASCII码为65的字符 'A'
- int:用于存储整数,通常为机器字长大小(通常是4个字节)。可以是有符号或无符号的。
int num = 10; // 声明一个整型变量,并初始化为10
- short / short int:短整型,通常为2个字节大小。
short sh = 100; // 声明一个短整型变量,并初始化为100
- long / long int:长整型,通常为4或8个字节大小。
long lg = 1000000L; // 声明一个长整型变量,并初始化为1000000
- long long / long long int:长长整型,通常为8个字节大小(C99引入)。
2. 浮点型(Floating-Point Types)
浮点型用于存储小数(实数)值,分为单精度浮点数和双精度浮点数。
- float:单精度浮点数,通常为4个字节大小。
float f = 3.14f; // 声明一个单精度浮点数,并初始化为3.14
- double:双精度浮点数,通常为8个字节大小。
double d = 3.141592653589793; // 声明一个双精度浮点数,并初始化为圆周率的近似值
3. 字符型(Character Type)
字符型用于存储单个字符,在内存中以整数形式存储。在C语言中,
char
类型既可以用于存储字符,也可以用于存储小整数。
char ch = 'A'; // 声明一个字符变量,并初始化为字符 'A'
4. 空类型(Void Type)
void
类型用于指示函数无返回值,或者指针无类型。不能声明void
类型的变量。
void func(void) {
// 函数没有返回值,使用 void
}
三、结语
后续进阶内容,敬请关注下文分享:
《C语言进阶——一文带你深入了解“C语言关键字”(中篇)》