C/C++定义与声明

来源:互联网 发布:淘宝联名精英版白金卡 编辑:程序博客网 时间:2024/04/30 16:11
《C++ Primer》中文第四版对变量的定义与声明做如下定义:1、变量的定义(definition)用于为变量分配存储空间,还可以为变量指定初始值。在一个程序中,变量有且仅有一次定义。2、声明(declaration)用于向程序表明变量的类型和名字。定义也是声明:当定义变量时我们声明了它的类型和名字;

(下面是来自:http://blog.csdn.net/binghehjbenben/article/details/7791224 关于声明与定义进行了更加详细的讨论)


C变量声明或定义时,需要类型说明符、存储类型、类型限定符。类型说明符就是,能够说明某个变量是什么类型的就是类型说明符,比如:int、double、char、指针、结构体、枚举等等,只要它能够说明变量是什么类型的就是类型说明符。
C存储类型说明符,包括extern,static,register,auto。默认的存储类型为auto。

             函数和全局变量具有外部连接,这意味着他们对构成程序的所有文件都是可用的.

            声明为Static的的文件域对象具有内部连接,只在声明他的文件中可用.

            局部变量没有连接, 因此只在声明他的块中可用.

            声明表述对象的名称和类型,定义则是导致为对象分配内存空间.

           Extern 的重要用途和多文件程序有关,C允许程序分散在两个或者多个文件中,分别编译然后再连接到一起.在这种情况下,必须以某中方式,将程序所需要的全局变量通知所有的文件.最简单的办法就是在一个文件中定义所有的全局变量,在另一个文件中用Extern声明. Extern 的作用是通知编译程序,告诉其后类型和名字的变量已经别的地方定义.换句话说Extern使编译程序了解变量的名字和类型同时又不再为他们分配内存.

           Static 的局部变量是函数或者文件中的永久变量.和全局变量不同 在函数或者文件外是看不到静态局部变量的.和全局变量相同的是他被分配固定的内存空间.这样在函数调用之间静态变量保持其值.在多次函数调用之间保持其值的局部变量.

           Static的全局变量另编译程序生成静态全局变量.使之只在定义该变量的文件中可见.虽然变量是全局的但是起他文件的程序无法感知他的存在,无法对他进行修改.

         小结: 局部的景泰变量名只在他们被声明的代码块中是认知的,全局的静态变量只在他们被声明的文件中可见,可修改的.

        Register任何类型的变量,在实践中 Register告诉编译程序把被他说明的     字符型和整数存放到CPU寄存器里面.而把其他的类型做适当的存储优化,提高这些数据的存取速度.    但是Register 的存储类型有一定的局限, 他只能修饰 局部变量和 函数的形参..
        
        auto、register、static、extern是属于存储类修饰符。在声明时,存储类修饰符最多只能使用一个,而且无法用在typeof声明中。

而类型限定符是指const、volatile、restrict。声明中可以使用多个类型限定符,顺序没有限制。另外,类型限制符可以用在typeof声明中。

(1)const
对象的类型如果有const限定符,该对象就是常量;在定义该对象之后,就无法修改它。
具体见下面的例子:
int main()
{
char a[100];
char c[100];
int j=100;
char * p;
const char *b = a;
const int i = j;
const int v[] = {1, 2, 3, 4, 5, 6};
int *k;
strcpy(a, "hello world!");
strcpy(c, "hello China!");
b = c; //可以修改b的值
/
printf("b=[%s]\n", b);
printf("i=[%d]\n", i);
printf("v[0]=[%d]\n", v[0]);

return 1;
}

(2)volatile
对象的类型如果有volatile限定符,就可能会被其他执行程序或事件所修改。volatile关键字告诉编译器在每次使用此对象的值时,都要重新读取,即使程序本身并没有修改它的值。

(3)restrict
restrict限定符只适用于对象指针类型。这是C99新增加的,用来告诉编译器,此指针所指向的对象如果被修改,就不可以被此指针以外的方式所存取,不管是直接地还是间接地。

C语言关键字__restrict在Cell上的应用

今天看到在一篇FFT在Cell上的优化文章中,提到关键字__restrict,说是它可以减少一些指令之间的dependency。以前还没有用过这个关键字,google了一下,有个理解比较好:
1. 首先我认为restrict只能修饰参数为指针类型的变量,否则没有意义。
2. 它只能用于修饰函数参数,否则也没有意义。
3. 编译器对它的实现是:(用上面那个函数举例,是个int *a加上int *b的函数)
把*a,*b都读入寄存器中(第一次访存),然后函数体内所有引用*a,*b的地方都使用寄存器里的值,所有对它们的修改也写入寄存器中,最后函数返回时,把寄存器中的值写回a,b指向的地址(第二次访存)。如果函数内部大量引用了a,b指向内存的值,这样的策略可以大大减小访存操作从而提高性能。当然,在x86机器上由于通用寄存器少,当寄存器溢出时,会为*a,*b都生成一个copy(这就是所谓的alias),这样反而会降低性能。但对于例如IA64这种有128个通用寄存器的机器来说,性能应该会大大提高。其实说了这么多,一句“进/出拷贝”就可以说明实质。
4.最后我认为restrict修饰集合类型的数据(例如struct)没有意义,因为对结构体的引用是多次访存,无法通过把值加载到寄存器中而避免多余的访存操作。

在Cell编程中,有些指针只是用来存储数据,某一个函数中总是访问某一个指针的不同区域,这样这个指针声明为__restrict是再好不过了,可以减少存取内存的操作,数据第一次载入寄存器后可以使用到函数退出才写入内存(如果寄存器足够容纳这些数据)。

C99 中新增加了 restrict 修饰的指针: 由 restrict 修饰的指针是最初唯一对指针所指向的对象进行存取的方法,仅当第二个指针基于第一个时,才能对对象进行存取。对对象的存取都限定于基于由 restrict 修饰的指针表达式中。

由 restrict 修饰的指针主要用于函数形参,或指向由 malloc() 分配的内存空间。restrict 数据类型不改变程序的语义。 编译器能通过作出 restrict 修饰的指针是存取对象的唯一方法的假设,更好地优化某些类型的例程。

[典型例子] memcpy() 在 C99 中,restrict 可明确用于 memcpy() 的原型,而在 C89 中必须进行解释。 void *memcpy(void *restrict str1, const void *restrict str2, size_t size);

指针在声明的时候可以用关键字restrict修饰,如
int *restrict p;

将告诉编译器,指针p是访问p所指对象的唯一方式