C语言圣经KR笔记 1.10外部变量和作用域

news2024/11/17 2:39:48

1.10外部变量和作用域

上一节main中的变量,如line、longest等等,对main来说是私有的或者说是局部的。因为它们是在main中定义的,其他函数不能直接访问它们。其他函数中的变量也是如此,例如,getline中的变量 i 与copy中的变量 i 没有关系。函数中的局部变量只在函数被调用时存在,当函数退出后就消失了。这就是为什么这样的变量通常被称作“自动”变量(遵循其他语言的术语)。我们后续也将使用“自动”这个词来指代这些局部变量。(第4章会谈到static存储类型,其中的局部变量可以在调用间保持它们的值。)

因为自动变量伴随着函数的调用或退出而出现或消失,它们不能在调用之间保持它们的值,每次进入时都必须明确地设定值。如果不设定值,就会包含垃圾值。

如果不想用自动变量的话,可以定义对所有函数来说都是“外部”的变量,也就是说,任何函数都可以用名字来访问该变量。(这个机制很像Fortan的COMMON,或者是Pascal最外层块中定义的变量。)因为外部变量是全局可访问的,因此可以用来取代参数列表,以实现函数间的数据通信。进一步来说,因为外部变量永远存在(不会随着函数的调用和退出,出现和消失),它们甚至能在设置它们的函数返回后保持它们的值。

外部变量必须被“定义”在所有函数之外,有且仅有一次;这会为它们分配存储空间。外部变量还必须在所有需要访问它的函数内“声明”;这是用于指明变量类型。声明必须是一个extern语句,或者在某些上下文时隐式声明。为了让上面的讨论更好理解,我们重写前面的计算最长行程序,并把line、longest和max作为外部变量。这要求改变所有三个函数的调用、声明和函数主体。

注意:getline已经是C标准库函数了(POSIX.1-2008.),如果要在新环境下编译运行代码,要给getline改个名字。

#include <stdio.h>

#define MAX_LINE 1000    /*最长的行长度*/

int max;                /* 目前的最大长度 */
char line[MAX_LINE];    /* 当前输入行 */
char longest[MAX_LINE]; /* 最长行存这里 */

int getline(void);
void copy(void);

/* 输出最长输入行,特别版 */
main()
{
    int len;
    extern int max;
    extern char longest[];

    max = 0;
    while ((len = getline()) > 0)
        if (len > max) {
            max = len;
            copy();
        }
    if (max > 0)
        printf("%s", longest);
    return 0;
}

/* getline: 特别版 */
int getline(void)
{
    int c, i;
    extern char line[];

    for (i = 0; i < MAX_LINE-1 && (c = getchar())!=EOF && c != '\n'; ++i)
        line[i] = c;
    if (c == '\n') {
        line[i] = c;
        ++i;
    }
    line[i] = '\0';
    return i;
}

/* 拷贝,特别版 */
void copy(void)
{
    int i;
    extern char line[], longest[];

    i = 0;
    while ((longest[i] = line[i]) != '\0')
        ++i;
}

main、getline、copy中的外部变量在代码的最前几行定义,这指明了它们的类型并给它们分配了存储空间。从语法上来说,外部变量的定义和局部变量的定义是一样的,但由于它们在函数外面出现,故变量就是外部的。在函数使用外部变量之前,必须要让函数知道它的名字。一种方法是在函数内写一个extern声明;声明与前面的写法一致,区别在于前面加了个extern关键字。

在某些环境下,extern声明可以省略。如果在源文件中,外部变量的定义先于它在某个函数中的使用,那就没有必要在这个函数中加入extern声明。这样main、getline和copy中的extern声明其实是冗余的。实际上,通常的实践方式是把所有外部变量的定义放在源文件开头,就能去掉所有的extern声明。

如果程序有几个源文件,且一个变量在文件1中定义,并在文件2和文件3中使用,那么在文件2和文件3中就需要extern声明,以便关联这个变量的出现。通常的实践是把所有的外部变量和函数的声明放在一个单独的文件中(历史上被称为header头文件),在每个源文件的开头用#include来包含它。按惯例使用.h作为头文件名的后缀。例如,标准库里的函数在如<stdio.h>之类的头文件里面声明。这个主题的详细讨论会在第四章,而标准库会在第七章和附录B。

由于特别版的getline和copy没有参数,故从逻辑上看它们的原型应当是getline() 和 copy()。但为了和旧的C程序兼容,C标准将空参数列表当作是旧风格的声明,并会为其关闭参数列表校验;空参数列表必须明确地用void来表示。第四章会进一步讨论。

注意要区分“定义”和“声明”。“定义”是变量被创建并分配存储的地方。“声明”是指明变量的性质的地方,并不分配存储。

顺带一提,有人倾向是把所有能看到的东西都变成extern变量,因为这看起来能简化通信——参数列表很短,而且只要你想,你总是能访问这些变量。但问题在于,即使你不想要的时候,外部变量也总是存在着。过于依赖外部变量是非常危险的,因为它导致程序的数据关联完全不明显——变量可能因意外甚至疏忽被改变,程序也很难修改。第二版的最长行程序比第一版差,部分是由于这个原因,而部分是因为破坏了两个有用函数的通用性(把它们和它们操作的变量绑定起来了)。

本章到这里我们已经覆盖了C语言的常规核心部分。用这些基础部分,可能写成长度相当可观的、有用的程序,建议你在往下学之前试试。后面的练习题包含了比本章前面样例更复杂的程序。

(第一章完)

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/956966.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言:static关键字的使用

1.static修饰局部变量 这是static关键字使用最多的情况。我们知道局部变量是在程序运行阶段在栈上创建的&#xff0c;但是static修饰的局部变量是在程序编译阶段在代码段&#xff08;静态区&#xff09;创建的。所以在static修饰的变量所在函数执行结束后该变量依然存在。 //…

C++(17):标准库特殊设施

tuple 类型 tuple是类似pair的模板。 每个pair的成员类型都不相同&#xff0c;但每个 pair都恰好有两个成员。不同tuple类型的成员类型也不相同&#xff0c;但一个tuple可以有任意数量的成员。 每个确定的tuple类型的成员数目是固定的&#xff0c;但一个tuple类型的成员数目可…

OS 死锁处理

如果P先申请mutex 则mutex从1置零&#xff0c;假设申请到的empty 0则empty变成-1阻塞态 同理C中mutex从0变为-1&#xff0c;那么如果想离开阻塞态&#xff0c;那么就需要执行V&#xff08;empty&#xff09;但是如果执行V&#xff08;empty&#xff09;就需要P&#xff08;mu…

postgresql-窗口函数

postgresql-窗口函数 简介窗口函数的定义分区选项&#xff08;PARTITION BY&#xff09;排序选项&#xff08;ORDER BY&#xff09;窗口选项&#xff08;frame_clause&#xff09; 聚合窗口函数排名窗口函数演示了 CUME_DIST 和 NTILE 函数 取值窗口函数 简介 常见的聚合函数&…

飞致云开源社区月度动态报告(2023年8月)

自2023年6月起&#xff0c;中国领先的开源软件公司FIT2CLOUD飞致云以月度为单位发布《飞致云开源社区月度动态报告》&#xff0c;旨在向广大社区用户同步飞致云旗下系列开源软件的发展情况&#xff0c;以及当月主要的产品新版本发布、社区运营成果等相关信息。 飞致云开源大屏…

开讲啦!0基础也能玩转飞桨开源社区

作为cs/ai学生&#xff0c;你是否经历过这些至暗时刻&#xff1a; 希望快速入门深度学习&#xff0c;无奈网上到处都是看不懂“黑话”一遍遍计算综测小数点后四位&#xff0c;不断在保研边缘反复横跳看着“洁白如新”的履历叹气&#xff0c;一听到“考研复试”就头皮发麻“0实习…

【ES6】Promise.allSettled的用法

Promise.allSettled() 是一个Promise方法&#xff0c;用于处理一个Promise数组&#xff0c;返回一个新的Promise数组&#xff0c;每个元素对应原始Promise的状态。这个方法可以用于处理多个异步操作&#xff0c;并且能够获取每个操作的结果和状态。 下面是Promise.allSettled(…

基于蛇优化算法优化的BP神经网络(预测应用) - 附代码

基于蛇优化算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于蛇优化算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.蛇优化优化BP神经网络2.1 BP神经网络参数设置2.2 蛇优化算法应用 4.测试结果&#xff1a;5.Matlab代…

ChatGPT~Error1015You are being rate limited

目录 问题背景 问题的原因 下来说说解决方案 总结 问题背景 今天使用Chatgpt的时候突然出现"You are being rate limited"的错误提示。 问题的原因 小问题了&#xff0c;又不是第一次被弄出来了&#xff0c;莫慌。 让我们先看看Chatgpt自己是怎么解释这个问题…

GE Diary 0001

今天是入职GEHC的第一天&#xff0c;虽然是OD岗位&#xff0c;但是员工氛围暂时没感觉有什么差异&#xff0c;领导也很随和。 公司印象&#xff1a;GEHC目前给我的印象是典型的外企&#xff1a;朝九晚五、无需打卡&#xff08;OD岗位需要打本公司的卡&#xff09;、短小精悍的会…

2781. 最长合法子字符串的长度

2781. 最长合法子字符串的长度 C代码&#xff1a;滑动窗口、哈希表 typedef struct{char* str;UT_hash_handle hh; } HashTable;HashTable* head;void AddToHash(char* str) {HashTable* out (HashTable*)malloc(sizeof(HashTable));out->str str;HASH_ADD_STR(head, str…

Opencv基于文字检测去图片水印

做了一个简单的去水印功能&#xff0c;基于文字检测去图片水印。效果如下&#xff1a; 插件功能代码参考如下&#xff1a; using namespace cv::dnn; TextDetectionModel_DB *textDetector0; void getTextDetector() {if(textDetector)return;String modelPath "text_de…

C++ 学习系列 -- const 关键字

C 中引入了 const 关键字&#xff0c;被 const 关键字修饰的变量其值不能修改&#xff0c;是只读的。 一 const 变量的初始化与存储 1. const 变量初始化时就需要赋初值&#xff0c;否则无法编译通过 二 const 与指针 指针常量&#xff1a; 指针所指向的值不可改变&…

Mysql数据库(2)—事务和锁

一、事务 数据库事务的特性&#xff1f; 数据库事务的四大特性是ACID。 原子性&#xff1a;就是所有操作要么全不做&#xff0c;要不全做。通过undo日志来实现。一致性&#xff1a;就是在并发情况下数据库由一个状态转移到另一个状态的数据要一致。通过事务的隔离级别来实现…

查询优化器内核剖析第一篇

SQL Server 的查询优化器是一个基于成本的优化器。它为一个给定的查询分析出很多的候 选的查询计划&#xff0c;并且估算每个候选计划的成本&#xff0c;从而选择一个成本最低的计划进行执行。实际上&#xff0c; 因为查询优化器不可能对每一个产生的候选计划进行优化&#xff…

2023-8-31 spfa求最短路

题目链接&#xff1a;spfa求最短路 #include <iostream> #include <cstring> #include <algorithm> #include <queue>using namespace std;const int N 100010;int n, m; int h[N], w[N], e[N], ne[N], idx;int dist[N]; bool st[N];void add(int a…

360勒索病毒:了解最新变种.360,以及如何保护您的数据

引言&#xff1a; 近年来&#xff0c;网络安全威胁不断演变&#xff0c; 360 勒索病毒作为其中的一种恶意软件&#xff0c;已经对许多个人和组织的数据造成了巨大的损失。本文91数据恢复将介绍 360 勒索病毒的特点&#xff0c;探讨恢复被其加密的数据的方法&#xff0c;并分享一…

ctfshow 红包题

前言&#xff1a; 最近一直在搞java很少刷题&#xff0c;看见ctfshow的活动赶紧来复现一波~ ctfshow 红包挑战7 <?php highlight_file(__FILE__); error_reporting(2); extract($_GET); ini_set($name,$value); system("ls ".filter($_GET[1])."" )…

重要变更 | Hugging Face Hub 的 Git 操作不再支持使用密码验证

在 Hugging Face&#xff0c;我们一直致力于提升服务安全性&#xff0c;因此&#xff0c;我们将修改 Hugging Face Hub 的 Git 交互认证方式。 从 2023 年 10 月 1 日 开始&#xff0c;我们将不再接受密码作为命令行 Git 操作的认证方式。我们推荐使用更安全的认证方法&#xf…

敏感接口权限校验

前端校验 &#xff08;从前端或者从token里面拿一下&#xff09;&#xff0c;看一下用户有没有这个页面的权限&#xff08;但是一般不用&#xff0c;因为nodejs也可以写后端&#xff0c;但是放到前端去校验不安全&#xff09; 后端校验 需要梳理敏感数据接口&#xff0c;将这…