理解C语言中几个常见修饰符

简介: 写在前面 今天下午一个同事问「register」关键字是什么作用?噢,你说的是「register」啊,它的作用是……脑袋突然断片儿,我擦,啥意思来着,这么熟悉的陌生感。做C语言开发时间也不短了,不过好像没有用到过「register」,但作用还是知道的,一下子想不起来了,一万个草泥马飞奔过来。

 

写在前面

今天下午一个同事问「register」关键字是什么作用?噢,你说的是「register」啊,它的作用是……脑袋突然断片儿,我擦,啥意思来着,这么熟悉的陌生感。做C语言开发时间也不短了,不过好像没有用到过「register」,但作用还是知道的,一下子想不起来了,一万个草泥马飞奔过来。

其实C语言中除了register外,还包含常见的conststaticvolatileautoextern等修饰符,现在一起再总结一下好了。

register 修饰符

register,寄存器变量,告诉编译器它所声明的变量在程序中使用的频率非常高,请编译器尽量将此变量放在寄存器中,这样程序执行速度更快。但实际上编译器不一定这么做,可以忽略此选项。

register 修饰符的几点注意点:

  • 变量必须是 CPU 接受的类型,单个值,长度小于等于整数的长度
  • 只能使用于局部变量和函数形参,全局(register)变量是非法的
  • 无论寄存器变量是否存放在寄存器中,它地址都是不能访问的(取&)
  • 其实过量的寄存器声明并没有什么坏处,寄存器可以忽略

const 修饰符

const修饰普通变量

有时候我们希望定义一个变量,它的值在整个作用域都不能变,比如定义缓冲区大小等,可以用 const 来修饰。

// 定义常量 strlen
const int strlen = 4096;
// 试图修改 strlen 变量,编译器会报错
strlen = 2048;

一般常量在定义时同时进行初始化,否则在定义完之后不能对其进行赋值操作,常见的初始化方式有:

const int num = get_num();  // 运行时初始化
const int num = n; // 运行时初始化
const int num = 10; // 编译时初始化

const 变量真的就不能修改吗?看个例子:

const int bufsize = 1024;
int *p = &bufsize;
*p = 2048;
printf("bufsize = %d\n", bufsize);

打印结果是2048。其实 const 修饰的变量不变的本质含义是程序中通过引用变量符号 bufsize 时不能够进行修改,而不是 bufsize 变量所指向那段内存数据不能修改。

const修改指针变量

const 可以与指针变量一起使用,可以限制指针变量,也可以限制指针变量指向的内容。

const int *ptr;     // 指针指向内容不能修改
int const *ptr; // 与第1种等价
int* const ptr; // 指针ptr变量本身不能修改
const int* const ptr; // 指针变量和指针变量指向内容都不能修改

const修改函数参数

其实C语言中使用 const 定义常量并没有什么优势,完全可以使用#define来替代。const 通常用在函数形参中,当形参是一个指针,为了防止函数内部修改指针指向的内容时,就可以用 const 限制。


size_t strlen(const char *s);
int strcmp(const char *s1, const char *s2);

常见C语言标准库中都有const限制,在我们自定义函数中也可以适当使用 const 来保证程序的健壮性。

const 类型与非 const 类型转换

当一个指针类似const char *str1,表示str1指针指向内容不能修改;但如果将 str1 赋值给 str2,这时 str2 没有通过 const 限制,通过 str2 就可以修改指针指向内容,这就失去了 const 的意义,编译器是不提倡这么做的。

const 与非 const 是两种类型,将非 const 指针赋值给 const 指针,编译器接受;如果将 const 指针赋值给非 const 指针,这样将增加指针变量的权限,不安全,有可能发生写入的危险。所以我们在写程序时遵守,对指针类型尽可能加 const 修饰;不能将 const 指针赋值给 const 指针。

static 修饰符

static 修饰符在程序中使用最为广泛,它大概有如下几种用法:

  • 修饰局部变量:增加了局部变量的生命周期,若定义未初始化,则默认初始化为0
  • 修饰全局变量:缩小了全局变量的作用域,限制在本模块(文件)中访问
  • 修饰函数:缩小了函数的作用于,限制函数只能被本模块调用

volatile 修饰符

关键字 volatile 感觉是和 register 有点相反的意思,表示变量随时可能被修改,且系统对实时性要求很高,请一定从内存中读取内容,不要直接拷贝寄存器中的数据,有可能数据老旧。常见的使用场合包括中断服务程序和嵌入式系统的寄存器相关操作。

extern 修饰符

关键字 extern 常用在变量和函数声明前,用来说明此变量或函数是在别处定义过的,要在此处引用。
在 hello.c 中:

void hello()
{
printf("Hello.\n");
}

在 main.c 文件中:

extern void hello();
hello(); // 声明之后调用 hello 函数

在 main.c 文件被编译时,告诉编译器hello()在别的地方定义过了,这里只是引用一下,放心编译好了,在程序最后链接的时候会去找hello实际定义的函数。

auto 修饰符

关键字 auto 其实可以理解为就是局部变量的显示说明,程序中很少去显示声明某个变量为 auto 的。

 

相关文章
|
前端开发 JavaScript C语言
《C语言编程初学者指南》一2.5 使用转换修饰符
本节书摘来自华章出版社《C语言编程初学者指南》一书中的第2章,第2.5节,作者【美】Keith Davenport(达文波特) , M1ichael Vine(维恩),更多章节内容可以访问云栖社区“异步社区”公众号查看 2.5 使用转换修饰符 由于信息是以二进制数据的形式存储在计算机内存中的,并且一系列的1和0读起来并不是特别有趣,作为C程序员,你必须特意告诉printf()这样的输入或输出函数,如何以一种对人类更加友好的形式把数据显示为信息。
1878 0
|
8天前
|
存储 Serverless C语言
【C语言基础考研向】11 gets函数与puts函数及str系列字符串操作函数
本文介绍了C语言中的`gets`和`puts`函数,`gets`用于从标准输入读取字符串直至换行符,并自动添加字符串结束标志`\0`。`puts`则用于向标准输出打印字符串并自动换行。此外,文章还详细讲解了`str`系列字符串操作函数,包括统计字符串长度的`strlen`、复制字符串的`strcpy`、比较字符串的`strcmp`以及拼接字符串的`strcat`。通过示例代码展示了这些函数的具体应用及注意事项。
|
11天前
|
存储 C语言
C语言程序设计核心详解 第十章:位运算和c语言文件操作详解_文件操作函数
本文详细介绍了C语言中的位运算和文件操作。位运算包括按位与、或、异或、取反、左移和右移等六种运算符及其复合赋值运算符,每种运算符的功能和应用场景都有具体说明。文件操作部分则涵盖了文件的概念、分类、文件类型指针、文件的打开与关闭、读写操作及当前读写位置的调整等内容,提供了丰富的示例帮助理解。通过对本文的学习,读者可以全面掌握C语言中的位运算和文件处理技术。
|
11天前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。
|
16天前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
16天前
|
C语言
C语言 字符串操作函数
本文档详细介绍了多个常用的字符串操作函数,包括 `strlen`、`strcpy`、`strncpy`、`strcat`、`strncat`、`strcmp`、`strncpy`、`sprintf`、`itoa`、`strchr`、`strspn`、`strcspn`、`strstr` 和 `strtok`。每个函数均提供了语法说明、参数解释、返回值描述及示例代码。此外,还给出了部分函数的自实现版本,帮助读者深入理解其工作原理。通过这些函数,可以轻松地进行字符串长度计算、复制、连接、比较等操作。
|
17天前
|
SQL 关系型数据库 C语言
PostgreSQL SQL扩展 ---- C语言函数(三)
可以用C(或者与C兼容,比如C++)语言编写用户自定义函数(User-defined functions)。这些函数被编译到动态可加载目标文件(也称为共享库)中并被守护进程加载到服务中。“C语言函数”与“内部函数”的区别就在于动态加载这个特性,二者的实际编码约定本质上是相同的(因此,标准的内部函数库为用户自定义C语言函数提供了丰富的示例代码)
|
1月前
|
C语言
【C语言】字符串及其函数速览
【C语言】字符串及其函数速览
23 4
|
1月前
|
编译器 程序员 C语言
【C语言篇】从零带你全面了解函数(包括隐式声明等)(下篇)
⼀般情况下,企业中我们写代码时候,代码可能⽐较多,不会将所有的代码都放在⼀个⽂件中;我们往往会根据程序的功能,将代码拆分放在多个⽂件中。
|
1月前
|
测试技术 C语言
C语言中的void函数
C语言中的void函数