目录
C中其他关键字:inline、register、restrict
C中其他关键字:inline、register、restrict
1. inline
作用
inline放在函数返回类型前修饰函数,被修饰的函数就叫做内联函数。
static inline void function(void)
在C语言中,函数调用时保存现场,函数中变量的使用等都会占用栈空间。而且进入函数前的保存现场,跟函数调用结束后的恢复现场都需要时间花销。
当函数的调用代价大于直接将代码拷贝到运行程序中时,我们就会使用inline关键字建议编译器将函数直接拷贝到函数调用中直接运行来换取函数的调用。(例如:经常需要调用某简短的函数时)
(注意:inline是向编译器提建议,不是命令编译器,所以加inline修饰的函数是否变成内联函数,这由编译器做决定!!)
用法
inline的使用方法其实是固定的,它定义在头文件中,并用static修饰。
//inline.h
#include <stdio.h>
void func1(void);
static inline void ile(void)
{
printf("I'm inline function!\r\n");
}
在使用此内联函数的文件中包含此头文件即可。
//main.c
#include "inline.h"
int main (int argc, char **argv)
{
func1();
ile();
return 0;
}
其他
奇怪了,在C程序中,函数的定义不是放在.c文件中的吗?为啥你的inline函数要放在.h文件中呢?
这就从inline的原理上说起,inline是将调用处的函数名直接由函数体的拷贝替换掉。
编译的时候我们就要知道函数体在哪,所以必须要将它的定义放在头文件中,然后包含到调用的文件中。
还有重要一点就是,要加static修饰,因为是简单的拷贝,如果不加的话,编译时就会报错说你重复定义函数。
经查看编译后的.o文件中可以看到函数体被拷贝到了调用inline函数的文件中。
2. register
作用
使用register 关键字修饰的局部变量是一个寄存器变量,它请求编译器尽可能的将变量存在寄存器中,这样使得CPU省去了从内存中获取数据的时间,从而提高程序的运行效率。
一般我们会把经常使用或者频繁访问的局部变量用register修饰,这里需要注意的是register修饰的局部变量名。不可以对其取地址,比如register int num = 5,不能&num 。因为&是从内存中取。而num在寄存器中。
对于全局变量和函数则不可以使用register修饰。而且register修饰的数据类型必须是CPU所接收的数据类型。比如有的单片机不支持浮点型。那么register就不能修饰浮点型变量。
用法
当一个变量会被频繁调用时,可以用register去修饰变量,将变量保存在寄存器中,可以提升访问变量的速度。
register int a = 0;
其他
编译器的优化很大程度上就是通过数据流分析、调整读取内存的顺序等,减少CPU对内存的读写。因为内存的运行速度相对于CPU是很慢的,计算机中添加cache就是为了解决CPU和内存运行速度差异过大的问题。就运行速度来说:寄存器>cache>内存>外存。
3. restrict
作用
c99中新增加了一个类型定义,就是restrict。
概括的说,关键字restrict只用于限定指针;该关键字用于告知编译器,所有修改该指针所指向内容的操作全部都是基于(base on)该指针的,即不存在其它进行修改操作的途径;这样的后果是帮助编译器进行更好的代码优化,生成更有效率的汇编代码。
用法
例如:
int foo (int* x, int* y) {
*x = 0;
*y = 1;
return *x;
}
很显然函数foo()的返回值是0,除非参数x和y的值相同。可以想象,99%的情况下该函数都会返回0而不是1。然而编译起必须保证生成100%正确的代码,因此,编译器不能将原有代码替换成下面的更优版本
int f (int* x, int* y) {
*x = 0;
*y = 1;
return 0;
}
现在我们有了restrict这个关键字,就可以利用它来帮助编译器安全的进行代码优化了
int f (int *restrict x, int *restrict y) {
*x = 0;
*y = 1;
return *x;
}
此时,由于指针 x 是修改 x的唯一途径,编译起可以确认 “y=1; ”这行代码不会修改 *x的内容,因此可以安全的优化为
int f (int *restrict x, int *restrict y) {
*x = 0;
*y = 1;
return 0;
}
其他
void *memcpy( void * restrict dest , const void * restrict src, size_t n)
这是一个很有用的内存复制函数,由于两个参数都加了restrict限定,所以两块区域不能重叠,即 dest指针所指的区域,不能让别的指针来修改,即src的指针不能修改。相对应的函数 memmove(void *dest, const void *src, size_t)则可以重叠。