一.变量的作用域和存储方式

在C语言中每个变量都有两种基本属性:数据类型、数据的存储类别。

数据类型很多人都已熟知,例如:字符型(char)、整型(int)、浮点型(float)等等。存储类别是指数据在内存中的存储方式,C语言中的存储类别共有4种:自动的(auto)、静态的(static)、寄存器的(register)、外部的(extern)。根据变量的存储类别,可以知道变量的作用域和生存期。

1.自动变量。关键字为auto,但实际上“auto”通常都可以省略,函数中的局部变量如果不用关键字static来定义则都默认为自动类型(auto)的变量,在调用函数时系统会将这些变量分配到内存的动态存储区中,函数调用结束后这些存储空间将被释放。我们平常使用的函数中未用static定义的局部变量都是auto类型。

2.静态变量。关键字为static。静态变量包括全局变量和静态局部变量。全局变量在变量申请时自动存储到内存的静态存储区,即自动申请为静态变量。若希望函数中局部变量的值在函数调用结束后不消失,则需要用关键字static将该局部变量声明为静态局部变量,这些变量也被存储在内存的静态存储区。加上关键字static修饰后,原来的局部变量就类似于全局变量,它的值在下次调用该函数之前不会发生改变;与全局变量不同的是,全局变量的值可以在程序中的任何部分进行修改,而静态局部变量在程序中的其他部分是不可见的。为了说明关键字static的作用,举例如下:

#include<stdio.h>

void add()

{

  static int n=0;//申请静态变量n,对其赋初值操作仅仅在编译函数时进行一次

  printf("n = %d\n",n);

  n++;

}

int main(void)

{

  add();

  add();

  add();

}

运行结果:

n = 0

n = 1

n = 2

通过以上讨论,得出理解静态局部变量的几个要点:

(1)静态局部变量存储在内存的静态存储区。

(2)对静态局部变量赋初值是在编译时进行的,即仅赋值一次。如果定义静态局部变量时不赋初值,则编译器将自动对其赋初值0(对数值型变量)或空字符(对字符型变量)。

(3)静态局部变量的值在函数调用结束后不发生改变,但在程序中的其他部分是不可见的。

静态局部变量的缺点:

(1)需要多占用系统的内存。

(2)降低程序的可读性。当调用函数次数较多时,可能会分不清静态局部变量的当前值是什么。

因此,在非必要场合应减少使用静态局部变量。

3.寄存器变量。关键字register。变量的值一般是存储在计算机的内存中的,但对于一些频繁调用的变量,为了减少变量的调用时间可将其存在CPU的寄存器中,定义这样的变量需要使用关键字register。需要说明的是,现在优化的编译器能够识别使用频繁的变量,从而自动将其放入CPU的寄存器中,因此需要程序员使用register关键字来定义寄存器变量的场合较少。

4.外部变量。外部变量是在函数外部定义的全局变量。关键字“extern”主要用来扩展外部变量的作用域。通常来说,外部变量的生存周期为程序的整个运行过程,它的作用域从定义该变量处到本程序文件的末尾,在此范围内该变量都是可见的。但是如果需要扩展外部变量的作用范围,就需要用到关键字extern。有以下两种扩展外部变量的情况:

(1)在一个文件内扩展外部变量的作用域。外部变量的作用域从定义该变量处到本程序文件的末尾,如果需要在定义之前使用该外部变量,应在使用之前用extern对该变量进行外部变量声明。例如:

#include<stdio.h>

extern int a;//外部变量声明

void output()

{

  printf("a=%d\n",a);

}

int a =3;

int main(void)

{

  output();

  return 0;

}

如果程序中没有声明语句“extern int a;”则编译时会报错,因为变量output()函数中的变量a未定义就使用。

(2)将外部变量的作用域扩展到其他文件。如果在本文件中定义的变量希望能用于其他文件,则可以在需要用到该变量的文件开头加入外部变量声明。例如

//文件auto1.c

#include<stdio.h>

extern int a;

void output()

{

  printf("a=%d\n",a);

}

int main(void)

{

  output();

  return 0;

}

//文件auto2.c

int a = 1000;

文件auto1.c中的声明语句“extern int a;”是告诉编译器:int型变量已经在其他文件中定义。

关键字“static”可用来限定外部变量的使用范围。如果某些外部变量只希望在本文件中使用,禁止在该文件以外的地方引用,可以在定义时在该变量前加关键字static,在前面的例子中如果将文件auto2.c改为:

//文件auto2.c

static int a = 1000;

则编译时会出错,因为文件auto1.c中的a成了未定义的变量。

小结:static声明一个变量的作用是(1)局部变量用static声明,该变量将被存入静态存储区,该变量在程序执行期间不释放。(2)全局变量用static声明,该变量的作用域被限定在本文件,即用static声明的全局变量仅在当前文件可见。

二.函数的作用域

C语言中的函数根据能否被其他源文件调用分为内部函数和外部函数。

只能被本文件中其他函数调用的函数称为内部函数,内部函数也称为静态函数,用关键字static来声明。例如:

static int fun(int a);

声明了一个内部函数fun(int a),该函数只能在本文件中可见。通常把只能由本文件使用的函数和外部变量放在本文件的开头用static声明,可以防止其他文件误调用,提高程序可靠性。这在多人合作编写的大型程序中非常有用。

可供其他文件调用的函数称为外部函数,用关键字extern来声明。例如

extern int fun(int a)

 {  

  …

 }

定义了一个外部函数。c语言中的函数默认为外部函数,因此函数定义时省略关键字extern则默认为外部函数。应注意的是:调用已经在其他文件中定义过的外部函数时,当前文件的开头需要加上被调用函数的extern声明,旨在告诉编译器:该函数已经在其他文件中定义过了。例如:

//文件extern1.c

#include <stdio.h>

extern int a ;

extern int sum(int x,int y);

int main(void)

{

  a = sum(2,3);

  printf("The sum is %d",a);

}

//文件extern2.c

int a = 8;

int sum(int x,int y)

{

  return (x+y);

}

文件extern1.c中“extern int sum(int x,int y);”语句声明sum()函数是已经在其他文件中定义过的函数,若没有该语句,编译器将报错:sum函数未定义就使用。

三.static、extern、auto的用法小结

   关键字static和关键字extern在声明函数和全局变量时有某种“互补性”。

   用关键字static声明定义的函数和全局变量作用域仅在当前文件,对其他文件不可见,不能被本文件以外的文件引用。函数和全局变量使用static声明,可以防止误操作。

用关键字extern声明定义的函数和全局变量可以在本文件以外的使用,但使用时必须在使用文件的前面加上该函数或全局变量的extern声明。另外,默认的函数、全局变量均为extern类型,故定义其他文件可以使用的函数和全局变量时可省略extern声明。

   关键字static和关键字auto在声明定义局部变量时有某种“互补性”。

   用static声明定义的局部变量将被存入静态存储区,该变量在程序执行期间不释放,这种用法比较少见。

用auto声明定义的局部变量将被存入动态存储区,每次函数调用结束后该内存空间就被释放,下次使用该变量时需要重新申请内存空间,重新赋初值,常见的局部变量均属这种类型,关键字auto可省略。

参考文献:《C语言程序设计》 第2版   谭浩强  清华大学出版社

C语言变量、函数的作用域及变量的存储方式的更多相关文章

  1. JavaScript基础03——函数的作用域及变量提升

    1.作用域 作用域,变量在函数内部作用的范围/区域.有函数的地方就有作用域.   2.局部作用域和全局作用域 function fn(){ var a = 1; } console.log(a); / ...

  2. go语言基础之不同作用域同名变量

    1.不同作用域同名变量 示例: package main import "fmt" var a byte //全局变量 func main() { var a int //局部变量 ...

  3. C变量类型和作用域

    C语言中所有变量都有自己的作用域,申明变量的类型不同,其作用域也不同.C语言中的变量,按照作用域的范围可分为两种, 即局部变量和全局变量. 一.局部变量 局部变量也称为内部变量.局部变量是在函数内作定 ...

  4. php基础知识【函数】(8)xml和变量函数

     一.XML函数 参数类型 data    --string,需要解析的数据集. parser  --resource,一个指向要取得字节索引的 XML 解析器的引用.  1.创建和释放XMl解析器 ...

  5. 分享:Oracle 系统变量函数用法说明

    在Oracle数据库中,Oracle系统变量函数是经常会使用到的函数,分享下Oracle系统变量函数的用法. Oracle函数多种多样,系统变量函数就是其中之一,介绍三种最常见的系统变量函数. Ora ...

  6. javascript 函数和作用域(函数,this)(六)

    重点. 一.函数 1.函数介绍 函数是一块JavaScript代码,被定义一次,但可执行和调用多次.JS中的函数也是对象,所以JS函数可以像其他对象那样操作和传递,所以我们也常叫JS中的函数为函数对象 ...

  7. c++变量的存储方式

    1.名字的作用域 作用域是从空间的角度来分析的,c++的作用域以花括号分隔,定于于所有{ }以外的名字具有全局作用域,定义于{ }以内的名字具有块作用域 2.变量的生命周期 生命周期是从变量存在的时间 ...

  8. C语言中变量和函数的作用域和链接属性

    C语言中变量和函数的作用域和链接属性 作用域 代码块作用域: 代码块指的是使用"{}"包围起来的部分. 在代码块中定义的变量,代码块之外是不能访问的. 代码块嵌套之后的变量作用域, ...

  9. JavaScript变量的作用域和函数的作用域的区别

    变量作用域和函数作用域都涉及到变量值的变化,本文旨在让大家明白他们之间的区别 变量的作用域: 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接 ...

随机推荐

  1. 【cs231n】线性分类笔记

    前言 首先声明,以下内容绝大部分转自知乎智能单元,他们将官方学习笔记进行了很专业的翻译,在此我会直接copy他们翻译的笔记,有些地方会用红字写自己的笔记,本文只是作为自己的学习笔记.本文内容官网链接: ...

  2. geoserver源码学习与扩展——增加服务接口

    参看:http://www.cnblogs.com/sillyemperor/archive/2011/01/11/1929420.html 上文写的很详细了.

  3. location的部分属性

    http://www.w3school.com.cn/jsref/dom_obj_location.asp location.host 可以设置或返回主机名和当前url的端口    www.w3sch ...

  4. Codeforces Round #360 (Div. 2) D. Remainders Game

    D. Remainders Game time limit per test 1 second memory limit per test 256 megabytes input standard i ...

  5. 使用cxf调用webservice

    1.引入maven包 <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt ...

  6. ADO.net笔记

    1.DbConnectionConnection对象也称为数据库连接对象,Connection对象的功能是负责对数据源的连接.所有Connection对象的基类都是DbConnection类.Conn ...

  7. 【Python】常用排序算法的python实现和性能分析

    作者:waterxi 原文链接 背景 一年一度的换工作高峰又到了,HR大概每天都塞几份简历过来,基本上一天安排两个面试的话,当天就只能加班干活了.趁着面试别人的机会,自己也把一些基础算法和一些面试题整 ...

  8. getline

    istream& istream::getline(char*, streamsize,char= '\n'); 函数getline与get的区别在于,函数get当遇到分隔符后,停止获取,并将 ...

  9. Educational Codeforces Round 42 (Rated for Div. 2)F - Simple Cycles Edges

    http://codeforces.com/contest/962/problem/F 求没有被两个及以上的简单环包含的边 解法:双联通求割顶,在bcc中看这是不是一个简单环,是的话把整个bcc的环加 ...

  10. poj1679次小生成树入门题

    次小生成树求法:例如求最小生成树用到了 1.2.4这三条边,总共5条边,那循环3次的时候,每次分别不用1.2.4求得最小生成树的MST,最小的MST即为次小生成树 如下代码maxx即求最小生成树时求得 ...