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

在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. Elasticsearch之中文分词器

    前提 什么是倒排索引? Elasticsearch之分词器的作用 Elasticsearch之分词器的工作流程 Elasticsearch之停用词 Elasticsearch的中文分词器 1.单字分词 ...

  2. Linux 动态链接库包含静态链接库的方法

    今天老司机们在讨论一个编译问题  A是一个静态库  C是一个动态库  B是运行程序,能不能将A打包到C 然后B只需要链接C 就可以了. 这个问题我以前在出来zlib库版本冲突的时候有点印象,所以写了个 ...

  3. springboot的controller使用及url参数的获取

    类上加上@RequestMapping其访问的地址就是类上的加上方法上的菜能访问到该方法,例如上图的地址就是/hello/say @RequestMapping(value = "/hell ...

  4. ios 下拉刷新开源框架 MJRefresh

    gitHub 下载框架 搜索MJExampleViewController.h 下拉刷新 MJTableViewController 上拉刷新 MJTableViewController Collec ...

  5. 二、nginx 安装目录详解

    rpm -ql nginx 路径 类型 介绍 /etc/logrotate.d/nginx  配置文件  Nginx 日志轮转,用于logrotate服务日志切割 /etc/nginx /etc/ng ...

  6. MongoDB3.xxx 用户创建

    启动MongoDB前需要关闭配置文件中的auth选项,否则不能创建用户 首先创建用户管理用户 use admin db.createUser({user:'admin',pwd:'123456', r ...

  7. Shell 命令挂后台执行

    使用nohup命令,结合& #!/bin/bash #挂后台执行文件 kimbo_test.sh nohup >& & 说明:0 是标准输入(STDIN),1 是标准输出 ...

  8. 自学数据库建模工具--powerdesigner

    网上找了个英文的,版本感觉有点旧12的,但还好英语还行,摸索了一下功能,大概截了几张图,感觉优点:1总共有物理模型pdm,概念模型cdm,业务模型bdm 可以互转,逻辑清晰   2表与表之前的联系,建 ...

  9. Dev-C++添加代码格式化(format source code)工具Artistic Style

    Dev-C++是一个轻便的C++ IDE开发环境,比起VS2010来轻巧得多.最新的版本是5.4.0是2013年2月14日发布的,下载地址如下: Dev-C++5.4.0 download 它是开源项 ...

  10. zoj4028 LIS,差分约束

    题意:给你以i为结尾的最长上升子序列的值,和每个值的区间范围求可行的a[i] 题解:差分约束,首先满足l[i]<=a[i]<=r[i],可以建一个虚拟节点n+1,那么有a[n+1]-a[i ...