extern和static、#define和typedef

定义和声明

(A)int i;
(B)extern int i;
那个是定义?那个是声明?

什么是定义?(A)

  • 所谓定义就是(编译器)创建一对象,为这个对象分配一块内存并给它起一个名字,这个名字就是我们经常说的变量名或对象名
  • 一个变量或对象在一定区域内(比如函数内、全局等)只能被定义一次,如果被定义多次,编译器会提示你重复定义同一个变量或对象。

什么是声明?(B)

  • 第一重含义:告诉编译器,这个名字已经匹配到一块内存上了,下面用到的变量或对象是在别的地方定义的。声明可以出现多次。
  • 第二重含义:告诉编译器,这个名字已经被使用了,别的地方不能用它作为变量名或对象名。

记住:定义和申明最重要的区别是:定义创建了对象并为这个对象分配了内存,声明没有分配内存。

extern

C中的关键字extern是用来做变量声明的, 声明extern关键字的全局变量和全局函数可以使得它们的作用域得到扩展

extern修饰全局变量

  • extern在一个文件内扩展全局变量的作用域(这种情况要发挥其作用的话不能被省略)

在C中, 如果全局变量不在文件开头定义, 其有效作用范围(作用域)只限于全局变量定义处到文件结束, 在定义处之前不能使用该变量 ,如需在定义处之前想用该全局变量的话, 需要在用之前用extern对该变量进行外部变量声明, 表示将此变量的作用域扩展到此位置, 这样就合法了
在这里插入图片描述

将全局变量的作用域扩展到其他文件(可以省略, 一般不写)

一个C程序可以由一个或多个源文件组成, 如果由多个组成, 那么在想在一个文件用另一个文件中定义的全局变量怎么办呢?

  • 如果在两个文件中都定义名字相同的变量, 会在连接时出现重复定义的错误, 正确的做法是, 在任意一个文件中在全局变量前加上extern修饰, 这样实际只定义了一个全局变量, 只是将其作用域扩展到了其他文件。
    在这里插入图片描述
    在text1.c中用extern声明的text2.c中变量a,则text1.c和text2.c中的a是地址相同的同一个变量,所以无论在
    text1还是text2中改变a, a的值都会a发生改变

extern修饰函数(可以省略,一般不写)

在声明函数时, 在函数最左端加关键字extern, 此函数就成为了外部函数, 可供其他文件调用(与之对应的是内部函数, 用staic修饰, 文章下面再来看)
在这里插入图片描述

在一个工程中

在这里插入图片描述

static

注意: 在用static声明变量时, 只能在声明变量的基础上加static修饰, 而不能单独使用, 例如 下面用法不对
int a;
static a;
在C中和static类似不能这样用的关键字还有auto ,register

static修饰的变量只在当前文件可见,不同文件staic 修饰相同名字的变量时,变量的地址是不同的
static修饰的变量是不会放在符号表的,是不会给其他文件使用的

修饰全局变量, 将全局变量的作用域限制在本文件

static修饰全局变量改变的是全局变量的作用域, 使全局变量只能在本文件中使用
在这里插入图片描述
运行结果出错, 可以看到static确实把a的作用域限定到了test.c中

修饰局部变量, 改变局部变量的生命周期(将局部变量存储在静态区)

static修饰局部变量, 会将局部变量存储在静态区(全局区)(普通局部变量存储在栈区), 改变局部变量的生命周期, 使其伴随整个程序, 程序运行结束时才会被释放
注意 : 虽然static修饰的局部变量改变了生命周期, 使其伴随整个程序, 程序结束前为其分配的空间一直都在, 但作用域并未改变

在这里插入图片描述

static修饰函数

在声明函数时在其最左边加static修饰, 这个函数就成了内部函数, 内部函数又称为静态函数, 会使函数作用域只限定于本文件., 这样在不同文件中就算有相同名字的函数也不会相互干扰

通常一个大的程序往往由多人分工来编写不同的文件模块, 在不同人编写代码时,很可能会出现相同名字的函数, 此时, 在自己写的函数前加static修饰, 就不会出现函数重名的问题了, 这就保证了程序的可靠性.

#define与typedef

#define是预处理指令,在编译时不进行任何检查,只进行简单的替换.
typedef是给一个存在的数据类型(注意:是类型不是变量)去一个别名,而非定义一个新的数据类型。

区别

(1)#define之后不带分号,typedef之后带分号。

(2)#define可以使用其他类型说明符对宏类型名进行扩展,而 typedef 不能这样做。如:

#define INT32 int
unsigned INT32 n;  //没问题
typedef int INT32;
unsigned INT32 n;  //有问题

(3)在连续定义几个变量的时候,typedef 能够保证定义的所有变量均为同一类型,而 #define 则无法保证。如:

#define PINT1 int*;
P_INT1 p1,p2;  //即int *p1,p2;
typedet int* PINT2;
P_INT2 p1,p2;  //p1、p2 类型相同

PINT1定义的p1与p2类型不同,即p1为指向整形的指针变量,p2为整形变量;PINT2定义的p1与p2类型相同,即都是指向 int 类型的指针。

(4)typedef 和 const 放在一起容易出现的错误

typedef struct student
{
	//code
}Stu,*pStu;

(A) struct student s1;  和  Stu s1;  没有区别
(B) struct student* s2; 和  Stu* s2;  和  pStu s2; 没有区别

(C)const pStu s3; 
(D)pStu const s4;
可能有的人会认为(C)const 修饰的是s3指向的对象。(D)const修饰s4这个指针。
很遗憾(C)const修饰的是s3这个指针,因为 pStu 是 " struct student{ //code } *"的别名。
" struct student{ //code } *"是一个整体,对于编译器来说,只认为 pStu 是一个类型名,
所以在解析的时候很自然的把 "pStu"这个数据类型忽略掉。
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值