C语言:几个不常用(或者说可能出错的如const)的type qualifier

几个不常用(除const和volatile外)的type qualifiers,const和volatile应该算是最熟悉的了,但是其他的几个就不大容易在代码中发现,写下来顺便学习下

描述:这几个type qualifiers的使用范围是有限制的,const和volatile可以使用在任何数据类型上,而restrict只能使用在指针上(不过也看见过直接使用在变量上的),当将一个qualifier用在一个数组上的时候,这个qualifier修饰的是该数组中的每个元素,而不是这个数组本身!。

const
用const来修饰一个变量表示该变量的内容不可更改,用来保护变量。const的修饰作用对象的解释和例子:
const int x = 4 ;       /*x这个int类型的变量的值不可更改*/
const int *z;    /*z是一个指向const int类型的指针,所以z所指的对象的值不可更改,但是z这个指针本身可更改*/
int * const ptr;   /*一个const类型的指针ptr,它指向一个int类型的数据,这个指针ptr本身不能更改(不能一会儿指向B一会儿指向C,它只能指向它的初始化值所指向的地方),但是ptr所指向的内存区内的内容可以被更改*/
const int * const p;  /*指针p本身不能被更改,同时它所指向的对象也不能被更改*/

const 和volatile联合在一起使用(看起来是不是有点怪怪的??)。设想一种情况,进程间使用共享内存的方式通信,例如在这个共享内存区中存放一个int数据,在其中一个进程(process 1)中将这个int数据用const修饰(该进程只读这个int数据)而在另一个进程(process 2)中不加任何修饰(该进程读写该int数据),在这中情行下,process 1虽然声明这个int为const,但是也不能保证它的值不变(它只能保证它自己不去修改它),这时就可以将其在process 1中声明为volatile const int类型。

一个const指针可以被初始化指向一个非const数据,但是不能用这个指针来修改这个数据的值。

如果将一个const数据的地址赋值给一个非const的指针,然后再通过这个指针来修改该const数据的值将发生不可预知的事情。(这一点没有确认过,因为曾经看见过一个例子讲如何修改一个const数据的值,它使用的就是这个办法。)

volatile
volatile用来禁止编译器对变量引用的优化。该qualifier强制编译器为其所修饰的对象分配内存空间,并且每次访问该对象都从内存中读取。
volatile对象的地址可以被赋值给一个非volatile的指针,同样一个非volatile对象的地址也能够被赋值给一个volatile的指针。

结构体(struct)、共用体(union)和枚举类型(enumeration)的tag不包含const或者volatile修饰符,如:
volatile struct whale {
int weight;
char name[8];
} beluga;
struct whale blue;
在这几句代码中,beluga为volatile但是blue不是。


__unaligned

Use this data-type qualifier in pointer definitions to indicate to the compiler that the data pointed to is not
properly aligned on a correct address. (To be properly aligned, the address of an object must be a multiple of
the size of the type. For example, two-byte objects must be aligned on even addresses.)

When data is accessed through a pointer declared __unaligned , the compiler generates the additional
code necessary to copy or store the data without causing alignment errors. It is best to avoid use of
misaligned data altogether, but in some cases the usage may be justified by the need to access packed
structures, or by other considerations.

Here is an example of a typical use of __unaligned :

typedef enum {int_kind, float_kind, double_kind} kind;
void foo(void *ptr, kind k) {
    switch (k) {
    case int_kind:
        printf("%d", *(__unaligned int *)ptr);
        break;
    case float_kind:
        printf("%f", *(__unaligned float *)ptr);
        break;
    case double_kind:
        printf("%f", *(__unaligned double *)ptr);
        break;
    }
}


__restrict / restrict
这是从IBM的一个网站上看来的:
restrict只可以用在指针身上。如果一个指针被restrict修饰,那么就在它(这个指针)和它所指向的对象之间建立了一种特殊的联系──只能用这个指针或者这个指针的表达式来访问这个对象的值。
一个指针指向一个内存地址。同一块内存可以由多个指针来访问并在程序运行时修改它(这块内存)。restrict告诉编译器,如果一块由一个被restrict修饰的指针所指向的内存被修改了,那么没有其它的指针可以来访问这块内存。编译器可能会选择一种方式来优化代码中调用被restrict修饰的指针的部分,这可能导致错误发生。程序员有责任来确保正确地按照他们所设想的来使用被restrict修饰的指针,否则的话,可能会发生意想不到的结果。
如果一块特定的内存区没有被修改,那么它可以被多个restrict指针(被restrict修饰的指针)所指代(或者叫做引用或访问)。
另外,restrict指针的赋值是有限制的,这一点在函数调用和嵌套块(
nested block 之间是没有区别的。在包含restrict指针的块中,只能将外层的restrict指针的值赋给内层的restrict指针,在同层内不可以相互赋值,当然在外层不可能赋以内层的值。一个例外是,当一个声明restrict指针的代码快执行完后,这个restrict指针所指的内存就可以被其它的指针访问了(因为那个restrict是那个代码块的局部变量,当执行完那个代码块后,这个指针也就不复存在了)。


原文:
The restrict type qualifier may only be applied to a pointer. A pointer declaration that uses this type qualifier
 establishes a special association between the pointer and the object it accesses, making that pointer and
expressions based on that pointer, the only ways to directly or indirectly access the value of that object.

A pointer is the address of a location in memory. More than one pointer can access the same chunk of
memory and modify it during the course of a program. The restrict type qualifier is an indication to the
compiler that, if the memory addressed by the restrict-qualified pointer is modified, no other pointer will
access that same memory. The compiler may choose to optimize code involving restrict-qualified pointers in a
way that might otherwise result in incorrect behavior. It is the responsibility of the programmer to ensure that
restrict
-qualified pointers are used as they were intended to be used. Otherwise, undefined behavior may
result.

If a particular chunk of memory is not modified, it can be aliased through more than one restricted pointer.


The following example shows restricted pointers as parameters of foo(), and how an unmodified object can
be aliased through two restricted pointers.

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

Assignments between restricted pointers are limited, and no distinction is made between a function call and
an equivalent nested block.
{
    int * restrict x;
    int * restrict y;
    x = y; // undefined
    {
        int * restrict x1 = x; // okay
             int * restrict y1 = y; // okay
        x = y1; // undefined
    }
}

In nested blocks containing restricted pointers, only assignments of restricted pointers from outer to inner
blocks are allowed. The exception is when the block in which the restricted pointer is declared finishes
execution. At that point in the program, the value of the restricted pointer can be carried out of the block in
which it was declared.


还有几个如auto、register、static和extern等type qualifiers就太常见了,没有必要记录下来,应该非常熟悉才对。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值