第2讲 - C语言关键字(2)

今天,我们介绍两个关键字,extern和sizeof

我们先提出一个问题?

假如我们创建两个源文件,一个叫做test.c,另一个叫做main.c,那么我们在test.c中定义的函数show()是否可以在main函数中进行调用?

答案是可以的,如下图

这个是我们创建的test.c文件,这里面的代码表示我们现在test.c文件夹中定义函数,然后我们在main.c文件中进行调用

我们生成解决方案:

 可以看到有些警告提醒我们的函数show未定义,我们进行运行试试

可以发现,函数成功的调用了,所以得出结论,不同的源文件,在一个文件中定义的函数,在另一个文件中可以调用(虽然有警告)

这时候我们有个疑问:对于变量来说是否同样适用呢?

答案是错误的,如图所示

 在test函数中,我们这样新创建一个变量g_val,初始化值为100,我们在main函数中打印这个变量,看看结果会是如何?

 

 可以发现,在main函数中,很明显地出现下划线错误提示,表示我们的g_val未定义

得出结论:在一个源文件中定义的变量,在另一个源文件中是无法使用这个变量的

那么有没有方法能够使用这个呢?

可以,我们这个时候引入extern函数

 extern表示声明整型 g_val,我们进行调用

 我们可以发现,成功打印了变量。

那这个写法行吗?extern int g_val=100;

我们进行运行,结果如图

 为什么不行呢?我们进行解释

答:因为=号就相当于初始化或赋值,初始化或者赋值都会创建空间,但是我们的extern是申明,所以并不会创建空间,所以不能有等号

总结:所有的变量在初始化的时候,不能有初始值!

假如我们有很多源文件,这些源文件都需要调用我们在test.c中创建的变量和函数,那是不是我们要在每一个源文件中都需要进行申明,这是不是太复杂了

总结:单纯的使用源文件,组织项目结构的时候,项目越大越复杂的时候,维护所需要的成本就越高

有没有一种方法能够单独创建一个文件,想要调用某个变量或者函数,在文件中进行申明即可,这时候我们就引入了头文件

头文件:组织项目结构的时候,减少大型项目的维护成本问题.

源文件中全部是定义,头文件中全部是声明

大部分头文件都是被多个源文件所包含

可能会产生一个问题:头文件被重复包含的问题

如何解决这个问题呢?

答:在代码最前面加上#pragma once

头文件中都可以包含哪些代码呢?

答:1:c头文件

2:所有的变量的声明

3:所有的函数的声明

4:#define宏定义 typedef  结构体

例如,我们首先在头文件中写出我们所要写出的代码

 然后我们在源文件中进行声明,

 注意,引用头文件时需要用#include"xxx"的形式

如何正确的使用头文件呢?

答:在test.c源文件中,我们一般写的代码是变量的定义和函数的定义,如图所示

int g_val = 100;
void show()
{
	printf("hello,show\n");
	return 0;
}

在test.h头文件中,我们一般写的代码是声明还有一些必要的头文件,为了避免头文件被重复包含的问题,我们还要在头文件的最前面加上#pragma once,代码如图所示

#define _CRT_SECURE_NO_WARNINGS
#pragma once

#include<stdio.h>
#include<Windows.h>
extern int g_val;

在main.c源文件中,我们应该首先声明我们自己创建的头文件test.h,例如#include"test.h",注意要用引号,我们在main.c的源文件的主体则是我们所构建的main函数,例如打印变量和调用函数,如图所示

#include"test.h"

int main()
{
	printf("%d\n", g_val = 100);
	show();
	system("pause");
	return 0;
}

我们运行代码,结果如图所示

 但我们的解决方案中是有报警告的

说我们的show函数未定义,

但是我们依旧成功运行了代码,原因是什么?

答: 首先,我们的代码进行编译,这时候,我们的两个源文件之间是独立的,所以我们的show函数是未定义的,程序进行报警,但是在我们链接的过程中,多个文件整合成一个文件,这时候我们的程序如果能找到show函数的定义式,那么报警作用也就自动消除了

这里我们得到一个结论:在不同的源文件中,变量是必须要声明的,函数是可以不声明的,但是函数不声明的情况下在部分编译器下容易报错,要想不产生报错信息,函数的声明是必需的,如何进行函数的声明呢?

答:函数的声明结构包括返回值类型,函数名,形参列表,要注意是没有函数体的,原因是:函数体其实和变量的初始化或赋值类似,变量的初始化或者赋值,会开辟空间,存储数据,而函数体也会开辟空间,存储的是代码,这个代码通常情况下是不会被写入的。因为不管是变量的声明,还是函数的声明,其结果都不会开辟空间,所以我们不可以带上函数体。理论是只要是声明,我们都可以把我们的extern带上,所以函数的声明结构如图所示

extern void show();

总结:问题1,头文件的意义是什么?

答:为了便于我们的项目组织和维护项目

问题2:头文件的组成结构是什么?

答:首先,#pragma once,为了防止头文件被重复包含

2:main函数中对应的库函数的头文件,例如#include<stdio.h>

3:我们要在源文件中使用的变量和函数的声明

问题3:函数的声明和变量的声明是否都需要在前面加上extern

答:变量的声明必须加上extern,函数的声明建议加上extern

假如我们都不加extern,

 我们进行运行,可以发现程序正常运行

 

 并且解决方案中没有警告

 那是不是就说明函数和变量的声明都可以不加extern,答案是错误的,原因如下

答:int g_val;这串代码是参数的声明还是参数的定义?说声明的话也很对,说定义的话,只不过没有初始化,可以发现,这种情况容易混淆,所以我们变量的声明必须加上extern

函数的声明可加可不加,原因是函数的声明和定义的区别主要是函数体的有无决定的,函数的定义有函数体,函数的声明没有函数体

函数的声明和定义以及调用要尽量保持一致

接下来,我们两个问题

问题1:全局变量可以跨文件访问吗?

答:可以

问题2:函数可以跨文件访问吗?

答:可以

在具体的应用场景,有没有可能我们不想让全局变量或者函数跨文件访问,而只想在本文件内部被访问

例如

 我们在test.c的变量创建的前面加上static,我们进行运行

 可以发现,出现错误,并且这里的错误是链接型错误,证明我们在链接的过程中没有找到对应的g_val变量

结论1:static修饰的全局变量,只能在本文件内部被访问,不能被外部文件直接访问

当我们用static修饰函数时

 我们进行运行

 可以发现,依旧产生错误

结论2:static修饰的函数,只能在本文件内部被访问,不能被外部文件直接访问

static限制全局变量,限制的是全局变量的作用域

当static修饰局部变量的时候,我们该如何思考呢?

答:我们先写这样一串代码

 void fun()
{
	int i = 0;
	i++;
	printf("i=%d\n", i);
}
int main()
{
	for (int i = 0; i < 10; i++)
	{
		fun();
	}
	return 0;
}

这串代码打印的结果是什么呢?

答:

 为什么是10个1呢,因为我们的i变量是局部变量,局部变量出函数会自动释放,所以就一直打印1

我们用static修饰i呢?

 void fun()
{
	static int i = 0;
	i++;
	printf("i=%d\n", i);
}
int main()
{
	for (int i = 0; i < 10; i++)
	{
		fun();
	}
	return 0;
}

我们进行运行

 可以发现,结果是1-10,原因可能是局部变量i出函数并没有被销毁,我们进行验证

int*p = NULL;
void fun()
{
	static int a = 100;
	p = &a;
}
int main()
{
	fun();
	printf("%d\n", *p);
	return 0;
}

我们首先创建一个指针,这个指针是一个全局变量,我们用p接受被static修饰的局部变量a的地址我们在main函数中调用fun函数,正常情况下,我们的局部变量a出函数后就会直接释放,那么我们的*p访问的应该是乱码,我们进行运行

 我们可以发现,结果为100,所以局部变量并没有被释放

总结:static修饰局部变量时,更改的是局部变量的生命周期,如何改变的呢?把局部变量的生命周期改成全局变量的生命周期

那是不是static修饰局部变量直接把局部变量变成全局变量呢?

答:并不是,例如

void fun()
{
	static int a = 100;

}
int main()
{
	printf("%d", a);
	return 0;
}

如果a被更改为全局变量的话,打印的结果为100,我们进行运行发现

 代码直接报错,并表示

 a是未定义的标识符

所以总结:static修饰局部变量,能够更改局部变量的生命周期,而不能更改局部变量的作用域

 其中,局部变量是存放在栈区的,栈区的特点是具有临时性,遵循后进先出的规则

static修饰的局部变量是存放在全局区和静态数据区的,特点是在整个进程的运行的生命周期中,局部变量始终是有效的

 void fun()
{
	static int i = 0;
	i++;
	printf("i=%d\n", i);
}

这里的i只会初始化一次,也就是第一次

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值