精通C语言一图搞清C语言到底有多少种变量存储类别

Posted 从善若水

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了精通C语言一图搞清C语言到底有多少种变量存储类别相关的知识,希望对你有一定的参考价值。

本人就职于国际知名终端厂商,负责modem芯片研发。
在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。

C变量存储类别

下文涉及的存储期、作用域和链接概念不懂的同学可以参考《【精通C语言】深度解析C变量作用域、链接和存储期的含义》

存储类别存储期作用域链接声明方式
自动自动块内
寄存器自动块内,使用关键字register
静态外部链接静态文件外部所有函数外
静态内部链接静态文件内部所有函数外,使用关键字static
静态无链接静态块内,使用关键字static
动态内存分配调用free()函数之后销毁没有标准规则,由程序员管理没有标准规则,由程序员管理使用malloc等类似函数分配
静态线程本地内部链接整个线程执行期间文件内部使用关键字static _Thread_local
静态线程本地外部链接整个线程执行期间文件外部使用关键字_Thread_local定义性声明;extern _Thread_local引用性声明
静态线程本地无链接整个线程执行期间使用关键字static _Thread_local

下面我们具体介绍每种存储类别的含义

1️⃣  自动变量

       自动变量使用存储类别说明符auto声明,默认情况下声明在块或函数头中的任何变量都属于自动存储类别。块作用域和无链接属性意味着只有在所在的块中才能通过变量名访问该变量。另一个函数中可以使用同名的变量,但是这个变量是存储在不同内存位置上的另一个变量。
       变量的自动存储期意味着,程序在进入变量声明所在的块时变量存在,程序退出该块时变量消失。看下面的代码👇

int loop(int n)
{
	int m; //m的作用域
	scanf("%d",&m);
	{
		int i; //m 和 i 的作用域
		for(i=m;i<n;++i)
			puts("i is local to a sub-block\\n");
	}
	return m; //m的作用域,i已经消失
}

2️⃣  寄存器变量

       变量通常存储在计算机内存中。如果幸运的话变量存储在计算机寄存器中,或者说存储在计算机最快的内存中。由于寄存器变量存储在寄存器而非内存中,所以无法获取寄存器变量的地址。大多数情况下寄存器变量与自动变量一样。使用register关键字声明寄存器变量。
       我们上面说“幸运的话”意味着变量不一定能存储在寄存器中,因为编译器要衡量当前寄存器或最快可用内存的数量,如果数量不够的话就不会放入。

3️⃣  静态外部链接

       属于这个类型的变量称为外部变量。把变量的定义性声明放在所有函数的外面就创建了外部变量。为了指出该函数中使用了外部变量,可以在函数中使用关键词extern再次声明。如果一个源代码文件使用的外部变量定义在另一个源代码文件中则必须显示使用关键字extern声明外部变量。如下面的code👇

int Errupt;       /*外部定义的变量*/
double Up[100];   /*外部定义的数组*/
extern char Coal; /*如果Coal被定义在另一个文件中,则必须这样声明*/

void next(void)
{
	......  /*并未在next()中声明变量Errupt和数组Up但是仍然可以使用*/
}

int main()
{
	extern int Errupt;    /*可选的声明*/
	extern double Up[];   /*可选的声明*/
	......
	return 0;
}

       这样的变量只能在编译器被初始化一次,如果没有显示初始化,它的所有字节会被自动设置为0。

4️⃣  静态线程本地外部链接

       与静态外部链接的区别是,变量的存储期是线程运行期间,当线程销毁后变量将消失。其余属性与静态外部链接一致。
看下面的demo code👇

/****************main.c******************/
#include <threads.h>

void set_threadlocal_var(int value); //引用性声明

_Thread_local int Star_CSRS = 1; //定义静态线程本地外部链接变量

int test_threadlocal_var(void *params)
{
	//根据参数params修改Star_CSRS变量
    set_threadlocal_var(params);
    set_threadlocal_var(params);
}


int main() {

    thrd_t t1,t2;

	/*创建两个线程*/
    thrd_create(&t1,test_threadlocal_var,2); 
    thrd_create(&t2,test_threadlocal_var,3);

    thrd_join(t1,0);
    thrd_join(t2,0);

    return 0;
}
/****************thrd_local.c******************/
#include <stdio.h>
void set_threadlocal_var(int value)
{
    extern _Thread_local int Star_CSRS;
    Star_CSRS += value;

    printf("Star_CSRS is %d ; address of Star_CSRS is %p.\\n",
           Star_CSRS,&Star_CSRS);
}

输出如下:
在这里插入图片描述

5️⃣  静态内部链接

       普通的外部变量可以用于同一程序中任意文件中的函数,但是内部链接的静态变量只能用于同一文件中的函数。可以使用存储类别说明符extern,在函数中重复声明任何具有文件作用域的变量,但是这样的声明不会改变其链接属性。看下面的代码👇

int traveler = 1;         /*外部链接*/
static int stayhome = 1;  /*内部链接*/

int main()
{
	extern int traveler;  /*使用定义在别处的traveler*/
	extern int stayhome;  /*使用定义在别处的stayhome*/
	......
	return 0;
}

       对于该程序所在的翻译单元,traveler和stayhome都具有文件作用域,但是只有traveler可以用于其它翻译单元。这两个声明都使用了extern关键字,指明了main()中使用的这两个变量的定义都在别处,但是这并未改变stayhome的内部链接属性
       这样的变量只能在编译器被初始化一次,如果没有显示初始化,它的所有字节会被自动设置为0。

6️⃣  静态线程本地内部链接

       与静态内部链接的区别是,变量的存储期是线程运行期间,当线程销毁后变量将消失。其余属性与静态内部链接一致。

#include <stdio.h>
#include <threads.h>

/*定义静态线程本地内部链接*/
static _Thread_local int Star_CSRS = 1;

void set_threadlocal_var(int value)
{
    extern _Thread_local int Star_CSRS;
    Star_CSRS += value;

    printf("Star_CSRS is %d ; address of Star_CSRS is %p.\\n",
           Star_CSRS,&Star_CSRS);
}

int test_threadlocal_var(void *params)
{
    set_threadlocal_var(params);
    set_threadlocal_var(params);
}


int main() {

    thrd_t t1,t2;

    thrd_create(&t1,test_threadlocal_var,2);
    thrd_create(&t2,test_threadlocal_var,3);

    thrd_join(t1,0);
    thrd_join(t2,0);

    return 0;
}

输出如下图👇
在这里插入图片描述

7️⃣  静态无链接

       这样的变量与自动变量一样,具有相同的作用域,但是程序离开它所在的函数后,变量不会消失。计算机在多次函数调用之间会记录它们的值。
       这样的变量只能在编译器被初始化一次,如果没有显示初始化,它的所有字节会被自动设置为0。

#include<stdio.h>

void trystat(void);

int main()
{
	int count;
	for(count=1;count<3;++count)
	{
		printf("Here comes iteration %d:\\n",count);
		trystat();
	}
	return 0;
}

void trystat()
{
	int fade = 1;
	static int stay = 1;
	printf("fade = %d and stay = %d\\n",fade++,stay++);
}

输出如下:
在这里插入图片描述

8️⃣  静态线程本地无链接

       与静态无链接的区别是,变量的存储期是线程运行期间,当线程销毁后变量将消失。其余属性与静态无链接一致。

看下面的demo code👇

/****************main.c******************/
#include <stdio.h>
#include <threads.h>

/*定义在thrd_local.c文件中*/
void set_threadlocal_var(int value);

int test_threadlocal_var(void *params)
{
    set_threadlocal_var(params);
    set_threadlocal_var(params);
}


int main() {

    thrd_t t1,t2;

    thrd_create(&t1,test_threadlocal_var,2);
    thrd_create(&t2,test_threadlocal_var,3);

    thrd_join(t1,0);
    thrd_join(t2,0);

    return 0;
}
/****************thrd_local.c******************/
#include <stdio.h>
void set_threadlocal_var(int value)
{
	/*定义静态线程本地无链接变量Star_CSRS*/
    static _Thread_local int Star_CSRS = 1;
    Star_CSRS += value;

    printf("Star_CSRS is %d ; address of Star_CSRS is %p.\\n",
           Star_CSRS,&Star_CSRS);
}

输出如下图:
在这里插入图片描述

9️⃣  动态内存分配

       动态内存分配就是使用malloc()等类似函数分配内存,内存的管理完全由程序员负责,标准并没有针对这样的内存制定内存管理规则
       malloc()和free()大家应该都很熟悉了,我们这里介绍一下 calloc() 函数。这个函数与malloc()的区别在于,它会自动把块中的所有位都设置为0

看下面的code:

#include<stdio.h>
#include<stdlib.h>

int main()
{

	int * malloc_p;
	int * calloc_p;
	
	malloc_p = (int*)malloc(sizeof(int));
	printf("the value of malloc_p is %d\\n",*malloc_p);
	
	calloc_p = (int*)calloc(1,sizeof(int));
	printf("the value of calloc_p is %d\\n",*calloc_p);
	
	return 0;
}

输出如下:
在这里插入图片描述
👆上面code的输出表明malloc()分配内存后并没有自动设置所有字节为0,而calloc()自动设置所有字节为0。


➰静态变量、自动变量、动态内存分配在内存中的位置

老程序员肯定知道静态变量存储在程序的全局区自动变量存储在程序的栈区动态内存分配位于程序的堆区。可以看下面的code👇

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int static_store=30;
const char * pcg = "Cong Shan Ruo Shui";

int main()
{
	int auto_store = 40;
	char auto_string[] = "Auto Cong Shan Ruo Shui";
	int * pi;
	int * pcl;
	
	pi = (int*)malloc(sizeof(int));
	*pi=35;
	
	pcl = (char*)malloc(strlen("Dynamic String")+1);
	strcpy(pcl , "Dynamic String");
	
	printf("static_store: %d at %p\\n",static_store,&static_store);
	printf("auto_store: %d at %p\\n",auto_store,&auto_store);
	
	printf("pi: %d at %p\\n",*pi,pi);
	printf("%s at %p\\n",pcg,pcg);
	printf("%s at %p\\n",auto_string,auto_string);
	printf("%s at %p\\n",pcl,pcl);
	printf("%s at %p\\n","Star Cong Shan Ruo Shui","Star Cong Shan Ruo Shui");
	
	return 0;
}

输出如下👇
在这里插入图片描述


在这里插入图片描述

以上是关于精通C语言一图搞清C语言到底有多少种变量存储类别的主要内容,如果未能解决你的问题,请参考以下文章

C语言动态和静态存储类别的区别

C语言中都有哪些存储类型?

C语言杂谈存储类别

C语言中,变量到底是个啥概念?

经典C语言讲解C语言中局部变量和全局变量 变量的存储类别

C语言编程程序的内存如何布局