C语言中变量属性

--事物的难度远远低于对事物的恐惧! 

        这章来聊聊变量的属性,玩过游戏的朋友都知道,游戏里各个角色都有各自特定的属性,才有了各个角色的功力不同,这个只能打小妖而那个却能战大怪,这就因为它们具有不同的属性。

        而在C语言中,变量也可以有自己的属性,属性不同,变量的在程序中具有的特性也不同。那么如何给变量加上自己的属性?很简单:在定义变量的时候加上"属性"关键字,这个"属性"关键字就指明了变量的特殊意义,语法如下:

        proterty(属性关键字)  type(类型名) var_name(变量名);

        所以我们就可以这样给变量加上属性

        auto int i;        //i的属性为auto

        register int j;    //i的属性为register

        static long l;    //i的属性为static

        extern double d;    //i的属性为extern

C语言中有那么多的属性关键字,它们分别使得变量具有什么特别的意义?下边我们来逐一分析分析:

auto属性关键字:

        -auto是C语言中局部变量的默认属性(注意是局部变量)

        -auto属性,表明将其修饰的变量保存于栈上(想想上一点说的局部变量,存在哪里?)

        -编译器默认所有的局部变量的属性都为auto(auto英文的意思,就是自动,所以属性名还是起的很贴心的)

        基于以上三点,我们就知道,下边代码中的两个变量i跟j的属性,是一致的:

void fun(void)
{
    int i;		//编译器默认变量i的属性为auto
    auto int j;	//显示指定变量j的属性为auto,实则与变量i具有一样的属性
}

register属性关键字:

        -register关键字指明将局部变量尽量存储于寄存器中(注意是局部变量,因为全局变量的生命周期为整个程序的生命周期,假如全局变量能声明为register变量,那么就会一直占用着这个寄存器,而cpu的寄存器就那么点,所以干脆就不允许声明全局变量为register变量)

        -register只是请求寄存器变量,但不一定请求成功(这点很容易理解,一个CPU就那么点寄存器,怎么可能将所有的变量都声明为寄存器变量)

        -register变量必须是cpu寄存器可以接受的值(好吧,这好像是废话。。。)

        -不能用&取址符获取register变量的地址(我们日常所说的取变量的地址,是哪里的地址?肯定是变量在内存里的地址嘛,寄存器又不是内存,怎么能取址?) 

来看下边这个例子

#include <stdio.h>

register int i;	//error,i为全局变量,不能用register关键字修饰

int main()
{
    register int j = 10;

    printf("0x%d08X\n", &j); //error,不能对register变量取址	
    return 0;
}

既然register有那么多的使用限制,那么register属性关键字到底有什么用?

答案是:效率!效率!效率!因为CPU是直接读取寄存器的,所以将变量放在寄存器中,CPU的操作速度会相当快,而要操作内存中的变量,那么就会先把内存的中变量放到寄存器中,CPU再从寄存器中读取,内存到CPU中间会有一个桥梁过度。

static关键字:

    -static关键字修饰的变量具有"静态"属性

        -static修饰的局部变量存储在程序静态区(即原来存储在栈中,转换到存储到静态区中),生命周期与全局变量一样。

    -static关键字也具有"作用域限定符"的意义

        -被static修饰的全局变量或函数,它们的作用域仅仅是在声明的文件中

    下边来个例子感受一下static关键字:

#include <stdio.h>

int g_v;	//全局变量,程序的任意地方均能访问
static int g_vs;	//静态全局变量,仅仅能在当前声明的文件中访问

int main()
{
    int var;	//局部变量,栈上分配空间
    static int svar;	//静态局部变量,静态数据区分配空间,生命周期与全局变量一致
	
    return 0;
}

对于以上说的auto、register、static三个关键字,下边用一个程序来做个对比:

#include <stdio.h>

int f1()
{
    int r = 0;
    r++;
    return r;
}

int f2()
{
    static int r = 0;
    r++;
    return r;
}

void fun(void)
{
    int i;		//编译器默认变量i的属性为auto
    auto int j;	//显示指定变量j的属性为auto,实则与变量i具有一样的属性
}


int main()
{
    auto int i = 0;       // 显示声明 auto 属性,i 为栈变量
    static int k = 0;     // 局部变量 k 的存储区位于静态区,作用域位于 main 中
    register int j = 0;   // 向编译器申请将 j 存储于寄存器中

    printf("%p\n", &i);
    printf("%p\n", &k);
    //printf("%p\n", &j);   // error,不能取寄存器变量的地址

    for(i=0; i<5; i++)
    {
    	printf("f1():%d\n", f1());	//5次的输出结果分别是多少?
    }

    for(i=0; i<5; i++)
    {
	printf("f2():%d\n", f2());	//这个呢?5次输出又是多少?
    }

    return 0;
}

输出结果如下:

我们来分析下输出结果

    首先看变量i与变量k的地址值,程序中我们定义的两个变量是相邻的,而从输出的地址值可以很明显看到,二者在内存中存储的位置绝对不是相邻或相近的,为什么?因为我们把i定义成普通局部变量,k定义为静态局部变量,二者在内存中的存储位置是不一样的,所以输出的地址值,肯定不会相邻的,这样问题就很明显了。

    这么说也许还不够,那我们再来做验证,把修饰变量k的static关键字去掉,让k与i具有一样的属性,均为普通局部变量,再来看看二者的地址输出值(代码修改很简单,这里就直接贴上输出结果),结果已经很明显,i跟k在内存中的位置,已经是相邻近了。

现在我们来分析两个for语句的输出,也很简单,因为f1()中的变量r是普通局部变量,作用域也仅仅在f1()函数中,所以每次循环f1()函数执行完,其里边的变量r就消亡了,下次循环,r就重新定义,所以第一个for循环每次输出的值,都是重新定义的;再来看看第二个for循环,因为f2()中的变量r定义为static静态变量,所以生命周期与全局变量一样,为整个程序的生命周期,这样每次循环,就会在上一次循环的基础上递增,所以就会有我们的输出。

extern关键字:

1. extern关键字用于声明"外部"定义的变量和函数

    -extern变量在文件的其他地方分配空间

    -extern函数在文件的其他地方定义

2. extern用于"告诉"编译器用C的方式编译

    -C++编译器和一些类C编译器默认会以自己的方式编译函数和变量,通过extern关键字可以命令编译器"以标准C方式进行编译",下边这段代码,就会以标准C的方式进行编译

extern "C"
{
    void fun()
    {
	//your code...
    }
};

下边用代码来演示,extern关键字如何使用,我们定义两个文件,main.c与test.c内容分别为

//main.c文件
#include <stdio.h>

extern int i;
extern int getI();

int main()
{
    printf("i = %d\n", i);
    printf("getI() = %d\n", getI());
    return 0;
}
//test.c文件
int i = 10;

int getI()
{
    return i;
}

 

输出:

 

很明显,当编译器编译到main.c中的i变量时,发现i变量使用extern关键字声明,这时编译器就知道,i变量是在其他地方定义了,于是编译器就到工程中的其他地方找,于是在test.c中找到了,同理函数int getI()也一样。

 

思考个问题:假如把test.c中的int i = 10,变为static int i = 10; 或者int getI()变为static int getI()程序还能编译通过吗?为什么?

总结:

1、auto变量存储在程序的栈中,是默认属性

2、static变量存储在程序的静态数据区中

3、register变量请求存储于CPU的寄存器中,请求不一定成功

4、extern变量在文件的其他地方分配空间

5、extern能够指示编译器按照标准C的方式编译程序

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值