C语言: static关键字总结、enum与#define的不同

static关键字总结(常考点)

首先提出我们这里要说明的问题:
1. static有哪几种使用方法。
2. static修饰的变量(全局、局部变量)在内存的哪个段里。
3. static修饰的函数与变量,被其他文件中以extern方式引用了会发生什么。

以上三个问题我将结合C语言与汇编指令进行解释说明(C语言里面的大部分疑惑只要通过看编译后的汇编指令,你都能搞懂)

1.static在C中的用法(C++请看侯捷老师的C++教程,这里不说C++的,不过本质上和C使用是一回事)
static修饰 全局变量、函数、局部变量
#include <stdio.h>
static int global_a;

static void anyFunction(); // 申明

static void anyFunction() // 定义
{
	printf("This is the anyFinction static defination.\n");
}
int main()
{
	volatile static int local_b = 0;
	volatile static int local_c;
	local_c = 1;
	volatile static int local_d;
	anyFunction();
	func1();
}
void func1()
{
	printf("%d\n", local_c);
}

输出内容: This is the anyFinction static defination.

先说结论:

static 无论修饰全局变量、局部变量还是函数,都是把目标链接时的可见性,限制在某个范围之内。
当修饰全局变量和函数时,是限制在当前.c文件之内,其他.c文件并不能访问,至于使用extern 能不能,后面会说明。
当修饰局部变量时,变量的链接时的可见性被限制在了函数内部。这点可以尝试以下代码证明。

#include <stdio.h>
static int global_a;
int main()
{
	volatile static int local_b = 0;
	volatile static int local_c;
	local_c = 1;
	volatile static int local_d;
	func1();
}
void func1()
{
	printf("%d\n", local_c);
}

编码时IDE就会提醒错误…,尝试编译链接:

error: ‘local_c’ undeclared (first use in this function);

2.static修饰的变量(全局、局部变量)在内存的哪个段里。

同样使用刚刚的代码, 用在线编译godbolt对以下代码进行编译(ARM GCC):

static int global_a;

volatile static void anyFunction(); // 申明

volatile static void anyFunction() // 定义
{
	// do_nothing;
	return;
}
int main()
{
	volatile static int local_b = 0;
	volatile static int local_c;
	local_c = 1;
	global_a = 1;
	volatile static int local_d;
	anyFunction();
}

其汇编指令:

anyFunction:
        push    {r7}
        add     r7, sp, #0
        nop
        mov     sp, r7
        ldr     r7, [sp], #4
        bx      lr
main:
        push    {r7, lr}
        add     r7, sp, #0
        movw    r3, #:lower16:local_c.5564
        movt    r3, #:upper16:local_c.5564
        movs    r2, #1
        str     r2, [r3]
        bl      anyFunction
        movs    r3, #0
        mov     r0, r3
        pop     {r7, pc}

贴图片算了:
在这里插入图片描述
!!!注意: 对local_c这个static修饰的局部变量,以及global_a这个static修饰的全局变量,他们的赋值操作使用的地址都不是基于sp做偏移,就证明了,被static修饰的变量占用的内存位于 程序的数据段,并非栈内存中(栈内存中的数据都是通过sp+偏移量访问的)。

于此同时,注意观察local_b,这个被static修饰的局部变量在函数的指令里面并未出现赋值等指令,也更加证明这一点,被static修饰的变量无论全局还是局部变量都位于数据段中,只不过,对于static修饰的变量如果有初始值且初始值不为0时,放入.data段,而初始值为0或无初始值,则放入.bss段。

这里再引出static修饰申明与修饰定义,当申明与定义一个有static一个没有static时,会发生什么

编译下面两段不同的代码

static int global_a;

void anyFunction(); // 申明不含static

static void anyFunction() // 定义包含static
{
	printf("This is the anyFinction static defination.\n");
}
int main()
{
	volatile static int local_b = 0;
	volatile static int local_c;
	local_c = 1;
	volatile static int local_d;
	anyFunction();
}

test1.c:6:13: error: static declaration of ‘anyFunction’ follows non-static declaration 报错了

static int global_a;

static void anyFunction(); // 申明含static

void anyFunction() // 定义不含static
{
	printf("This is the anyFinction static defination.\n");
}
int main()
{
	volatile static int local_b = 0;
	volatile static int local_c;
	local_c = 1;
	volatile static int local_d;
	anyFunction();
}

This is the anyFinction static defination. 没问题

总结: 为了避免这种差异,请在申明与定义都保持关键字的一致!!!!这是个好习惯。

3. static修饰的函数与变量,被其他文件中以extern方式引用了会发生什么。

test.c文件

#include <stdio.h>
volatile static void print()
{
    printf("This is a static function."__FILE__"\n");
}

test1.c文件

#include <stdio.h>
static int global_a;

static void anyFunction(); // 申明

extern volatile void print();
// extern volatile static void print();  // 这种写法也会报错

void anyFunction() // 定义
{
	printf("This is the anyFinction static defination.\n");
}
int main()
{
	volatile static int local_b = 0;
	volatile static int local_c;
	local_c = 1;
	volatile static int local_d;
	anyFunction();
    // print();
}

编译

 gcc -std=c11 -o test1.exe ./test1.c ./test.c

结果:

undefined reference to `print’
collect2.exe: error: ld returned 1 exit status

总结:
被static修饰的函数与变量,外部文件使用extern引用也没有用。

总结

OK,问题说完,static应该没啥问题了。C++中因为static在类中可用,然后再结合一些什么类的特性,就会变得很复杂,所以C++中的自己去看,不过你忽视掉C++中的各种类特性,只关注被static修饰后的函数或数据放在哪个段,可能更好理解,因为这些所谓的作用域限制,只是在编译链接阶段,编译器作出的限制,其实,只要知道了变量或函数的起始地址,通过指针都能操作,哪怕是static修饰了的。因为在汇编中没有这些概念,汇编中只要有地址,随便操作。

2. enum 与 #define的不同

这个就记一点,#define 是在预处理阶段通过文本替换,直接把内容粘贴到文件中,而enum是语言特性,是在编译阶段,编译器识别的。

  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值