static 和 extern 关键字

文章详细解释了C语言中static关键字如何修饰局部变量、全局变量和函数,以及它如何影响生命周期和作用域。同时介绍了extern关键字的作用,以及如何正确地使用extern声明和externC以解决不同编译器间的链接问题。
摘要由CSDN通过智能技术生成

前言

一、static是什么

二、static的用法

1.修饰局部变量

2.修饰全局变量

3.修饰函数

三、extern 关键字

前言

static在不同的语言中又有不同的使用方法,本文介绍的是static在C语言当中的用法。

一、static是什么

static在C语言中是关键字,中文直译过来是“静态的”,static关键字在C语言中主要是用来修饰变量和函数。

二、static的用法

1.修饰局部变量
——称为静态局部变量

static修饰局部变量时,会改变局部变量的存储位置,从而使得局部变量的生命周期变长。

我们用一段代码来进行详解:

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

void test()
{
	int num = 1;
	num++;
	printf("%d ", num);
}

int main()
{
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		test();
	}
	return 0;
}

请添加图片描述

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

void test()
{
	static int num = 1;
	num++;
	printf("%d ", num);
}

int main()
{
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		test();
	}
	return 0;
}

请添加图片描述
原因是:
static修饰了局部变量num,令局部变量um变成静态的,使得每次test()函数结束时局部变量x都不销毁,再次进入test()函数时则保留原有数值运行,因此num++数值越来越大。
注意:static修饰局部变量只改变生命周期,不改变作用域!

2.修饰全局变量
——称为静态全局变量

static修饰全局变量时,会改变全局变量的链接属性,从而使得全局变量的作用域变小。

*** 这段话什么意思呢,先来简单说说全局变量。***

全局变量的作用域十分的广,只要在一个源文件中定义后,这个程序中的所有源文件、对象以及函数都可以调用,生命周期更是贯穿整个程序。文件中的全局变量想要被另一个文件使用时就需要进行外部声明(以下用extern关键字进行声明)。

接着我们用代码进行解析:

请添加图片描述
当我加上static 关键字后,出现了报错:
请添加图片描述
原因:
会发现生成错误,无法解析外部符号num,全局变量num不能被源.c文件调用了。所以我们可以很容易看出static修饰后让全局变量num的作用域变小了,令全局变量num无法被其他文件调用。

其本质是:

全局变量本身是具有外部链接属性的,在A文件中定义的全局变量,在B文件中可以通过【链接】来使用;
但如果全局变量被static修饰,那这个外部链接属性就会被修改成内部链接属性,此时这个全局变量就只能在自己的源文件中使用;

3.修饰函数
——称为静态函数

static对函数的修饰与修饰全局变量十分相似,修饰函数时会改变函数的链接属性,从而使得函数的作用域变小。

请添加图片描述
当我使用了extern关键字后:
请添加图片描述
当我使用了static关键字后:
请添加图片描述
请添加图片描述
会发现Add函数无法被解析了,static修饰后Add函数的作用域变小了。

本质和全局变量很像:
函数本身也是有外部链接属性的;
被static修饰后,函数的外部链接属性被修改成内部链接属性,使得这个函数只能在自己的源文件内被使用,因此函数的作用域就变小了。

三、extern 关键字

extern关键字会提升变量或者函数的逼格,使得它们可以跨文件被访问。话虽然是这么说,使用这个关键字必须要注意一些东西。

首先,你得在c文件里面实现这些全局变量和全局函数,这是最基本的,然后只需要在需要用到这些变量和函数的文件里声明一下,用extern修饰声明,这样弄完之后就可以随意使用这些全局变量和全局函数了。请不要为编译器担心,担心它们找不到这些东西,只要你实现了,不怕编译器找不到。

在别的文件里写一堆的extern声明显得特别不专业,也显得代码十分臃肿,有没有好的做法呢?有的。
用法示例如下:
/Demo.h/

#ifndef _DEMO_H_ //保证头文件只被包含一次
#define _DEMO_H_
 
extern int a;
extern int b;
int add(int a, int b);
 
#endif
/**
//下面的写法也可以
#pragma  once   //保证头文件只被包含一次
extern int a;
extern int b;
*/

/Demo.cpp/

#include "Demo.h" 
int a = 10;
int b = 20;
 
int add(int l, int r)
{
    return l + r;
}

/main.cpp/

#include "Demo.h"
#include <iostream>
using namespace std;
 
void main()
{
    cout << "a = " << a << ", b = " << b << endl;
 
    int c = add(1, 2);
    printf("c = 1 + 2 = %d \n", c);
    system("pause");
}

运行结果如下:
请添加图片描述
这样处理之后只需要到用到的地方加入#include"Demo.h"一句即可,很方便吧!这么干方便了这些变量和全局函数的管理。
下面是关于extern关键字的一些说明:

(1)extern声明全局变量

在A.cpp中使用B.cpp中的变量,需要extern声明。定义的形式类似于:
extern int a;
这里需要注意定义和声明的区别,extern int a = 10;属于定义了。

/Demo.h/

#ifndef _DEMO_H_
#define _DEMO_H_
 
extern int a = 10;//应该只保留声明
extern int b = 20;//应该只保留声明
int add(int a, int b);
 
#endif
/**
//下面的写法也可以
#pragma  once
extern int a;
extern int b;
*/

/Demo.cpp/

#include "Demo.h"
int a = 10;
int b = 20;
 
int add(int l, int r)
{
    return l + r;
}

/main.cpp/

#include "Demo.h"
#include <iostream>
using namespace std;
 
void main()
{
    cout << "a = " << a << ", b = " << b << endl;
 
    int c = add(1, 2);
    printf("c = 1 + 2 = %d \n", c);
    system("pause");
}

这么干肯定是编译不通过的,因为存在重定义,#include"Demo.h"这一句是单纯的代码替换,在Demo.cpp和main.cpp里替换之后你自然发现全局变量被定义了两次,肯定会报错。一句话,声明可以拷贝n次,但是定义只能定义一次。(声明不分配内存空间,定义会分配内存空间,同一个变量不应该被分配多个内存空间)

(2)extern声明外部函数

在A.cpp中使用B.cpp中的函数,需要extern声明。
extern关键字的作用是告诉编译器,在某个cpp文件中,存在这么一个函数/全局变量。
函数的声明类似于:
extern int sum(int, int);
函数的声明语句中,关键字extern可以省略,因为全局函数默认是extern类型的,因此也就出现了Demo.h中的状况。
虽然用extern声明了变量和函数之后,在别的文件里就随意使用了,但是你也千万别忘了书写变量和函数的实现,否则就会出现无法解析的外部符号,编译无法通过。
还有一件很有趣的事情,如果你将前面的Demo.cpp改为Demo.c的话,编译器会告诉你说找不到变量和函数是无法解析的外部符号,怎么办呢?
之所以会出现这样的原因,是因为c++编译器和c编译器差异性的问题,C++语言在编译的时候为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,因此会造成链接时找不到对应函数的情况,我们是在main.cpp文件里包含了Demo.h也就是在main.cpp里面声明了这么一些变量和函数(代入即可),main.cpp里面的声明会被c++编译器成处理中间的名称,而Demo.c里面的实现会被c编译器处理,两者处理的差异性(详情可以看我后面提供的链接),导致问题多多。总之处理之后.c文件的实现和.cpp里面的声明不一致了,这也是导致找不到的原因,这个时候就必须用extren "C"了,告诉c++编译器,请保持我的名称,不要给我生成用于链接的中间函数名。
我们将Demo.h里面的东西改一下写法即可:

/Demo.h/

#ifndef _DEMO_H_
#define _DEMO_H_
 
#ifdef  __cplusplus
extern "C" {
#endif
 
    extern int a;
    extern int b;
    int add(int a, int b);
 
 
#ifdef  __cplusplus
}
#endif
 
#endif

/Demo.c/

//#include "Demo.h"
#include <stdio.h>
int a = 10;
int b = 20;
 
int add(int l, int r)
{
#ifndef __cplusplus
    printf("这是一个c程序!\n");
#endif // !_cplusplus
 
#ifdef __cplusplus
    printf("这是一个c++程序!\n");
#endif // !_cplusplus
    return l + r;
}

/main.cpp/

#include "Demo.h"
#include <iostream>
using namespace std;
 
void main()
{
#ifdef __cplusplus
    cout << "这是一个c++程序" << endl;
#endif
 
#ifndef __cplusplus
    cout << "这是一个c程序" << endl;
#endif
    cout << "a = " << a << ", b = " << b << endl;
 
    int c = add(1, 2);
    printf("c = 1 + 2 = %d \n", c);
    system("pause");
}

运行结果如上图,特别要声明一下__cplusplus,前面是两个_,在.c文件没有定义__cplusplus而cpp文件定义了__cplusplus,一不小心将__cplusplus写成了_cplusplus会出现什么呢?就是我下面的情况了。

/Demo.h/

#ifndef _DEMO_H_
#define _DEMO_H_
 
#ifndef  _cplusplus
extern "C" {
#endif
 
    extern int a;
    extern int b;
    int add(int a, int b);
 
 
#ifndef  _cplusplus
}
#endif
 
#endif

/Demo.c/

//#include "Demo.h"
#include <stdio.h>
int a = 10;
int b = 20;
 
 int add(int l, int r)
{
#ifndef _cplusplus
    printf("这是一个c程序!\n");
#endif // !_cplusplus
 
#ifdef _cplusplus
    printf("这是一个c++程序!\n");
#endif // !_cplusplus
    return l + r;
}

/main.cpp/

#include "Demo.h"
#include <iostream>
using namespace std;
 
void main()
{
#ifdef _cplusplus
    cout << "这是一个c++程序" << endl;
#endif
 
#ifndef _cplusplus
    cout << "这是一个c程序" << endl;
#endif
    cout << "a = " << a << ", b = " << b << endl;
 
    int c = add(1, 2);
    printf("c = 1 + 2 = %d \n", c);
    system("pause");
}

说实话,结果我挺震惊的,在Demo.c文件中定义的函数跑出一个“这是一个c程序”,也没什么,在一个cpp文件中的函数跑出一个“这是一个c程序”,我就很震惊了,原因我也不清楚,不过基本就是上面的解决方案了,最后说一下不在Demo.c包含#include"Demo.h"的原因,因为在extern “C”并不是为C所支持,其实也不算吧!
因为网上说.c文件没有定义_cplusplus而cpp文件定义了_cplusplus,如果真是这样在Demo.c包含#include"Demo.h"也不会出现问题,可是不知道是我的编译器原因还是什么的,c文件和cpp文件都没定义_cplusplus,因此有可能Demo.c包含#include"Demo.h"出现问题,因为为了能在cpp文件中使用,Demo.h文件里我一般要写成#ifndef_cplusplus,此时若Demo.c包含#include"Demo.h",恰好c文件中也没有定义_cplusplus,那么extern “C”就暴露出来了,自然会错。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值