c语言有32个关键字,每个关键字你都理解吗?

今天出场的是:

auto ,  register,  static,   extern

为什么他们会一起呢,说到这里不得不谈到c语言对变量的描述。

c给每个变量3个基本属性:

1.    作用域

2.    生命期

3.    存储类型

一些基本概念定义:

块的定义:

在c语言里 : 是以{}定义的  很显然函数是其中一个。

声明和定义:

1  声明是说有这个东西,但并不是马上要产生出来,比如你跟别人说你会玩dota,但你现在并没有玩。理解这个就好理解定义了。

注意:  声明可以在多个地方, 而这些地方就是所谓的命名空间(c语言没有这个概念,只是比喻),如果要引用全局变量,而又不在全局变量的作用范围内,可以用extern关键字,表示我要引用这个变量,他已经在某个文件里或其他全局区域定义了,还有就是没有定义,我也可以用,但link时候如果没有定义就会报错,无法解析(这个问题要牵扯到编译器的链接原理,在以后会有分析)。

2   定义就是产生它,一般我们都是声明和定义一起的,全局变量都是定义在文件的最上面,所以没有将声明和定义分开,分开的情况是在,多个文件编译中,如果其中一个(如果是头文件)文件定义了一个全局变量a,那么你引用这个头文件时(所谓的头文件包含,就是把头文件的东西copy到你的.c文件上面,只是方便浏览源代码才有头文件概念,一次定义,可以多处使用),很显然,你的.c文件再定义一个就是重复定义了。还有一种情况是: 在一个.c文件里定义了一个全局变量int a,如果你又在其他.c文件里定义一个全局变量int
a,link 时( 链接)就会出错,如果是同名但不通类型,链接正常(编译器可以区分他们,这个问题要牵扯到编译器的链接原理,在以后会有分析)。

注意: 定义只能定义一次,不能多次,如果要问为什么,那是因为定义就是给变量分配内存空间(按变量的类型分配大小),所以只能定义一次。

作用域:  一个变量,就像当官一样,也有自己的领地,比如全局变量(就是从定义地方到文件结束),但是在某个块的内部,如果也定义了一个同名的变量(只可以定义一个),那这个地方就归他管了,你没有权利,他就是这个地方的皇帝,然而如果在其他块中没有与你同名的变量,那这些地方就由你管。看下面的图知道, 如果不是在函数内定义的变量,都是全局变量,其实全局变量区,就是各个函数之间的区域。全局是向下扩展的,并不是整个文件,就是说,在某个定义全局变量的上面,已经没有他的存在。如果上面的要访问他,可以用extern关键字(声明),这些对于局部变量行不通,对于局部变量在c语言里都必须定义在最前面。

要理解生命期先知道下面的。

看一下源代码文件的结构:

图 1

上面这个图配合下面概念讲的。

全局变量和局部变量, 你真的理解他们吗?其实谈到这个,又得讲一下c语言内存分配:

系统内核为高2GB,用户为低2GB(在32位的内存中)



 

图 2

BSS: 是“Block Started by Symbol”的缩写,意为“以符号开始的块”,在程序开始之前,内核将此段初始化为0。既这片内存在系统启动之后本身初值为0。

在c语言里 内存可分为静态存储区和动态存储区。

静态存储区: 就是程序在编译时就已经分配了,比如,全局变量(全局静态变量),静态局部变量等,且初始化是在程序启动开始时就已经初始化好了,对于这点,举个例:

void Fun()

{

static  int   a = 10; /*其实这个语句在函数运行时并没有执行,它是提供给系统(编译                               器)一个初值10,然后再程序启动之前,把此变量初始化。*/

}

动态存储区: 程序在运行时,根据需要才分配的,比如,用户用malloc等动态申请的内存(系统堆,但用户控制),系统控制的函数调用栈。

所以,

全局变量:  就是定义在函数外的,且内存在静态存储区,作用范围:定义他的位置到文件结束。

局部变量:定义在块内(函数是块的最高境界),分为普通的局部变量(就是在栈中),和静态局部变量(在静态存储区),作用范围:定义它的位置到块结束。

注意: 很多人会不知道,什么时候用局部变量,什么时候用全局变量,少用全局变量,多用局部变量。全局变量是放在静态存储区中,定义了全局变量,系统就少了一个可以利用数据存储空间,太多全局变量,会导致编译器无足够内存分配;而局部变量,可以在不同的模块中重用(合理分配),其次从模块的耦合性来说,全局变量使模块的耦合性增加,模块独立性不高,还有在多线程程序里面,一般都建议不要用全局变量,这样可能造成线程访问冲突,加锁等很多问题,但这个东西是相对的,有时用全局变量已有好处,比如全局变量的空间范围一般比函数栈空间要大(和编译器有关),有时可能栈要溢出。如果函数要反复经常调用,函数里面有很多的局部变量,这些变量就可能要求栈反复地申请和释放,这样也耗时,所以这个问题要自己去体会,根据实际情况分析。

生命期:  就是一个变量内存分配到内存释放(我们称为死亡)的时间段。全局变量和局部静态变量一般是和程序共存亡,普通的局部变量一般是和块共存亡。

注意:虽然局部静态变量离开他的块时是不能访问的,但他并没有死亡,我们可以通过其他手段来访问它(嘿嘿)。

存储类型: 这个后面会有详细的讲解,现在只是说一下,其实在32系统中(其他可以类推),系统内存就是一个数组,每个地址对应一个字节。然后,为了让变量掌握的字节个数不同(提供更多给接口方便我们使用),系统为了区分它们,就给他们取了不同的名字(char,short,int,long等),然后在用的时候,就从它们的起始地址取出所占的字节数,然后再按一定语意来解析它们(之前协商好的协议(自己的理解))。

好,现在来具体讲哈

auto ,  register,  static,   extern

4个关键字

auto: 从名字上,我们称为自动变量,一般都叫局部变量,变量的默认属性就是auto,所以一般都没有用。

register: 就是定义寄存器变量,此变量有个例外,就是如果他真的称为寄存器变量,那么他存在于寄存器中,就不是在内存中,所以没有地址。此关键字会让系统尽量合适地把此变量作为寄存器变量(为什么不是一定、而是尽量,原因有很多,比如寄存器有限等),目的是提高访问速度。当一个变量被频繁读/写时,需要反复访问内存,花费大量存取时间。为了提高访问效率,可以使用CPU寄存器变量,不需要访问内存,直接进行读/写,这个关键字在嵌入式程序设计中经常会用到。

注意:

1) 只有局部自动变量和形参才可以定义为寄存器变量。因为寄存器变量属于动态存储方式,因此凡需要采用静态存储方式变量都不能定义为寄存器变量。

但是使用register修饰符有几点限制

2) register变量必须是能被CPU所接受的类型。

这通常意味着register变量必须是一个单个的值,并且长度应该小于或者等于整型的长度。不过,有些机器的寄存器也能存放浮点数。

3)因为register变量可能不存放在内存中,所以不能用“&”来获取register变量的地址。

在调用一个函数时占用一些寄存器以存放寄存器变量的值,函数调用结束后释放寄存器。此后,在调用另外一个函数时又可以利用这些寄存器来存放该函数的寄存器变量。

4)由于寄存器的数量有限(不同的类型cpu(Intel系列,ARM系列,PowerPC系列等)寄存器数目不一),不能定义任意多个寄存器变量,而且某些寄存器只能接受特定类型的数据(如指针和浮点数),因此真正起作用的register修饰符的数目和类型都依赖于运行程序的机器,而任何多余的register修饰符都将被编译程序所忽略。

5) 早期的C编译程序不会把变量保存在寄存器中,除非你命令它这样做,这时register修饰符是C语言的一种很有价值的补充(由于历史原因)。然而,随着编译程序设计技术的进步,在决定哪些变量应该被存到寄存器中时,现在的C编译环境能比程序员做出更好的决定。实际上,许多编译程序都会忽略register修饰符,因为尽管它完全合法,但它仅仅是暗示而不是命令。

static:  此关键字很重要

对于变量:

1 static的变量存储在静态区,所以变量的值,有持久性,不会随着块的离开而消失;

2 变量被隐藏,如果是全局变量(此针对于多个文件)可见性就是定义他那个文件,其他文件不可访问,不可以用extern来引用,反过来就是说,如果没有static关键字,其他文件就可以通过extern来引用;

3 变量没有定义初值,系统(编译器)会默认设置初值为0;

对于函数: 没有static修饰的函数,在多个文件编译时,具有全局可见性(这个读者自己可以试试)而有static修饰的函数,只是在本文件可见,对其他文件隐藏。

extern : 上面很多地方提到, 对于全局变量如果不在作用域内,extern  可以扩大他的作用域。例如,在单个文件中,在全局变量之上地方可以用extern来引用下面定义的全局变量,而在多个文件中,extern可以引用其他文件的全局变量。

注意:  extern能否引用其他文件的全局变量,要看其他文件的全局变量是否隐藏,很显然局部变量是不能引用的。

声明:

本文完全是为了学习而诞生的, 我们只有不断理解,不断地从错误的认识中清醒过来,才可能更好使用它,希望对你们有用,要成为顶级的c程序员这些只是,记住只是基础中的基础,还有很多的东西有待我们去研究实践,这路还很长。

  思考小问题:

       c语言里的一个递归问题,怎么从第20层直接返回到第17层,保证程序正常运行?

顶级c程序员之路 基础篇 - 第一章 关键字的深度理解 number-1的更多相关文章

  1. 顶级c程序员之路 选学篇-1 深入理解字节,字节序与字节对齐

     深入理解字节,字节序与字节对齐 一 总述 作为一个职业的coder玩家,首先应该对计算机的字节有所了解. 我们经常谈到的2进制流,字节(字符)流,数据类型流(针对编程),结构流等说法,2进制流,0和 ...

  2. 从零开始的程序逆向之路基础篇 第二章——用OllyDbg(OD)分析一个简单的软件

    作者:Crazyman_Army 原文来自:https://bbs.ichunqiu.com/thread-43469-1-1.html 0x00知识回顾 (由于笔者省事,没开XP虚拟机,而且没关闭A ...

  3. Java语言程序设计(基础篇)第一章

    第一章 计算机.程序和Java概述 1.1 引言 什么是程序设计呢? 程序设计就是创建(或者开发)软件,软件也称为程序. 1.2 什么是计算机 计算机是存储和处理数据的电子设备,计算机包括硬件(har ...

  4. DirectX9:基础篇 第一章 初始化Direct3D

    一.简介 二.Direct3D类 1.创建D3D类 IDirect3D9* WINAPI Direct3DCreate9(UINT SDKVersion); //Direct3D类的创建 IDirec ...

  5. Java编程基础篇第一章

    计算机语言 人与计算机交流的方式. 计算机语言有很多种如:C语言,c++,Java等 人机交互 软件的出现实现了人与计算机之间的更好的交流(交互) 交互方式 图形化界面:便于交互,容易操作,简单直观, ...

  6. 《程序员代码面试指南》第一章 栈和队列 最大值减去最小值小于或等于num的数量

    题目 给定整数数组arr和整数num,共返回多少的数组满足如下情况 max(arr[i...j]) - min(arr[i...j]) <= num max(arr[i...j])表示数组arr ...

  7. 《程序员代码面试指南》第一章 栈和队列 构造数组的MaxTree

    题目 给出一个无重复元素的数组,构造此数组的MaxTree, java代码 /** * @Description: 构造数组的MaxTree * @Author: lizhouwei * @Creat ...

  8. 《程序员代码面试指南》第一章 栈和队列 设计一个有getMin功能的栈

    题目 实现一个特殊的栈,在实现栈的基本功能上,再实现返回栈中最小的元素的操作 要求 1. pop.push.getMin操作时间复杂度都是O(1) 2. 设计的栈类型可以使用现成的栈结构 java代码 ...

  9. Java 进阶 hello world! - 中级程序员之路

    Java 进阶 hello world! - 中级程序员之路 Java是一种跨平台的语言,号称:"一次编写,到处运行",在世界编程语言排行榜中稳居第二名(TIOBE index). ...

随机推荐

  1. cassandra权威指南读书笔记--cassandra概述

    cassandra是一个开源的.分布式.去中心化.弹性可扩展.高可用.容错.可调一致性.面向行数据库,分布式设计基于Amazon Dynamo,数据模型基于Google BigTable.cassan ...

  2. Java JDBC 模糊查询 避免输入_,%返回全部数据

    Java JDBC 模糊查询 避免输入_,%返回全部数据 "SELECT * FROM employees WHERE INSTR(first_name,?)>0 " 仅供参 ...

  3. 使用Observer实现HBase到Elasticsearch的数据同步

    最近在公司做统一日志收集处理平台,技术选型肯定要选择elasticsearch,因为可以快速检索系统日志,日志问题排查及功业务链调用可以被快速检索,公司各个应用的日志有些字段比如说content是不需 ...

  4. Java——方法及构造方法、intellij IDEA中的一些快捷键

    intellij IDEA中的一些快捷键: 一.方法基础 给你一个两个int类型的数相加的例子: 这个例子说明了 public static void main(String[] args) {}相当 ...

  5. Codeforces Round #645 (Div. 2) D. The Best Vacation (贪心,二分)

    题意:一年有\(n\)个月,每月有\(d_{i}\)天,找出连续的\(x\)天,使得这\(x\)天的日期总和最大,任意一年都能选. 题解:首先要先贪心,得到:连续的\(x\)天的最后一天一定是某个月的 ...

  6. Codeforces Round #649 (Div. 2) A. XXXXX (贪心)

    题意:有一个长度为\(n\)的数组,找一段最长子数组,使得其元素和为\(x\),如果存在,输出子数组的长度,否则输出\(-1\). 题解:这题我们要从元素和\(sum\)来考虑,首先,如果原数组的所有 ...

  7. C# TCP应用编程一 概述

    TCP 是Transmission Control Protocol(传输控制协议)的简称,是TCP/IP 体系中面向连接的运输层协议,在网络中提供全双工的和可靠的服务.一旦通信双方建立了TCP 连接 ...

  8. Django服务器布置(Ubuntu+uwsgi+nginx+Django)

    一.安装Python apt install python3 二.安装pip apt install python3-pip 三.创建目录 创建虚拟服务目录 mkdir -p /data/env 创建 ...

  9. kubernetes实战-交付dubbo服务到k8s集群(一)准备工作

    本次交付的服务架构图:因为zookeeper属于有状态服务,不建议将有状态服务,交付到k8s,如mysql,zk等. 首先部署zk集群:zk是java服务,需要依赖jdk,jdk请自行下载: 集群分布 ...

  10. Python append() 与深拷贝、浅拷贝

    在leetcode77中,发现list.append的结果不对.原代码: class Solution: def combine(self, n: int, k: int) -> List[Li ...