ANSI C (4) —— 存储类别和类型限定
2016-12-06 18:26
106 查看
register
寄存器(register)变量的被访问速率远远高于内存的被访问速率,所以编译优化常常这样做:将循环控制变量和使用频繁的变量安排在CPU的寄存器中。通常,仅仅在块内声明寄存器变量。
取地址符&不能用于寄存器变量。
比如下面的代码将会发生错误: error: address of register variable ‘i’ requested
register int i; for(i=0;i<10000;i++){ if(i == 100){ printf("this is 100.\n"); printf("%p\n",&i); } }
auto
auto修饰的变量在离开自身的作用域后其占用的内存自动释放。事实上,大多数情况下我们用的变量都是auto类型的。如果编译器不能或者不愿将程序员声明的register变量进行寄存处理,那么变量类型默认是auto类型的。
auto变量必须定义在函数体内
int sum(int a,int b){ auto int s=a+b; //仅仅在函数内可见 return s; } auto int a=12; //error: file-scope declaration of 'a' specifies 'auto'
extern
extern的用法 —— C++说明
extern告诉编译器,其声明的函数和变量的定义在别的文件中。如果定义时没有初始化,系统在为其分配存储空间时,一次性初始化成0.
与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。
对于函数和变量而言,我们如果在本模块中extern声明了某一个函数或变量,那么编译器将会从其他文件中寻找这个函数或变量的定义,我们不用加上含有这个函数声明的.h文件。
extern.cpp:
#include <iostream> #include <stdio.h> using namespace std; int main() { extern int get(); cout<<get()<<endl; extern int a[5]; int i; for(i=0;i<5;i++){ cout<<a[i]<<endl; } return 0; }
head.cpp
int get(){ return 23; } int a[5] = {1,2,3,4,5};
extern.cpp和head.cpp在同一文件夹中,这份代码是可以正常运行的。
当然,我们也可以将extern.cpp的中引用放在头文件中,直接”#include”即可
head.h
#ifndef HEAD_H_ #define HEAD_H_ extern int get(); extern int a[5]; #endif
extern.cpp:
#include <iostream> #include <stdio.h> #include "head.h" using namespace std; int main() { cout<<get()<<endl; int i; for(i=0;i<5;i++){ cout<<a[i]<<endl; } return 0; }
C++和C的混编
C++ 可以看做是C的一种扩展,C与C++ 的混合编译也是很自然的事情。二者的区别仅在于编译后函数的名字不同。C简单地使用函数名,而C++ 编译后的函数名则总是将参数类型列表作为其一部分。C++ 提供了特殊的机制来声明 C 函数。C++调用C函数
代码:
chello.c:
#include <stdio.h> #include <stdlib.h> void print_hello(){ printf("hello!\n"); }
temp.cpp
#include <iostream> using namespace std; extern "C" void print_hello(); int main(){ print_hello(); return 0; }
编译执行:
[edemon@CentOS workspace]$ gcc -c chello.c -o chello [edemon@CentOS workspace]$ g++ -c temp.cpp -o temp [edemon@CentOS workspace]$ gcc temp chello -lstdc++ -o exe [edemon@CentOS workspace]$ ./exe hello!
C调用C++ 的函数
在C++ 中创建可被C调用的函数,仍然需要extern “C”。不过函数体可用C++完成。
例子:
ctemp.c
#include <stdio.h> #include <stdlib.h> int main(){ print_hello(); return 0; }
cpphello.cpp
#include <iostream> extern "C" void print_hello(); void print_hello(){ std::cout<<"hello!"<<std::endl; }
编译执行
[edemon@CentOS workspace]$ gcc -c ctemp.c -o ctemp [edemon@CentOS workspace]$ g++ -c cpphello.cpp -o cpphello [edemon@CentOS workspace]$ gcc cpphello ctemp -lstdc++ -o temp [edemon@CentOS workspace]$ ./temp hello!
volatile
volatile告诉编译器,变量的值可能被改变,不要做编译优化。比如下面的代码
volatile int p = 23; cout<<p*p<<endl;
volatile 告诉编译器p是随时可能发生变化的,每次使用它的时候必须从p的地址中读取,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。上面的代码就有可能出现这样的情况:
cout<<p*p<<endl;中的第一个p是23,在多线程复杂环境中,第二个p就很可能不是23了。
一个经典的函数:
int square(volatile int *ptr) { return *ptr * *ptr; }
事实上我们期望它返回这样的数值:
int square(volatile int *ptr) { int p = *ptr; return p * p; }
但是编译器给我们返回了这样的数值:
int square(volatile int *ptr) { int p1 = *ptr; int p2 = *ptr; return p1 * p2; }
static
static全局变量static全局变量即静态全局变量,它只在定义它的源文件内有效,其他源文件不能访问。存储于全局存储区(初始化的或未初始化的)。
static局部变量
static局部变量同样存储在全局存储区(初始化)。函数调用静态局部变量的时候修改变量后离开,下次读的时候从全局存储区读出的静态局部变量就是上次修改后的值。
程序执行前,就分配存储空间,如果定义时没有初始化,那么系统赋予0,且仅仅初始化一次。
C++ primer中也提到过“静态局部变量保存在全局数据区,而不是保存在栈中”
他的存储空间和内容是不会因函数的结束而消失,但是他仅仅在函数体内是可见的。
int count(){ static int s=0; return s++; } int main() { int i; for(i=0;i<7;i++){ printf("%d ",count()); } return 0; } /* 0 1 2 3 4 5 6 */
const
被const修饰的变量是只读的,不能被人为修改,即已定义的const变量不能作为左值。一个const变量必须在定义的时候就被初始化。const和指针的关系:
按照const在
data type *的前后分为:
const修饰的是数据类型,则为常量指针。(const char* p)
const char *p = "hello world"; *p = 'p'; //error: assignment of read-only location ‘*p’ p = "p"; // it's ok.
const修饰的是指针本身,那么则为指针常量。(char* const p)
char * const p = "hello world"; *p = 'p'; //it's ok p = "p"; // error: assignment of read-only variable ‘p’
由此,我们得到启发,可以通过上面的方法,改变
const data *pointer或者
data * const pointer
ANSI C说,const修饰的变量是不能作为数组的长度,结果我本地出现了神奇的事情:
temp2.c:
#include <stdio.h> #include <stdlib.h> const int len = 12; int main(){ float num[len]; return 0; }
man gcc:
-ansi In C mode, this is equivalent to -std=c89. In C++ mode, it is equivalent to -std=c++98.
[edemon@CentOS workspace]$ gcc -std=c89 -o temp2 temp2.c [edemon@CentOS workspace]$ ./tmep2
这是怎么啦? -_-||
另外,const变量不能作为switch,case后的常量。
#include <stdio.h> #include <stdlib.h> int main(){ const int a = 1; int b = 2;; switch (b){ case a: printf("this is a = 1\n"); break; default:break; } return 0; }
编译:
[edemon@CentOS workspace]$ gcc temp3.c temp3.c: In function ‘main’: temp3.c:8:3: error: case label does not reduce to an integer constant case a: ^
但是在c++中这是可以的。
#include <stdio.h> #include <stdlib.h> using namespace std; int main(){ const int a = 1; int b = 2;; switch (b){ case a: printf("this is a = 1\n"); break; default:break; } return 0; }
编译
[edemon@CentOS workspace]$ g++ temp3.cpp
这是因为C中的const不是真正的静态,而c++中const数值则是常量。
相关文章推荐
- 存储类别和类型限定词
- C++存储类别、类型修饰符、输入限定符
- ANSI C的三个类型限定词:const,volatile,restrict
- 变量存储说明符,限定符,类型转换
- C存储类型和类型限定符
- C存储类型和类型限定符
- C Primer Plus 第12章 12.7 ANSI C的类型限定词
- linux C编程(三)ANSI C中的类型限定词(const/volatile/restrict)
- ANSI C的类型限定词
- Oracle基本数据类型存储格式浅析(一)—(五)
- 各种类型文件在SQL Server中存储的解决方案
- 将所有的表中,数值类型由char,varchar改为nchar,nvarchar 的存储过程
- Oracle基本数据类型存储格式浅析
- 存储过程传递参数时出现类型转换错误!如:varchar转换为int时出错
- 在C#中存储Blob类型的数据,
- Java中使用Hibernate存储Date类型及Boolean类型到Orcale数据库中的心得
- oracle不同数据类型存储空间的实例比较
- 遍历数据库中的用户存储过程,获得每个存储过程的参数名、参数类型、存储过程名称等信息以xml的形式保存
- 关于datetime类型时间存储
- Oracle基本数据类型存储格式浅析(一)——字符类型