C语言储存类别和内存分配malloc、calloc和realloc函数

26 篇文章 3 订阅


如果可以使用左值修改对象中的值,则该左值就是一个可修改的左值。

cosnt char * pc = "Behold a string literal!";

程序根据声明,把相应的字符串常量存储在内存中,内含这些字符值的数组就是一个对象,由于数组中的每一个字符都能被单独访问,所以每个字符也是一个对象,该声明还创建了一个标识符为pc的对象,储存着字符串的地址。由于可以设置pc重新指向其他字符串,所以标识符pc是一个可以修改的左值
const只能保证被pc指向的字符串内容不被修改,但是无法保证pc不指向别的字符串,由于pc指定了储存‘B’字符的数据对象,所以pc是一个左值,但不可修改。

作用域

作用域描述程序中可访问标识符的区域。一个C变量的作用域可以是块作用域、函数作用域、函数原型作用域或文件作用域。

  • 是用一对花括号括起来的代码区域,定义在块中的变量具有块作用域
  • 函数作用域仅用于goto语句的标签。
  • 函数原型作用域用于函数原型声明,其范围是从形参定义处到原型声明结束。
  • 文件作用域,定义在函数外面的变量具有函数作用域,从它的定义处到该定义所在文件的末尾均可见,由于这样的变量可用于多个函数,所以文件作用域变量也称为全局变量

链接

C变量有三种连接属性:外部链接、内部链接或无链接。具有块作用域、函数作用域或函数原型作用域的变量都是无链接变量。这意味着这些变量属于他们定义的块、函数或原型私有。全局变量可以是外部链接或内部链接。外部链接可以在多文件程序中使用,内部链接变量只能在一个翻译单元中使用。
如何知道全局变量是外部链接还是内部链接?查看外部定义是否使用了static:

int giants = 5;      //文件作用域,外部链接
static int dodgers = 3; //文件作用域,内部链接
//该文件和同一程序的其他文件都可以使用变量giants,
//而dodgers属于文件私有,只有在该文件中的函数可以使用

存储期

作用域和链接描述了标识符的可见性,存储期描述了通过这些标识符访问的对象的生存期。C对象有4个存储期:静态存储期、线程存储期、自动存储期和动态分配存储期。

  • 静态存储期:在程序执行期间一直存在,文件作用域变量(全局变量)都具有静态存储期。
  • 线程存储期:用于并发程序设计,程序执行被分为多个线程,具有线程存储期的对象,从被声明到线程结束一直存在。以关键字_Thread_local声明一个对象时,每个线程都将获得该变量的私有备份。
  • 自动存储期:块作用域的变量通常具有自动存储期,当程序进入定义这些变量的块时,为这些变量分配内存,当退出这个块时,释放刚才分配的内存。

多文件

只有当程序由多个翻译单元组成时,才体现区别内部链接和外部链接的重要性。
复杂的C程序通常有多个单独的源代码文件组成,有时这些文件可能要共享一个外部变量。C通过在一个文件中定义式声明(定义),然后在其他文件中**引用式声明(声明)**来实现共享。
也就是说,除了一个定义式声明外,其他声明都要使用extern关键字,而且,只有定义式声明才能初始化变量。
定义式声明会为变量分配存储空间。
如果外部变量定义在一个文件中,那么其他文件使用该变量之前必须先声明它(用extern关键字),也就是说,在某文件中对外部变量进行定义式声明只是单方面允许其他文件使用该变量,其他文件在使用extern关键字声明之前不能直接使用它。

外部链接的静态变量

把变量的定义性声明放在所有函数的外面便创建了外部变量。

  • 为了指出该函数使用了外部变量,可以在函数中用关键字extern再次声明
  • 如果一个源代码文件使用的外部变量定义在另一个源代码文件中,则必须用extern在该文件中声明该变量。
  • 外部变量如果未被显示初始化,他们会被自动初始化为0,同样也适用于外部定义的数组元素。
  • 只能使用常量表达式初始化文件作用域变量(全局变量
#include<stdio.h>
int x = 10;
int y = 2 * x;//报错,x是变量。[Error] initializer element is not constant
int main(void){	
	return 0;
}
  • 不要使用extern关键字创建外部定义,只用它来引用现有的外部定义
#include<stdio.h>
int tern = 10;//tern被定义 ----------定义式声明 

int main(void){	
	//使用在别处定义的tern -------引用式声明
	//关键字extern表明该声明不是定义,它指示编译器其别处查询其定义 
	extern int tern;

	return 0;
}

内部链接的静态变量

具有静态存储期、文件作用域和内部链接。

int traveler = 1; //外部链接,静态变量
static int stayhome = 1; // 内部链接,静态变量
int main(){
}

traveler和stayhome都具有文件作用域,但是只有traveler可用于其他翻译单元(外部链接),
普通的外部变量可用于同一程序中任意文件中的函数,但是被static修饰的内部连接的静态变量只能用于同一文件中的函数。

多文件

只有当程序有多个翻译单元组成时,才体现区别内部链接和外部链接的重要性。
复杂的C程序通常由多个单独的源代码文件组成,有时这些文件需要共享一个外部变量。

  • C通过在一个文件中进行定义式声明,然后在其他文件中进行引用式声明来实现共享。
  • 除了一个定义式声明外,其他声明都要使用extern关键字,只有定义式声明才可初始化变量。
  • 如果外部变量定义在其中一个文件中,那么其他文件在使用该变量之前必须用extern关键字声明。

函数的存储类别

函数可以是外部函数(默认)和静态函数。C99新增了一个内联函数。
外部函数可以被其他文件的函数访问,而内部函数只能用于其定义所在的文件。

double gamma(double);//默认为外部函数
static double beta(int);//静态函数
extern double delta(double); //外部函数

除非使用static关键字,否则一般函数默认为extern

分配内存:malloc()

  • 接收参数:所需的内存字节数。
  • 返回值:动态分配内存块的首字节地址。分配内存失败则返回空指针NULL。
  • 通常把返回值强制转换为匹配的类型。提高代码可读性。
//动态分配数组 
#include<stdio.h>
#include<stdlib.h>//为malloc()、free()提供原型

int main(void){
	double * ptd;
	int max;
	int number;
	int i = 0;
	
	puts("What is the maximum number of type double entries?");
	if (scanf("%d",&max) != 1){
		puts("Number nout correctly entered -- bye.");
		exit(EXIT_FAILURE);
	}
	/*
		分配足够的内存空间以存储用户要存入的所有数,然后把动态分配的内存地址赋给指针ptd
		在C中,不一定要使用强制类型转换(double *),但是在C++中必须使用 
	*/
	ptd = (double *) malloc(max * sizeof(double));
	/*malloc()可能分配不到所需的内存,在这种情况下,该函数返回空指针,程序结束*/ 
	if(ptd == NULL){
		puts("Memory allocation failed. Goodbye.");
		/*
			如果内存分配失败,可以调用exit()函数结束程序
			其原型在stdlib.h库中,其标准提供了两个返回值以保证在所有操作系统中都能正常工作:
			EXIT_FAILURE:程序异常终止。 
			EXIT_SUCCESS:普通的程序结束 
		*/ 
		exit(EXIT_FAILURE);
	}
	/*ptd现在指向有max个元素的数组*/
	puts("Enter the values (q to quit):");
	while (i < max && scanf("%lf",ptd+i) == 1)//ptd+i与&ptd[i]等价 
		++i;
	printf("Here are your %d entries:\n", number = i);
	for (i = 0; i < number; i++){
		printf("%7.2f ",ptd[i]);
		if(i % 7 == 6)
			putchar('\n');
	}
	if (i % 7 != 0)
		putchar('\n');
	puts("Done.");
	
	/*free()函数位于程序的结尾,它释放了malloc()分配的内存。free()函数只释放其参数指向的内存块。*/ 
	free(ptd);
	
	return 0;
} 

free()的重要性

  • 静态内存的数量在编译时是固定的,在程序运行期间不会改变。
  • 自动变量使用的内存数量在程序执行期间自动增加或减少。
  • 动态分配的内存数量只会增加不会减少,除非使用free()进行释放。
    如果malloc()和free()不配对使用,可能会耗尽内存,从而导致内存泄漏

calloc()函数

函数原型:void* calloc(unsigned int num,unsigned int size);
功能:在内存的动态存储区中分配num个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。
calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不做初始化,分配到的空间中的数据是随机数据。
典型的用法如下

long * new;
new = (long *)calloc(100, sizeof (long));
  • 如果要存储不同的类型,应使用强制类型转换符
  • calloc()函数接收两个无符号整数作为参数,第一个参数是所需的存储单元数量,第2个参数是存储单元的大小(以字节为单位)。
  • calloc()把块中所有位都设置为0.
  • 也可用free()来释放calloc()分配的内存。

realloc()函数

realloc原型是extern void *realloc(void mem_address, unsigned int newsize);
语法
指针名=(数据类型
)realloc(要改变内存大小的指针名,新的大小)。
新的大小可大可小(如果新的大小大于原内存大小,则新分配部分不会被初始化;如果新的大小小于原内存大小,可能会导致数据丢失

先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,

如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来

mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。

即重新分配存储器块的地址。如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。当内存不再

使用时,应使用free()函数将内存块释放。

调整的空间比原有空间大:

大了一点:多出来的空间小于下面空闲的空间;
做法:

  • 直接延伸申请空间
  • 返回原空间首地址

大了很多:多出来的空间,大于下面空闲空间
做法:

  • 1.重新开辟新空间
  • 2.将旧空间的内容拷贝到新空间中
  • 3.释放旧空间
  • 4.返回新空间的首地址

调整的空间比原有空间小:

做法:

  • 1.将原空间缩小
  • 2 .返回旧空间首地址

如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。

总结,C的5种存储类别:

  • 自动,在中不带存储类别说明符,或带auto修饰的变量(或作为函数头的形参)属于自动存储类别。具有自动存储期,块作用域,无链接,若未初始化,其值未定义
  • 寄存器,在中带register存储类别说明符的变量(或作为函数头的形参)属于寄存器存储类别。具有自动存储类别,无链接,块作用域,且无法获取其地址,因为它存储在寄存器中,而不是内存中。把一个变量声明为寄存器变量即请求编译器将其存储在访问数读最快的区域。若未初始化,其值未定义
  • 静态、无链接,在中用static声明的变量,具有静态存储期,块作用域,无链接。只在编译时被初始化一次,如果未被显示初始化,其字节都被设置为0。
  • 静态,外部链接,在所有函数外部,且没有使用static声明的变量。具有静态存储期,文件作用域,外部链接,只在编译时被初始化一次,如果未被显示初始化,其字节都被设置为0。
  • 静态、内部链接,在所有函数外,且使用了static关键字声明。具有静态存储期,文件作用域,内部链接,只在编译时被初始化一次,如果未被显示初始化,其字节都被设置为0。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SOC罗三炮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值