extern数组与extern指针
数组名代表了存放该数组的那块内存,它是这块内存的首地址。这就说明了数组名 是一个地址,而且,还是一个不可修改的常量,完整地说,就是一个地址常量。数组名 跟枚举常量一样,都属于符号常量。数组名 这个符号,就代表了那块内存的首地址。注意了!不是数组名 这个符号的值是那块内存的首地址,而是数组名 这个符号本身就代表了首地址这个地址值,它就是这个地址。这就是数组名 属于符号常量的意义所在。由于数组名 是一种符号常量,它是一个右值,而指针,作为变量,却是一个左值,一个右值永远都不是左值,那么,数组名 永远都不会是指针!
对于这段话我是这么理解的:数组名 在经过编译之后将变成一个数值,这个数值就是该数组的首地址。由于数组名 是一个地址,那么把它赋给一个指针变量也就不足为奇了。
例如有定义
char a[14];
char * p;
char * q;
void foo(char * pt)
{
};
考虑以下几种赋值:
p=a;// 合法,将一个地址赋给一个指针变量;
q=p;// 合法,将一个指针变量的值赋给另一个指针变量;
a=p;// 非法,a 是数组名 即地址,不是一个变量,不可被赋值(也就是上文中说的"数组名 是右值" )
再看这几种调用:
foo(a);// 将一个地址作为参数传入函数,函数中用一个指针变量接收这个地址值
foo(p);// 将一个指针变量的值传入函数(也是一个地址),函数中用一个指针变量接收这个地址
可以看出许多时候数组名 和指针可以等同地看待,而c 也把它们看作是兼容的类型对待,这就是为什么我那个错误的声明不被编译器在语法检查的时候“ 喀嚓” 的原因。
关于extern 的作用,许多地方都有说明,例如可以在c++ 里进行c 格式函数的声明,可以声明一个变量或函数是外部变量或外部函数;我们这里要讨论的是外部变量的声明。被extern 修饰的全局变量不被分配空间,而是在连接的时候到别的文件中通过查找索引定位该全局变量的地址。
有了这些基础后,我们现在正式开始研究extern 数组和extern 指针的问题:
首先在一个.c 文件中有如下定义:
char a[]={1,2,3,4};
分析:这是一个数组变量的定义,编译器将为这个数组分配4 字节的空间,并且建立一个索引,把这个数组名 、数组类型和它被分配的空间首地址对应起来。它被编译之后生成一个中间文件
然后我们在另一个.c 文件中分别以不同的形式进行声明:
(1) extern char a[];
分析:这是一个外部变量的声明,它声明了一个名为a 的字符数组,编译器看到这个声明就知道不必为这个变量分配空间,这个.c 文件中所有对数组a 的 引用都化为一个不包含类型的标号,具体地址的定位留给连接器完成。编译完成之后也得到一个中间文件,连接器遍历这个文件,发现有未经定位的标号,于是它搜 索其他中间文件,试图寻找到一个匹配的空间地址,在此例中无疑连接器将成功地寻找到这个地址并将此中间文件中所有的这个标号替换为连接器所寻找到的地址, 最终生成的可执行文件中,所有曾经的标号都应当已经被替换为地址。这是一个正常工作过程,连接出来的可执行文件至少在对于该数组的引用部分将工作得很好。
(2) extern char * a;
分析:这是一个外部变量的声明,它声明了一个名为a 的字符指针,编译器看到这个声明就知道不必为这个指针变量分配空间,这个.c 文件中所有对指针a 的引用都化为一个不包含类型的标号,具体地址的定位留给连接器完成。编译完成之后仍然得到一个中间文件,连接器遍历这个文件,发现有未经定位的标号,于是它搜索其他中间文件,试图寻找到一个匹配的空间地址,经过一番搜索,找到了一个分配过空间的名为a 的地方(也就是我们先定义的那个字符数组),连接器并不知道它们的类型,仅仅是发现它们的名字一样,就认为应该把extern 声明的标号连接到数组a 的首地址上,因此连接器把指针a 对应的标号替换为数组a 的首地址。这里问题就出现了:由于在这个文件中声明的a 是一个指针变量而不是数组,连接器的行为实际上是把指针a 自身的地址定位到了另一个.c 文件中定义的数组首地址之上,而不是我们所希望的把数组的首地址赋予指针a (这很容易理解:指针变量也需要占用空间,如果说把数组的首地址赋给了指针a ,那么指针a 本身在哪里存放呢?)。这就是症结所在了。所以此例中指针a 的内容实际上变成了数组a 首地址开始的4 字节表示的地址(如果在16 位机上,就是2 字节)。本例中指针a 的初值将会是0x0a090807 (little endian ),显然不是我们的期望值,所以运行会出错也就理所应当了。
?
几点细节:我们发现,使用extern 修饰的变量在连接的时候只找寻同名的标号,不检查类型,例如如果我们定义的甚至不是一个变量而是一个全局的函数,比如去掉定义
char a[]={....};
代之以
void a(){};
连接器居然也会连接通过。
实例如下:
比如在 a.c 文件中有这样一段代码
int g_i[] = {1, 2, 3, 4};
extern void testdotp();
void main( void )
{
int i = 0;
int num = 0;
num = sizeof (g_i) / sizeof ( int );
for (i = 0; i < num; i++)
{
printf( "g_i[%d] = %d " , i, g_i[i]);
}
printf( "/n" );
testdotp();
}
而在 b.c 中的代码如下:
extern int *g_i;
void testdotp()
{
printf( "*(&g_i + 2) = %d/n" , *(&g_i + 2));
printf( "&g_i = %d/n" , &g_i);
printf( "&g_i + 1= %d/n" , &g_i + 1);
printf( "g_i = %d/n" , g_i);
printf( "g_i + 1 = %d/n" , g_i + 1);
}
运行结果为
g_i[0] = 1 g_i[1] = 2 g_i[2] = 3 g_i[3] = 4
*(&g_i + 2) = 3
&g_i = 4344368
&g_i + 1= 4344372
g_i = 1
g_i + 1 = 5
分析如下 :
因为 b.c 文件中 g_i 变量的地址是 a.c 文件中 g_i 数组的首地址,故 g_i 的值为 g_i[0] 的值, &g_i 的值为 g_i 地址的首地址。
而 *(&g_i + 2) 的值: &g_i 的值为 g_i 数组的首地址, (&g_i + 2) 就为数组 g_i 第 3 个元素的地址, *(&g_i + 2) 就为第 2 个元素的值,即 3 。
&g_i + 1 :由于 &g_i 的值为 g_i 数组首地址,由于在 32 位机上运行,故 &g_i + 1 在 &g_i 基础上加上 4 个字节
g_i + 1 :由于 g_i 是一个指针变量, g_i 变量内存放的是地址,又因为 g_i 的值为 1 ,而 g_i + 1 就为 1 + 4 的单元的内存空间( 32 位机上),故 g_i + 1 为 5 。
转自:http://blog.csdn.net/hxg130435477/archive/2009/03/21/4012686.aspx
相关文章:

Java项目:医院管理系统(java+javaweb+jdbc+Mysql+lw)
源码获取:博客首页 "资源" 里下载! 功能介绍: 登录、注册、用户/管理员(角色)、用户信息管理、科系信息管理、查看所有科系、新增科系信息、删除指定科系、修改科系信息、病房信息管理、病人信息管理、医生类型管理、病人手术管理…

Mongodb地理空间索引
1、索引: 建立索引既耗时也费力,还需要消耗很多资源。使用{"bakckground":true}选项可以使这个过程在后台完成,同时正常处理请求。如果不包括background 这个选项,数据库会阻塞建立索引期间的所有请求。阻塞的做法会让索…

Juniper的路由器、防火墙、交换机如何恢复出厂配置
Juniper的路由器、防火墙、交换机如何恢复出厂配置有些时候,在正常的业务使用中,逐条删除配置的内容很繁琐,我们可以使用恢复出厂配置,清空设备中的配置;还有的时候,由于设备配置异常,可以使用恢…

【转载】标准输入输出、错误输出、重定向标准输出
【转载】标准输入输出、错误输出、重定向标准输出 原文:标准输入输出、错误输出、重定向标准输出 再来看看 >& 操作符: 重定向操作符描述 > 将命令输出写入到文件或设备(如打印机),而不是命令提示符窗口或…

(Interrupt Latency) 中断延迟
中断延迟 (Interrupt Latency) 中断延迟 是指从硬件中断发生到开始执行中断处理程序第一条指令之间的这段时间。 也就是: 计算机接收到中断信号到操作系统作出响应,并完成换到转入中断服务程序的时间。 不严格地,也可以表述为:…

Java项目:干活管理系统(java+SSM+Jsp+Mysql)
源码获取:博客首页 "资源" 里下载! 前台用户和后台管理员两种角色: 前台用户功能有:发布兼职、发布帖子、查看公告、个人中心、投诉等。 后台管理员功能有:用户管理、兼职管理、帖子管理、聊天管理、广告管…

20135234mqy 实验四
北京电子科技学院(BESTI) 实 验 报 告 课程:java程序设计 班级:1352 姓名:mqy 学号:20135234 成绩: 指导教师:娄嘉鹏 实验日期:2015.6…

我的第一张地图报表
一直以来对于地图都很陌生,感觉好强大,可以根据地理位置去分析数据,下面我简单的做了一张地图展示数据的报表。 接下来就简单的说一下设计过程和应该注意的地方 效果:(鼠标放在省份区域上,显示该省的数据&a…

SpringBoot第十篇:thymeleaf详解
作者:追梦1819 原文:https://www.cnblogs.com/yanfei1819/p/10931435.html 版权声明:本文为博主原创文章,转载请附上博文链接! 引言 SpringBoot 对 Web 的支持,官方推荐的是模板引擎 thymelaf。本章中&…

嵌入式系统开发过程中遇到的——volatile
嵌入式 系统开发过程中遇到的—— volatile 对于不同的计算机体系结构,设备可能是端口映射,也可能是内存映射的 。如果系统结构支持独立的 I/O 地址空间,并且是端口映射,就必须使用汇编语言完成实际对设备的控制ÿ…

薏米红豆粥的功效和实践演示
熬薏米红豆粥有很多技巧和讲究。薏米很硬,红豆也很硬,假设已经煮在锅里,大概熬一个多小时不坏,这是一种浪费火灾或电力,它甚至可以把水烧开,原因症结。我建议的方法有两种:第一种方法是在锅里加…

Java项目:财务预算管理系统(java+SSM+Jsp+Mysql+Layui+Maven)
源码获取:博客首页 "资源" 里下载! 一、项目简述 功能包括:实现公司对项目的管理。 二、项目运行 环境配置: Jdk1.8 Tomcat8.5 mysql Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持) …

POJ 3080 多个串最长公共子序列
求多个串最长公共子序列,字典序最小输出。枚举剪枝kmp.比较简单,我用find直接查找16ms #include<iostream> #include<string> #include<algorithm> using namespace std; string s[61]; int main() {int ta;cin>>ta;int n;while…

HDU 2561 第二小整数
2019-05-27 18:07:06 加油,坚持!!! 水题 #include <bits/stdc.h> using namespace std; int a[100]; int main() {int t;scanf("%d", &t);while(t--){int n;scanf("%d", &n);for (int i 0; i …

Java项目:在线高中考试系统(java+SSM+Jsp+Mysql+Maven)
源码获取:博客首页 "资源" 里下载! 项目分为前台和后台,前台主要为学生角色、后台主要为管理员角色。 管理员添加试题和发布试卷,学生负责在线考试、在线查看成绩和错题记录列表等。 管理员功能有:年级管理…

asp.net mvc 学习
Routing讲解: http://www.cnblogs.com/wangiqngpei557/p/3379095.html Filter讲解: http://www.cnblogs.com/ymnets/p/3452407.html ASP.NET MVC 支持以下类型的操作筛选器: 授权筛选器。 这些筛选器用于实现 IAuthorizationFilter 和做出关于…

Linux数据库性能优化--文件系统相关优化
实际也中也用到下文中所说的内存文件系统1、ramfs 记得是32位文件系统安装oracle 为oracle分配SGA突破1.7G大小限制2、mmap 的文件可以放在tmpfs挂载的文件系统中http://www.ibm.com/developerworks/cn/linux/management/tune/index.html1. 引言实践证明Lin…
jQuery Mobile的学习时间bottonbutton的事件学习
版权声明:本文为博主原创文章。未经博主同意不得转载。https://blog.csdn.net/xmt1139057136/article/details/27700521 程序猿都非常懒,你懂的! 生命的绝唱来机仅仅争朝夕,如诗的年华更需惜时如金。不要让今天的懈怠成为一生的痛…

C++中 public,protected, private 访问标号小结
第一:private, public, protected 访问标号的访问范围。 private: 只能由1.该类中的函数、2.其友元函数访问。 不能被任何其他访问,该类的对象也不能访问。 protected: 可以被1.该类中的函数、2.子类的函数、以及3.其友元函数…

Java项目:学生管理系统(java+Springboot+Maven+mybatis+Vue+Mysql)
源码获取:博客首页 "资源" 里下载! 一、项目简述 本系统功能包括: 学生管理,教师管理,课程管理,成绩管理,系统管理等等。 二、项目运行 环境配置: Jdk1.8 Tomcat8.5 M…

UVA 11752 超级幂
UVA 11752 超级幂 Z - The Super PowersTime Limit:1000MS Memory Limit:0KB 64bit IO Format:%lld & %llu Submit Status Practice UVA 11752Description 题意:定义一个数为超级幂,当这个数能表示成至少两个不同数字的幂时。如162^4&#x…

Awstats
c是一个非常简洁而且强大的统计工具。它可以统计您站点的如下信息:一:访问量,访问次数,页面浏览量,点击数,数据流量等精确到每月、每日、每小时的数据二:访问者国家、访问者IP、操作系统、浏览器…

fixture详细介绍-作为参数传入,error和failed区别
前言 fixture是pytest的核心功能,也是亮点功能,熟练掌握fixture的使用方法,pytest用起来才会得心应手! fixture简介 fixture的目的是提供一个固定基线,在该基线上测试可以可靠地和重复地执行。fixture提供了区别于传统…

哈佛结构和冯诺依曼结构区别。
哈佛结构是一种将程序指令存储和数据存储分开的存储器结构。中央处理器首先到程序指令存储器中读取程序指令内容,解码后得到数据地址,再到相应的数据存储 器中读取数据,并进行下一步的操作(通常是执行)。程序指令存储和…

js 数据函数
//shift:删除原数组第一项,并返回删除元素的值;如果数组为空则返回undefined var a [1,2,3,4,5]; var b a.shift(); //a:[2,3,4,5] b:1 //unshift:将参数添加到原数组开头,并返回数组的长度…

Java项目:平行志愿管理系统(java+Springboot+Maven+mybatis+Vue+Mysql)
源码获取:博客首页 "资源" 里下载! 一、项目简述本系统功能包括: 系统管理,招生计划,学生管理,录取结果,自动分配,调剂管理等等。 二、项目运行 环境配置: J…

冒泡 MS Azure 不便宜。。。
一直在等 MS Azure 中国开卖, 最近有消息说正式商用了。。。 看看去,ok 发现官方网站 很奇葩。没有购买的地方 说毛线 啊 卧槽 欺骗感情还是吊人胃口? 好看了一下VM的价格,卧槽真不便宜。 即使是 768 MB的也要 0.22RMB 小时。本来…

数据库的概念以及MYSQL的安装和卸载
一、数据库的基本概念: 1、什么是数据库? DataBase,简称DB。是用来存储和管理数据的仓库。 2、数据库的特点: 持久化存储数据的。其实数据库就是一个文件系统。方便存储和管理数据使用了统一的方式操作数据库——SQL 3、最热门…

对ARM异常(Exceptions)的理解
对ARM异常(Exceptions)的理解 1 .对 ARM 异常( Exceptions )的理解 所有的系统引导程序前面中会有一段类似的代码,如下: .globl _start ;系统复位位置 _s…

Java项目:花店商城系统(java+Springboot+Maven+mybatis+Vue+Mysql)
源码获取:博客首页 "资源" 里下载! 一、项目简述 本系统功能包括: 商品的分类展示,用户的注册登录,购物车,订单结算,购物车加减,后台商品管理,分类管理&#x…