ANSI C (4) —— 存储类别和类型限定

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数值则是常量。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值