通常用类型存储类别来描述一个变量。

C90还增加了两个属性:恒常性(constancy)、易变性(volatility);

分别用关键字const和volatile来声明。

这两个关键字创建的类型是限定类型(qualified type)。

C99标准新增了第3个限定符:restrict,用于提高编译器优化。

C11标准新增了第4个限定符:_Atomic

C11标准提供了一个可选库,由stdatomic.h管理,以支持并发程序设计,而且_Atomic是可选支持项。

C99为类型限定符增加了一个新属性:它们现在是幂等的(idempotent)。可以在一条声明中多次使用同一个限定符,多余的限定符将被忽略。

12.5.1 const类型限定符

以const声明的对象,其值不能通过赋值或递增、递减来修改。

可以初始化const变量,const声明让该变量成为只读变量。初始化后不能改变它的值。

const的使用:(指针和形参中)

声明普通变量和数组时使用const关键字很简单。

指针复杂一些,因为要区分限定指针本身为const还是限定指针指向的值为const

const float * pf;   //pf 指向一个float类型的const值。

float * const pt;   // pt  是一个const指针。创建的指针pt本身的值不能更改。pt必须指向同一个地址,但是它指向的值可以改变。

const float *const ptr;  //表明ptr既不能指向别处,它指向的值也不能改变;

float const * pfc  // 把const放在类型名之后, *之前,说明该指针不能用于改变它所指向的值。简而言之,const放在*左侧任意位置,限定了指针指向的数据不能改变;const放在*的右侧,限定了指针本身不能改变。

const关键字常见的用法:声明为函数形参的指针。

例如:display()显示一个数组的内容,要把数组名作为实际参数传递给该函数,但是数组名是一个地址。该函数可能会更改主调函数中的数组,但是下列的原型保证了数据不会被更改:

void display(const tint array[], int limit);

cons tint array[] 与 cons tint * array相同,所以该声明表明不能更改array指向的数据。

总结:如果一个指针仅用于给函数访问值,应将其声明为一个指向const限定类型的指针。如果要用指针更改主调函数中的数据,就不使用const关键字。

使用全局变量是一种冒险的方法,因为这样做暴露了数据,程序的任何部分都能更改数据。把数据设置为const,就可以避免这样的危险。用const限定符声明全局变量很合理。可以创建const变量,const数组和const结构。(结构是一种复合数据类型)

方案一:遵循外部变量的常用规则,即在一个文件中使用定义式声明,在其他文件中使用引用式声明(extern关键字)。

方案二:把const变量放在一个头文件中,然后在其他文件中包含该头文件。

使用方案二必须在头文件中用关键字static声明全局变量。如果去掉static,那么在两个文件中同时包含constant.h将导致每个文件中都有一个相同标识符的定义式声明。C标准不允许这样做。

头文件方案的好处是:方便偷懒,不用惦记着在一个文件中使用定义式声明。在其他文件中使用引用式声明。缺点就是:数据是重复的,如果const数据包含庞大的数组,就不能视而不见了。

12.5.2 volatile类型限定符

volatile限定符告知计算机,代理(而不是变量所在的程序)可以改变该变量的值。通常,它被用于硬件地址及在其他程序或同时运行的线程中共享数据

volatile涉及到编译器的优化,例如:

val1 = x;

val2 =x;

智能的(优化的)编译器会注意到以上代码使用了两次x。但并未改变它的值。于是编译器把x的值临时储存在寄存器中,然后在val2需要使用x时,才从寄存器中读取x的值,以节约时间。这个过程被称为高速缓存(caching)。高速缓存是不错的优化方案,但是如果一些其他代理在以上两条语句之间改变了x的值,就不能这样优化了。如果没有volatile关键字,编译器就不会知道这种事情是否发生。因此,为安全起见,编译器不会进行高速缓存。如果声明中没有volatile关键字,编译器会假定变量的值在使用过程中不变,然后再尝试优化代码。

同时使用const和volatile限定一个值,例如:通常const把硬件时钟设置为程序不能更改的变量,但是可以通过代理改变,这时用voltatile。声明中同时使用两个限定符,它们的顺序不重要。

12.5.3 restric 类型限定符

restrict允许编译器优化某部分代码以更好地支持计算。它只能用于指针,表明该指针是访问数据对象的唯一且初始的方式。

以下代码举例:

int ar[10];

int * restrict restar = (int *) malloc(10*sizeof(int));

int * par =ar;

说明:restar是访问由malloc所分配内存的唯一且初始的方式。可以用restrict关键字限定它。而指针par既不是访问ar数组中数据的初始方式,也不是唯一方式。所以不用把它设置为restrict。

如果未使用,restrict关键字,编译器就必须假设最坏的情况(即,在两次使用指针之间,其他的标识符可能已经改变了数据)。如果使用了restrict关键字,编译器就可以选择捷径优化计算。

restrict限定符还可用于函数形参中的指针。意味着编译器可以假定函数体内其他标识符不会修改该指针指向的数据。而且编译器可以尝试对其优化,使其不做别的用途。

restrict关键字有两个读者:1、编译器,该关键字告知编译器可以自由假定一些优化方案。2、关键字告知用户要使用满足restrict要求的参数。总而言之,编译器不会检查用户是否遵循这一限制,但是无视它后果自负。

12.5.4 _Atomic 类型限定符C11

并发程序设计把程序执行分成可以同时执行的多个线程。这给程序设计带来了新的挑战,包括如何管理访问相同数据的不同线程。

stdatomic.h和threads.h提供了一些可选的管理方法。

要通过各种宏函数来访问原子类型。当一个线程对一个原子类型的对象执行原子操作时,其他线程不能访问该对象。

Int hogs;  //普通声明

Hogs =12;  //普通赋值

可以替换成:

_Atomic int hogs;        //hogs是一个原子类型的变量

atomic_store(&hogs,12);  //stdatomic.h中的宏

hog储存12是一个原子过程,其他线程不能访问hogs。

编写这样代码的前提是,编译器要支持这一新特性。

12.5.5 旧关键字的新位置

C99允许把类型限定符和存储类别说明符static放在函数原型和函数头的形式参数的初始方括号中。

void ofmouth(int * const a1, int * restrict a2, int n);

新的等价语法如下:

void ofmouh(int a1[const], int a2[restrict], int n);

C语言中类型限定符的更多相关文章

  1. CUDA1.1-函数类型限定符与变量类型限定符

    这部分来自于<CUDA_C_Programming_Guide.pdf>,看完<GPU高性能变成CUDA实战>的第四章,觉得这本书还是很好的,是一种循序渐进式的书,值得看,而不 ...

  2. GPU编程自学6 —— 函数与变量类型限定符

    深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题.这里主要记录自己的GPU自学历程. 目录 <GPU编程自学1 -- 引言> <GPU编程自学2 -- CUD ...

  3. C 类型限定符

    C 类型限定符 1. Introduction C 语言中的大部分类型都可以用称为限定符(qualifier)的关键字 const. volatile. restrict. _Atomic 加以限定. ...

  4. ISO/IEC 9899:2011 条款6.7.3——类型限定符

    6.7.3 类型限定符 语法 1.type-qualifier: const restrict volatile _Atomic 约束 2.除了指针类型(其被引用的类型是一个对象类型)之外的类型,不应 ...

  5. 类型限定符volatile

    目录 类型限定符volatile 强制内存读取 禁止编译优化 注意:volatile不能够保证线程同步 volatile bool flag; volatile int a; 添加volatile限定 ...

  6. JAVA语言中的修饰符

    JAVA语言中的修饰符 -----------------------------------------------01--------------------------------------- ...

  7. Java C# C语言中的占位符

    一般拼接一段字符串在编程中是很常见的事,下面简单做个总结: 什么是占位符?占位符就是先占住一个固定的位置,等着你再往里面添加内容的符号. 1.Java中处理方法: package com.amos; ...

  8. C#语言中的修饰符

    public:公有访问.不受任何限制. private:私有访问.只限于本类成员访问,子类和实例都不能访问. protected:保护访问.只限于本类和子类访问,实例不能访问. internal:内部 ...

  9. 关于Java语言中那些修饰符

    一.在java中提供的一些修饰符,这些修饰符可以修饰类.变量和方法,在java中常见的修饰符有:abstract(抽象的).static(静态的).public(公共的).protected(受保护的 ...

随机推荐

  1. eclipse安卓模拟器Failed to install on device 'emulator-5554': timeout处理方案

    我们在用模拟器调试的时候,经常会出现Failed to install on device 'emulator-5554': timeout这个错误.其实就是有些虚拟器在部署的时候时间过于长.系统就认 ...

  2. springmvc 中异常处理

    springmvc 中异常处理常见三种处理方式: 1:SimpleMappingExceptionResolver处理的是处理器方法里面出现的异常 2 3.自定义异常处理器:处理的是处理器方法里面出现 ...

  3. MMSeg中文分词算法

    Java中有一些开源的分词项目,比如:IK.Paoding.MMSEG4J等等.这里主要说的是MMSEG4J中使用的MMSeg算法.它的原文介绍在:http://technology.chtsai.o ...

  4. C++——static

    1.第一条也是最重要的一条:隐藏.(static函数,static变量均可) 所有未加static前缀的全局变量和函数都具有全局可见性:加static前缀的全局变量和函数只有有局部可见性: //a.c ...

  5. C++面向对象类的实例题目六

    问题描述: 编写一个程序计算两个给定长方形的面积,其中在设计类成员函数addarea()(用于计算两个长方形的总面积)时使用对象作为参数. 程序代码: #include<iostream> ...

  6. 【转】nginx+memcached构建页面缓存应用

    如需转载请注明出处: http://www.ttlsa.com/html/2418.html nginx的memcached_module模块可以直接从memcached服务器中读取内容后输出,后续的 ...

  7. Excel打开图片

    =HYPERLINK("D:\固定资产图片\"&C2&".jpg",C2)

  8. C#和.NET Framework简介

    注:本文大部分借鉴了<果壳中的C#5.0权威指南>,小编也想根据这本书好好梳理一下C#. 序言:C#是一种通用的类型安全且面向对象的编程语言.这种语言的目标是提高程序员的生产力,为此,需要 ...

  9. 自动化打包资源混淆集成python实践----打包

    1.自动化打包方案 1)友盟多渠道多渠道打包 2)gradle productFlavors系统的条件编译 3)美团打包 4)APK文件注释写入渠道号 2.各打包方案简介 1)友盟多渠道多渠道打包(w ...

  10. 海量推荐系统:mapreduce的方法

    1. Motivation 2. MapReduce MapReduce是一种数据密集型并行计算框架. 待处理数据以"块"为单位存储在集群机器文件系统中(HDFS),并以(key, ...