前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C语言变量的作用域和类别-学习二十

C语言变量的作用域和类别-学习二十

作者头像
XG.孤梦
发布2022-03-01 14:12:53
9480
发布2022-03-01 14:12:53
举报
文章被收录于专栏:日志随记日志随记

本文最后更新于2022年02月14日,已超过13天没有更新。如果文章内容或图片资源失效,请留言反馈,我会及时处理,谢谢!

变量的作用域

  • 在程序中能对变量进行存取操作的范围称为变量的作用域
  • 根据变量的作用域不同,变量分为局部变量全局变量
  • 局部变量
    • 在一个函数体内或复合语句内定义变量称为局部变量
    • 局部变量只在定义它的函数体或复合语句内有效,即只能在定义它的函数体或复合语句内部使用它, 而在定义它的函数体或复合语句之外不能使用它。
  • 例子
代码语言:javascript
复制
#include <stdio.h>

void main(){

    int i,a,b; // 定义局部变量 i,a,b
    i = i + 1; // 可以使用 i

     {   
        int c;
        c = a + b;  // c 在此范围内有效; a, b在此范围内有效
     }
    c = c + 1;  // c 在此范围内无效
}

int add(int x,int y){
    int j;     // 定义局部变量 j
    j = x + y; // 可以使用 j

    i = x + y;  // 不能使用main函数里局部变量i,系统会提示 i 未声明
}

全局变量

  • 函数定义之外定义的变量称为全局变量
  • 全局变量可以在定义它的文件中使用,其作用域是从它的定义处开始到变量所在文件的末尾
  • 例子
代码语言:javascript
复制
#include <stdio.h>

 int i,j,a,b, n = 5; // 定义全局变量
void main(){
    a = 5;
    b = 6;
    i = a + b; 
     {   
        j = a - b;  // c在此范围内有效;a,b在此范围内有效
     }
   int add(int x); // 函数声明
   printf("i = %d, j = %d, m = %d\n",i,j,add(5));
}

int add(int x){
    int m;
    m = n + 1; // 可以使用n
    return m;
}

建议:

  • 不必要时不要使用全局变量,原因如下:
    • 1.全局变量在程序的全部执行过程中占用存储单元,而不是仅在需要时才开辟单元。
    • 2.使用全局变量过多,会降低程序的清晰性。 在各个函数执行时都可能改变外部变量的值,程序容易出错,所以要限制使用全局变量。
    • 3.降低函数的通用性。 因为函数在执行时要依赖于其所在的外部变量。如果将-一个函数移到另一个文件中,还要将有关的外部变量及其值一起移过去。但若该外部变量与其他文件的变量同名时,就会出现问题,降低了程序的可靠性和通用性
  • 一般要求把C程序中的函数做成一个封闭体, 只可以通过“实参-形参”的渠道与外界发生联系外。

如果外部变量与局部变量同名,即全局变量与局部变量同名时,局部变量优先!

代码语言:javascript
复制
#include <stdio.h>

int a = 3, b = 5; // a,b为外部变量

void main()
{
    int a = 8; // a为局部变量
    printf("%d\n", max(a, b));//这里a为局部变量,b为全局变量
}
max(int a,nt b)
{
    int c;
    c = a > b ? a : b; // 形参a、b作用范围只在max函数里
    return (c);
}

变量的存储类别

  • 1.动态存储方式与静态存储方式
    • 从变量的作用域(即从空间)角度来分,可以分为全局变量局部变量
    • 从变量值存在的时间角度来分,又可以分为静态存储方式动态存储方式
  • 静态存储方式:
    • 指在程序运行期间由系统分配固定的存储空间的方式。
  • 动态存储方式:
    • 则是在程序运行期间根据需要进行动态的分配存储空间的方式。
  • 这个存储空间可以分为三部分:
    • 程序区
    • 静态存储区:全局变量
    • 动态存储区:函数的形参、函数中定义的变量、函数调用时的现场保护和返回地址等
  • 变量和函数有两个属性: 数据类型数据的存储类别
    • 存储类别指的是数据在内存中存储的方式
    • 存储方式分为两大类:静态存储类动态存储类。 包含:
      • 自动的( auto ) ;
      • 静态的( static ) ;
      • 寄存器的( register ) ;
      • 外部的( extern )。
    • 根据变量的存储类别,可以知道变量的作用域和生存期

auto变量

  • 自动的auto,不专门声明为static存储类别的局部变量都是动态分配存储空间,在调用该函数时系统会给 它们分配存储空间,在函数调用结束时自动释放这些存储空间。因此这类局部变量称为自动变量。
  • 函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类。
  • 关键字auto作存储类别的声明
  • 例子
代码语言:javascript
复制
    int f(int a)    // 定义f函数,a为形参
     { 
         auto int b,C=3;  // 定义b、C为自动变量
    }
  • 关键字auto可以省略
    • auto int a,b,c=3;int a,b,c=3; 二者等价

static静态局部变量

  • 静态的static,当函数中的局部变量的值在函数调用结束后不消失而保留原值,该变量称为静态局部变量
  • 用关键字static进行声明
  • 例子
代码语言:javascript
复制
#include <stdio.h>

void main()
{
    int f(int a);
    int a = 0, i;
    for (i = 0; i < 3; i++) {
        printf("%d\n", f(a));
    }    
}

int f(int a) {   // 每调用一次,开辟新 a 和 b,但是 c 没有
    auto b = 0;
    static c = 0;
    b++;
    c++;
    return (a + b + c);
}
  • 静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不释放
  • 动态局部变量属于动态存储类别,占动态存储区空间而不占静态存储区空间函数调用结束后即释放
  • 静态局部变量在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值。 以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值
  • 如果在定义局部变量时不赋初值,则对静态局部变量来说,编译时自动赋初值 0 (对数值型变量)或 空字符 (对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值
  • 虽然静态局部变量在函数调用结束后仍然存在,但其他函数不能引用它
例题

求1-5的阶

代码语言:javascript
复制
#include <stdio.h>

void main()
{
    int fac(int a);
    int i;
    for (i = 1; i <= 5; i++) {
        printf("%d的阶乘=%d\n",i, fac(i));
    }
}

int fac(int a) {
    static int f = 1;
    f = f * a;
    return (f);
}

register变量

  • 寄存器的register,变量的值是存放在内存中的,当程序中用到哪个变量的值时,由控制器发出指令将内存中该变量的值送到运算器中。经过运算器进行运算,如果需要存数,再从运算器将数据送到内存存放。
  • 如果有一些变量使用频繁,则为存取变量的值要花费不少时间。
  • 为提高执行效率,C语言允许将局部变量的值放在CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存中去存取
  • 由于对寄存器的存取速度远高于对内存的存取速度,因此这样做可以提高执行效率。这种变量叫做寄存器变量
  • 用关键字register作声明
  • 例子
代码语言:javascript
复制
#include <stdio.h>
void main()
{
    long fac(long);
    long i, n;
    printf("请输入一个整数:");
    scanf_s("%ld", &n);
    for (i = 1; i <= n; i++){
        printf("%ld的阶乘=%ld\n", i, fac(i));
    }
}
long fac(long n){
    register long i, f = 1;  // 定义寄存器变量
    for (i = 1; i <= n; i++) {
        f = f * i;
    }    
    return(f);
}

extern外部变量

  • 外部的extern,变量是在函数的外部定义的全局变量,它的作用域是从变量的定义处开始,到本程序文件的末尾
  • 在此作用域内,全局变量可以为程序中各个函数所引用。编译时将外部变量分配在静态存储区
  • extern来声明外部变量,以扩展外部变量的作用域。
  • 例子

在一个文件内声明外部变量

代码语言:javascript
复制
#include <stdio.h>
void main()
{
    int max(int, int);
    extern A, B;   // 声明外部变量
    printf("%d\n", max(A, B));
}

int A = 15, B = 8;  // 定义外部变量

int max(int x, int y){
    int z;
    z = x > y ? x : y;
    return(z);
}

在多文件的程序中声明外部变量,用extern将外部变量的作用域扩展到其他文件。

  • 文件一
代码语言:javascript
复制
#include<stdio.h>

int A; //定义外部变量

void main(){
    printf("%d\n",A++);
}
  • 文件二
代码语言:javascript
复制
extern A = 100;  //声明A为一个已定义的外部变量

static声明外部变量

  • 在程序设计中,某些外部变量只限于被本文件引用而不能被其他文件引用。这时可以在定义外部变量时加一个static声明
  • 例子
  • 文件一
代码语言:javascript
复制
#include<stdio.h>

static int A; // 定义外部变量

void main() {
    printf("%d\n", A++);
}
  • 文件二
代码语言:javascript
复制
extern int A;  // 无法使用 全局变量
void fun(int n) {
    A = A * n;
}

关于变量的声明和定义

  • 定义性声明:需要建立存储空间的(如: int a; )声明。
  • 引用性声明:不需建立存储空间的声明( extern a; )。
  • 注意:
    • 声明包括定义,但并非所有的声明都是定义
    • int a; 而言,它既具声明,又是定义。
    • 而对 extern a; 而言,它是声明而不是定义。

总结

  • 1.从作用域角度分,有局部变量和全局变量。它们采用的存储类别如下:
    • 局部变量包括:
      • 自动变量、静态局部变量、寄存器变量。
    • 全局变量包括:
      • 静态外部变量、外部变量。
    • 形式参数可以定义为 自动变量 或 寄存器变量。
  • 2.从变量存在的时间来区分,有动态存储静态存储两种类型。
    • 动态存储:
      • 自动变量、寄存器变量、形式参数。
    • 静态存储:
      • 静态局部变量、静态外部变量、外部变量。
    • 静态存储是程序整个运行时间都存在,而动态存储则是在调用函数时临时分配单元。
  • 3.从变量值存放的位置来区分,可分为:
    • 内存中静态存储区:
      • 静态局部变量、静态外部变量、外部变量。
    • 内存中动态存储区:
      • 自动变量和形式参数。
    • CPU中的寄存器:
      • 寄存器变量。
  • 4.关于作用域和生存期的概念
    • 作用域:如果一个变量在某个文件或函数范围内是有效的,就称该范围为该变量的作用域
    • 生存期:如果一个变量值在某一时刻是存在的,则认为这一时刻属于该变量的生存期
    • 作用域是从空间的角度,生存期是从时间的角度

内部函数和外部函数

  • 根据函数能否被其他源文件调用,将函数区分为内部函数外部函数

内部函数

  • 内部函数又被称为静态函数,它只能被定义它的文件中的其他函数调用,而不能被其他文件中的函数调用, 即内部函数的作用范围仅仅局限于本文件
  • 在定义内部函数时,在函数名和函数类型的前面加static。即 static 类型标识符 函数名(形参表)
  • 例如:
    • static int fun( inta, intb)

外部函数

  • 定义函数时,如果在函数首部的最左端加关键字extern,则表示此函数是外部函数,可供其他文件调用
  • 例如:
    • 函数首部可以写为 extern int fun (int a,int b) 这样,函数fun就可以为其他文件调用。
  • 如果在定义函数时省略 extern ,则隐含为外部函数
  • 在需要调用此函数的文件中,用extern对函数作声明,表示该函数是在其他文件中定义的外部函数
  • 例子

有一个字符串,内有若干个字符,输入一个字符,要求程序将字符串中该字符删去。用外部函数实现。

  • 文件一 main主函数
代码语言:javascript
复制
#include<stdio.h>

void main() {
    extern void estring(char str[]);
    extern void dstring(char str[], char ch);
    extern void pstring(char str[]);

    char c, str[80];
    printf("请输入一串字符:\n");
    estring(str);
    printf("请输入一个要删除的字符:\n");
    scanf_s("%c", &c);
    dstring(str, c);
    pstring(str);
}
  • 文件二 dstring 删除字符函数
代码语言:javascript
复制
void dstring(char str[], char ch) {
    int i, j;
    for (i = j = 0; str[i] != '\0'; i++) {
        if (str[i] != ch) {
            str[j] = str[i];
            j++;
        }
    }
    str[j] = '\0';
}
  • 文件三 estring 接收输入函数
代码语言:javascript
复制
#include <stdio.h>

extern void estring(char str[80]) {
    gets(str);
}
  • 文件四 pstring 打印输出函数
代码语言:javascript
复制
#include <stdio.h>

void pstring(char str[]) {

    printf("%s\n", str);
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-02-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 变量的作用域
  • 全局变量
    • 建议:
    • 变量的存储类别
      • auto变量
        • static静态局部变量
          • 例题
        • register变量
          • extern外部变量
            • static声明外部变量
              • 关于变量的声明和定义
              • 总结
              • 内部函数和外部函数
                • 内部函数
                  • 外部函数
                  相关产品与服务
                  对象存储
                  对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档