娓娓道来c指针 (2)内存分配

本文详细解析了C语言中变量的作用域与生存期概念,包括全局变量、文件内部静态变量和局部变量的不同作用域特点,以及静态变量、自动变量和堆变量的生存期特性。

                            (2)内存分配

c语言中描述变量的时候常用的两个用语

1.作用域:也叫可见域,指的是变量的作用范围。在哪个范围内,该变量是可见的、可以使用的。

2.生存期:也叫存储期,指的是变量从创建到销毁的生存时间段。

作用域和存在域是两个不同的概念,比如在程序的某个位置,某变量存在(内存中分配了地址)但不可见(不可使用)。

作用域

从作用域看,变量分为以下三种:

1.全局变量

在c语言中,把在任何函数之外声明的变量称为全局变量。一般情况下,全局变量在任何地方都是可见的。当然也有例外,比如在语句块{}内声明了一个同名的局部变量,则该全局变量会暂时不可见。

//全局变量
int foo = 5;
void fun()
{
	printf(" fun()->foo...%d\n", foo);
}
int main()
{
	int bar = 3;
	printf("main()->foo...%d\n", foo);
	printf("main()->bar...%d\n", bar);
	{
		//局部变量,屏蔽同名全局变量foo
		int foo = 6;
		printf("    {}->foo...%d\n", foo);
		//局部变量,屏蔽同名局部变量bar
		int bar = 4;
		printf("    {}->bar...%d\n", bar);
	}
	fun();
	return 0;
}
运行


从运行结果可以看出全局变量的可见范围。当然,如果在全局变量未被声明之前就试图使用它,则也会出错。(这与规则:“未声明,不可使用”有关。)

除此之外,c中的设计思想是:一个全局变量也是一个默认的外部变量(extern)。也就是说,一个全局变量不仅在本文件中是全局可见的,在别的文件中也是可见的。如

//1.c
int foo;
在另一个文件中有

//2.c
int foo;
编译报错:foo被重复定义,命名冲突。

这种默认行为被广为诟病,不少人认为,变量的可见域默认下应仅限于当前文件,需要扩大时,应该由程序员自己控制。并且对于函数,也有同样的默认行为。

如何防止这种行为?

(1)若是确实需要使用一个同名的变量,则可对已存在的同名全局变量用static修饰,使之成为静态的全局变量。这样它的可见域,就仅限于它所在的文件之内。

//1.c
static int foo; //可见域被限制于本文件内
在另一个文件中 重新声明 int foo; 没有问题。

(2)不定义新的,直接使用别的文件中的全局变量。语法是 extern int foo; 关键字 extern 是外部的意思,表明变量 foo,已在别的地方定义,这里只是在使用之前,作一个声明而已,不是重复定义。当然不作此声明也是不可用的,这再次体现了:可见,但不一定可用。


2.文件内部的静态变量

这就是上文中说的静态的全局变量。


3.局部变量

在函数中,或更直接的说是在语句块{}内定义的变量,是局部变量。它的可见域仅限于语句块内,在其它地方无法引用。局部变量在函数被调用时由系统分配存储区,在不同的函数中同名的变量实际上在内存中占不同的单元,因此在不同的函数中可以定义相同名字的局部变量。如函数的形参,main函数中定义的变量都是局部变量。

对作用域进行总结

c语言中存在三种作用域

(1)块作用域
自动变量(auto、register)和内部静态变量(static)具有块作用域,在一个块内声明的变量,其作用域从声明点开始,到该块结束为止。函数定义中声明的形参,其作用域限定在该函数体内,与其他函数中声明的同名变量不是一回事,允许在不同的函数中使用相同的变量名,编译器将为这些变量分配不同的存储单元,不会冲突。
(2)文件作用域
外部静态变量(static)具有文件作用域,从声明点开始到文件末尾,此处所指的文件是编译基本单位—c文件。
(3)全局(程序)作用域
全局变量(extern)具有全局作用域,只要在使用前对其进行声明,便可在程序(由若干个文件组成)的任意位置使用全局变量。


生存期

从生存期看,分为以下三种:

1.静态变量(static variable)

全局变量和指定static的局部变量,都具有静态存储期。它们从程序开始一直到程序结束都存在,故被统称为静态变量。


2.自动变量(auto variable)

没有被指定为static的局部变量和寄存器变量(register variable)都是自动变量。函数的形参及代码块中定义的变量都属于auto变量,这是C语言中应用最广的一种变量,这类变量是栈分配的,是动态分配存储空间的。举函数形参为例,当调用该函数时,为形参分配存储空间,当函数调用结束时,就自动释放这些存储空间。对代码块中定义的变量(包含函数中定义的变量),当执行到变量声明语句时,系统为这些auto变量分配空间,当程序流程离开代码块时,这些变量被自动撤销,其占用的内存空间被释放。


3.堆变量

通过malloc()函数分配内存区域的变量被放在堆中,故称为堆变量。并且似乎这个“堆”和数据结构中的堆没有什么联系,只是一种称呼而已。这种变量需要手动释放内存区域:free(变量名),也就是说它从创建时起就一直存在直到使用free()释放为止。当然,即使最后忘了释放,如今的操作系统也会在程序结束后释放为它分配的内存。不过仍然建议:谁创建,谁释放。


专栏目录:


<think>我们讨论的是全局指针全局指针是在全局作用域中声明的指针变量,可以在程序的任何地方访问。它们存储在静态存储区,生命周期与整个程序运行期相同。 全局指针的常见问题包括:初始化、作用域、生命周期、线程安全等。 回答结构: 1. 定义和基本特性 2. 初始化和默认值 3. 使用注意事项(包括线程安全、内存管理) 4. 示例代码 最后提出相关问题。</think>### 全局指针详解 #### 一、全局指针的定义与特性 全局指针是在函数外部声明的指针变量,具有全局作用域和静态存储期。 **基本定义:** ```c #include <stdio.h> int global_var = 42; // 全局变量 int* global_ptr = &global_var; // 全局指针初始化 void func() { printf("Value via global pointer: %d\n", *global_ptr); } int main() { func(); return 0; } ``` **关键特性:** 1. **作用域**:整个程序可见(包括所有源文件) 2. **生命周期**:程序启动时创建,程序结束时销毁 3. **存储位置**:数据段(已初始化)或BSS段(未初始化) 4. **初始化**: - 显式初始化:`int* ptr = &var;` - 隐式初始化:未初始化的全局指针默认为`NULL` #### 二、使用注意事项 1. **多文件访问**(需用`extern`): ```c // file1.c int* global_ptr = NULL; // file2.c extern int* global_ptr; // 声明在其他文件中定义 void init_ptr() { global_ptr = malloc(sizeof(int)*10); } ``` 2. **线程安全问题**: ```c #include <pthread.h> int* shared_ptr = NULL; pthread_mutex_t ptr_mutex = PTHREAD_MUTEX_INITIALIZER; void* thread_func(void* arg) { pthread_mutex_lock(&ptr_mutex); if(!shared_ptr) { shared_ptr = malloc(100); // 延迟初始化 } pthread_mutex_unlock(&ptr_mutex); // 使用指针... return NULL; } ``` 3. **内存管理陷阱**: ```c char* buffer = NULL; // 全局指针 void init_buffer() { buffer = malloc(1024); // 分配内存 } void cleanup() { free(buffer); // 必须显式释放! buffer = NULL; // 避免悬指针 } ``` #### 三、与局部指针的关键区别 | 特性 | 全局指针 | 局部指针 | |--------------|-----------------------------|--------------------------| | 作用域 | 整个程序 | 仅在定义块内有效 | | 生命周期 | 程序运行期间 | 函数/块执行期间 | | 初始化 | 默认为NULL | 未初始化则含随机值 | | 线程安全 | 需额外同步机制 | 天然线程私有 | | 内存位置 | 数据段/BSS段 | 栈内存 | | 访问效率 | 直接访问(无间接寻址) | 可能通过栈帧访问 | #### 四、最佳实践 1. **避免过度使用**: - 优先使用局部指针和参数传递 - 仅在真正需要全局访问时使用 2. **安全初始化**: ```c // 使用静态初始化确保线程安全 FILE* log_file = fopen("app.log", "w"); // 或者使用单次初始化锁 void init_global() { static pthread_once_t once = PTHREAD_ONCE_INIT; pthread_once(&once, init_resources); } ``` 3. **常量全局指针**: ```c // 指向常量的全局指针 const char* const APP_VERSION = "1.2.3"; // 指针和指向的数据都不可修改 ``` 4. **模块化封装**: ```c // module.h void set_config(int value); int get_config(); // module.c static int* config_ptr = NULL; // 隐藏实现 void init_module() { config_ptr = malloc(sizeof(int)); *config_ptr = 0; } void set_config(int value) { if(config_ptr) *config_ptr = value; } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值