C语言类型限定符(type specifier)(二)——restrict和_Atomic详细教程

前言:C语言中的类型限定符一共有四个,const,volatile,restrict,_Atomic,前面的一片文章详细介绍了volatile的作用以及使用方法,本文为系列文章第二篇,介绍接下来的两个类型限定符,restrict,和 _Atomic,前一篇文章请参考:

C语言类型限定符(type specifier)(一)——volatile详细教程

一、restrict类型限定符

(1)restrict的限制作用——是给人看的

restrict的意思就是限定,限制,那到底是限定什么?限制什么呢?实际上,这个关键字只能够用于指针,当然这个指针可以是声明的变量,也可以是函数的参数,作用就是表明这个指针是初始化数据块以及访问这个数据块唯一的方式,而不通过其他变量或者是指针改变,这不就是一种限制 一种限定嘛

到底怎么用呢?看一个简单的例子:

int a;
int * restrict p = &a;
*p = 200;  //通过指针改变它的值
a = 100;   //通过它本身改变它的值    
printf("a is %d.\n", a); //输出的结果是100

这里一定会疑惑,不是说被restrict修饰指针才是初始化以及改变它所指向的变量值的唯一方式么,这里为什么会通过a自己去改变还行,居然没有报错,实际上restrict只是一个约定,而不是强制约束,即告诉我们编写代码的人,这个指针是一个被restrict修饰的指针,那么如果想要初始化或者是修改它所指向变量的值,最好就通过这个rerestrict修饰的指针来完成,而不要通过其他的方式,当然是用其他的方式也是可以的,只是不建议这么做。

为什么要有一种这样的约定呢?是为了让编译器能够更好的优化一部分代码,具体怎么优化呢?参见下面的代码:

(2)restrict的优化作用——给编译器看的

举个简单的例子

int foo (int* x, int* y) {
    *x = 0;
    *y = 1;
    return *x;
}

很显然函数foo()的返回值是0,不管给x,y传递进去什么参数,返回的都是0。虽然我们人的的确确知道,这个函数的返回值永远是0,但是编译器不知道啊,编译器只知道它总是返回x,所以编译没办法优化它,编译器不能将原有代码替换成下面的更优版本

int f (int* x, int* y) {
    *x = 0;
    *y = 1;
    return 0;  //注意:这里是不能替换哦!!!
}

但是,我们现在使用restrict来试一下,看看,现在我们有了restrict这个关键字,就可以利用它来帮助编译器安全的进行代码优化了,到底怎么优化呢?看下面的例子,将参数用restrict进行约束

int f (int *restrict x, int *restrict y) {
    *x = 0;
    *y = 1;
    return *x;
}

现在不仅我们人知道,要改变x,y这两个指针所指向的值,通过x,y是唯一的方式,不仅如此现在编译器也知道了这件事,它知道没有其他的方式来改变x,y所指向的值,而在代码中,改变x所指向的值的语句只有一句,那就是

*x = 0

此时,由于指针 x 是修改 *x的唯一途径,编译起可以确认 “*y=1; ”这行代码不会修改 *x的内容,也没有其他代码修改x的值,因此可以安全的优化为

int f (int *restrict x, int *restrict y) {
    *x = 0;
    *y = 1;
    return 0;  //这个地方是可以优化的
}

这就是restrict为什么告诉编译器来优化自己的原因了,当然了是不是说这个restrict一定是一种强制执行的约束呢?当然不是了,

看下面的代码

#include <stdio.h>
#include <stdlib.h>

int f (int *restrict x, int *restrict y) 
{
    *x = 0;
    int *px = x;
    *px=100;     //通过其他方式改变了x的值,也是正确的
    *y = 10000;  //
    return *x;   //所以返回的值100
}

int main(int argc, char **argv)
{
   
    int xx=10;
    int yy=20;
    int *x=&xx;
    int *y=&yy;
    int result=f(x,y);
    
	printf("result is %d.\n", result);

    getchar();
	return 0;
}

我们发现,我们依然是可以通过其他的方式来改变restrict所指向变量的值,所以这种约束并不是强制执行的,只不过这个时候如果编译器再进行优化,将返回值优化成结果 0 ,就会出错了,因为已经有别的指针 px ,改变了x的值了

 下面总结关于restrict类型限定符的一些说明

(1)restrict是C99的内容,C++中不支持该类型约束关键字,另外MSVC中也是不支持这个关键字的,他属于标准C的内容,经试验,MingW中GCC支持restrict;

(2)restrict更多的是给人看的,告诉人不要通过其他方式改变这个指针所指向的变量,但是这种约束并不是强制约束哦;

(3)restrict只能用于修饰指针,包括指针变量和指针形参;

(4)个人觉得restrict在性能上的优化微乎其微,没什么太大作用,可能这也是它不被广泛支持的原因之一吧!!

二、_Atomic类型限定符——C11,在stdatomic.h头文件

这个关键字比较简单,

并发程序的设计往往是通过多个线程来实现的,多线程最大的问题在于资源的竞争,即多个线程同时竞争使用某一个资源,这可能导致一些致命的问题,一种解决方式是通过添加“锁”的方式,保证及时的加锁以及锁的释放,保证在某一个时刻始终只有一个线程在使用该资源,CII中引入了一个新的类型修饰符,也可以达到同样的效果。

你如下面的操作

int age;
age = 25;

这只是普通的变量定义与复制操作,如果有多个线程同时操作age,则会造成资源的竞争,怎么办呢?C11中可以这样做

_Atomic int age; //通过_Atomic声明这个变量是一个“原子类型”

atomic_store(&age, 25); //通过这个宏函数来给原子变量赋值

这样保证了当一个线程在对一个原子类型的对象进行操作时,其他线程是不能够访问该原子对象的,防止了资源竞争

总结:

(1)C11中引入了原子类型修饰符_Atomic,在 stdatomic.h 头文件中,该头文件中定义了很多的宏函数来操作原子类型,当然适用的前提条件是编译器必须要支持这个C11新特性;

(2)当一个线程在对一个原子类型的对象进行操作时,其他线程是不能够访问该原子对象的,防止了资源竞争

 

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值