器→工具, 编程语言

C语言学习之关键字

钱魏Way · · 0 次浏览

C语言的关键字由C语言的标准决定。

C99标准中定义的关键字

数据类型关键字

基本数据类型(8个)

  • void:声明函数无返回值或无参数,声明无类型指针,显式丢弃运算结果
  • char:字符型类型数据,属于整型数据的一种
  • int:整型数据,通常为编译器指定的机器字长
  • float:单精度浮点型数据,属于浮点数据的一种
  • double:双精度浮点型数据,属于浮点数据的一种
  • _Bool: 这是C99引入的新类型,表示布尔类型,在h头文件中,定义了宏bool,true,false。
  • _Complex: 用于表示复数类型,在h头文件中,定义了宏complex,用于支持复数的运算。
  • _Imaginary: 用于表示虚数类型。但是,这个类型在大多数实现中并未使用,通常我们使用_Complex表示复数类型。

详见:C语言:基本数据类型

类型修饰关键字(4个)

  • short:修饰int,短整型数据,可省略被修饰的int。
  • long:修饰int,长整形数据,可省略被修饰的int。
  • signed:修饰整型数据,有符号数据类型
  • unsigned:修饰整型数据,无符号数据类型

复杂类型关键字(5个)

  • struct:结构体声明
  • union:共用体声明
  • enum:枚举声明
  • typedef:声明类型别名
  • sizeof:得到特定类型或特定类型变量的大小

存储级别关键字(8个)

  • auto:指定为自动变量,由编译器自动分配及释放。通常在栈上分配
  • static:指定为静态变量,分配在静态变量区,修饰函数时,指定函数作用域为文件内部
  • register:指定为寄存器变量,建议编译器将变量存储到寄存器中使用,也可以修饰函数形参,建议编译器通过寄存器而不是堆栈传递参数
  • extern:指定对应变量为外部变量,即在另外的目标文件中定义,可以认为是约定由另外文件声明的对象的一个“引用“
  • const:与volatile合称“cv特性”,指定变量不可被当前线程/进程改变(但有可能被系统或其他线程/进程改变)
  • volatile:与const合称“cv特性”,指定变量的值有可能会被系统或其他进程/线程改变,强制编译器每次从内存中取得该变量的值
  • inline: 用于声明内联函数。这是一个函数修饰符,它建议编译器将函数体插入每个调用该函数的地方以减少函数调用的开销。
  • restrict: 用在指针类型的参数前,表示该指针是访问数据对象的唯一且初次的方式,这个关键字主要在程序优化时使用。

auto

在C语言中,auto是一个关键词,用于定义一个变量在函数内的自动存储类别。在函数内部定义的变量默认情况下都以auto存储类别声明,可以使用auto关键字来明确声明,但是通常情况下不需要这样做。使用auto关键字声明的变量会在函数执行期间自动创建,并在函数结束时自动销毁。

例如:

void example() {
  auto int i = 10; // 等同于 int i = 10;
  printf("%d", i);
}

在上面的例子中,我们使用auto关键字声明变量i,然后将其初始化为10。但是,由于auto是默认的存储类别,因此可以省略auto关键字,直接使用int i = 10;声明变量。最后,我们使用printf函数将变量i的值打印到控制台上。

static

在C语言中,static是一个关键词,用于声明一个静态变量或者静态函数,其作用范围仅限于当前文件或者当前函数。

静态变量

静态变量是指生命周期持续整个程序运行期间,存储在静态存储区的变量。与普通变量不同,静态变量只会被初始化一次,其余的赋值操作都会直接修改其值。

void example(){
  static int count = 0;
  count++;
  printf("count: %d\n", count);
}

int main(){
  example(); // count: 1
  example(); // count: 2
  example(); // count: 3
  return 0;
}

在上面的例子中,我们定义了一个静态变量count,并将其初始化为0。每次调用example函数时,count的值都会加1,并且在调用结束后保留其值,所以每次调用都会输出一个递增的数字。

静态函数

静态函数是指不可见于其他文件的函数,用于封装代码和隐藏实现细节。静态函数只能在当前文件中使用,不能被其他文件调用。

// test.c
static int add(int a, int b){
  return a + b;
}

// main.c
#include <stdio.h>

int main(){
  int sum = add(1, 2); // 编译错误,add函数未定义
  printf("sum: %d\n", sum);
  return 0;
}

在上面的例子中,我们在test.c文件中定义了一个静态函数add,用于计算两个整数的和。但是,当我们在main.c文件中尝试调用该函数时,会出现编译错误,因为add函数未定义。这是因为add函数是一个静态函数,只能在当前文件中使用。

register

在C语言中,register是一个关键字,用于请求将变量存储在寄存器中,以便更快地访问。但是请注意,register只是一个请求,编译器可以选择忽略它。

使用register关键字声明的变量通常只有局部作用域,只能在函数内使用,不能被其他函数访问。

void example(){
  register int i = 10;
  printf("%d\n", i);
}

在上面的例子中,我们使用register关键字声明变量i,并将其初始化为10。这将告诉编译器将变量i存储在寄存器中,以便更快地访问。但是,请注意,register只是一个请求,编译器可以选择忽略它。

extern

在C语言中,extern是一个关键字,用于声明一个变量或函数是在其他文件中定义的。当我们在一个文件中使用另一个文件中定义的变量或函数时,需要使用extern关键字进行声明。

在C语言中,变量默认情况下是具有外部链接的,这意味着它们可以被定义在其他文件中的函数和变量访问。但是,如果想在当前文件中引用其他文件中定义的变量或函数,就需要使用extern关键字。

// test.c
int count = 0;

// main.c
#include <stdio.h>

extern int count; // 声明变量count在另一个文件中定义

void example(){
  printf("count: %d\n", count);
}

int main(){
  example(); // count: 0
  count++;
  example(); // count: 1
  return 0;
}

在上面的例子中,我们在test.c文件中定义了一个全局变量count,然后在main.c文件中使用extern关键字声明该变量。这使得我们可以在example函数中访问count变量,而不需要在example函数中重新定义它。最后,我们在main函数中增加了count的值,并再次调用example函数来验证其值已经改变。

const

在C语言中,const是一个关键字,用于定义一个常量,即一个不能被修改的值。使用const关键字声明的变量在程序执行期间不能被修改,任何试图修改它们的操作都将导致编译错误。

void example(){
  const int i = 10;
  printf("%d\n", i);
}

int main(){
  example(); // 10
  return 0;
}

在上面的例子中,我们使用const关键字声明变量i,并将其初始化为10。由于变量i是一个常量,它不能在example函数中被修改。因此,任何试图修改它的操作都将导致编译错误。

const关键字也可以用于指针类型,表示指针所指向的值不能被修改。

void example(){
  const int* p = NULL;
  int i = 10;
  p = &i;
  printf("%d\n", *p);
}

int main(){
  example(); // 10
  return 0;
}

在上面的例子中,我们使用const关键字声明指针p,它指向一个整数常量。这意味着我们不能使用p来修改i的值。但是,我们仍然可以通过p来访问i的值。

volatile

在C语言中,volatile是一个关键字,用于告诉编译器一个变量的值可能会在程序执行期间被修改。这将防止编译器在优化代码时对该变量进行优化,保证变量的值在程序执行期间始终是最新的。

void example(){
  volatile int i = 10;
  while(i == 10){
    // do something
  }
}

在上面的例子中,我们使用volatile关键字声明变量i,并将其初始化为10。然后,在一个while循环中,我们检查变量i的值是否为10。因为编译器知道变量i的值可能在程序执行期间被修改,所以它不会将循环优化为一个无限循环,而是保留循环中对变量i的检查操作。

volatile关键字通常用于与硬件相关的程序,例如设备驱动程序或嵌入式系统中的应用程序。在这些情况下,变量的值可能会被外部的事件或设备修改,而不是由程序本身修改。

inline

在C语言中,inline是一个关键字,用于告诉编译器将函数的代码插入到调用该函数的地方,从而避免函数调用的开销。使用inline关键字声明的函数被称为内联函数。但是请注意,inline只是一个请求,编译器可以选择忽略它。

inline int add(int a, int b){
  return a + b;
}

int main(){
  int sum = add(1, 2); // 编译器可能将add函数展开为表达式1+2
  printf("sum: %d\n", sum);
  return 0;
}

在上面的例子中,我们使用inline关键字声明函数add,它将两个整数相加并返回结果。当我们在main函数中调用add函数时,编译器可以将add函数的代码直接插入到调用该函数的地方,从而避免了函数调用的开销。

使用inline关键字声明内联函数可以提高程序的性能,但是请注意,过度使用内联函数可能会导致代码膨胀,从而增加可执行文件的大小。因此,我们需要根据具体情况权衡利弊,在必要的情况下使用inline关键字声明内联函数。

restrict

在C语言中,restrict是一个关键字,用于告诉编译器一个指针是可以独占使用的,也就是说,指针所指向的内存区域不会被其他指针所访问。使用restrict关键字声明的指针被称为受限指针。restrict关键字可以帮助编译器进行更好的代码优化。

void example(int* restrict a, int* restrict b, int* restrict c, int n){
  for(int i=0; i<n; i++){
    a[i] = b[i] + c[i];
  }
}

在上面的例子中,我们使用restrict关键字声明指针a、b和c,它们分别指向三个整数数组。因为这些指针是受限指针,编译器可以假设它们所指向的内存区域不会被其他指针所访问,从而进行更好的代码优化。

使用restrict关键字可以提高程序的性能,但是请注意,它需要我们确保指针之间没有别的重叠,否则会导致未定义行为。因此,我们需要谨慎使用restrict关键字,并确保其使用是正确的。

流程控制关键字

跳转结构(4个)

  • return:用在函数体中,返回特定值(或者是void值,即不返回值)
  • continue:结束当前循环,开始下一轮循环
  • break:跳出当前循环或switch结构
  • goto:无条件跳转语句

B分支结构(5个)

  • if:条件语句
  • else:条件语句否定分支(与if连用)
  • switch:开关语句(多重分支语句)
  • case:开关语句中的分支标记
  • default:开关语句中的“其他”分治,可选。

C循环结构(3个)

  • 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语言:控制流语句

C11标准中增加的关键字

  • _Alignas: 用于指定变量或类型的对齐要求。例如,_Alignas(double) int x将声明一个整型变量x,它的对齐方式与double类型相同。
  • _Alignof: 用于获取一个类型的对齐要求。例如,size_t a = _Alignof(double)将得到double类型的对齐要求。
  • _Atomic: 用于声明原子类型的变量,这些变量在多线程环境下进行读写操作时不会被中断。
  • _Generic: 提供了一种根据表达式的类型选择不同代码的方式,这是C11标准引入的一种类型泛化的方式。
  • _Noreturn: 用于标识一个函数不会返回。这可以帮助编译器进行优化。
  • _Static_assert: 用于编译时断言。如果其条件为假,编译器将产生一个错误。
  • _Thread_local: 用于声明线程局部存储持续期的变量,这样每个线程都有这个变量的一个实例,各个线程中的实例互不影响。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注