C语言之sizeof、内存分区

sizeof

  • sizeof 返回的是变量(单个变量或结构体)实际所占用的空间的大小
typedef struct Person
{
	char a;
	int b;
}PERSON;

int main()
{
    std::cout << "Hello World!\n"; 
	printf("int size is %d\n", sizeof(int));
	double a = 3.14;
	printf("a size is %d\n",sizeof(double));
	PERSON xiaoming;
	printf("xiaoming size is %d\n",sizeof(xiaoming));
}

输出结果:
Hello World!
int size is 4
a size is 8
xiaoming size is 8
  • 对齐模式问题
#pragma pack(1)
typedef struct Person
{
	char a;
	int b;
}PERSON;

int main()
{
	PERSON xiaoming;
	printf("xiaoming size is %d\n",sizeof(xiaoming));
}

输出结果:
xiaoming size is 5
  • sizeof 返回的类型是unsigned int类型
void test(void)
{
	unsigned int a = 10;
	if (a - 20 > 0)
	{
		printf("大于0\n");
	}
	else
	{
		printf("小于0\n");
	}
}

输出结果是“大于0”, 因为sizeof()的返回值是unsigned int类型。
  • sizeof 计算数组,数组作为函数参数会退化为指向数组首元素的指针
int caculateArraySize(int arry[]) //传数组时写这种方式比较好
{
	return sizeof(arry);
}

void test1()
{
	int arr[] = {1,2,3,4,5,6};
	printf("arr size is %d\n",sizeof(arr));
	printf("sizeof arry is %d\n", caculateArraySize(arr));
}
输出结果:
arr size is 24
sizeof arry is 4

变量的间接赋值

void test01()
{
	//直接赋值
	int a = 10;
	a = 100; 
	//间接赋值
	int *p = &a;
	*p = 200;
}
typedef struct Person
{
	char a;
	int b;
	char c;
	int d;
}PERSON;
void test02()
{
	PERSON p = {'a',100,'b',200};
	printf("p.d: %d\n",p.d);
	p.d = 2000;
	printf("%d\n",(char*)&p+12);  //输出d的地址
	printf("%d\n",&(p.d));        //输出d的地址
    printf("%d\n",*(int *)((char *)&p + 12));  //输出d的值
   
    //&p
    int *pp=NULL;
    printf("pp:%d\n",pp);
    printf("pp+1:%d\n", pp+1); 	
}
输出结果
p.d: 200
7338780
7338780
2000
pp:0
pp+1:4

内存分区概念

运行之前

  • 预处理:宏定义展开,头文件展开,条件编译,这里并不检查语法
  • 编译: 检查语法,并将预处理后文件编程生成汇编文件
  • 汇编:将汇编文件生成目标文件(二进制文件)
  • 链接:将目标文件链接为可执行程序
    在没有运行程序前,也就是说程序没有加载到内存前,可执行程序内部已经分好3段信息,分别为代码区(text),数据区(data), 未初始化数据区(bss)
    栈变量和堆变量在运行时才有
  • 代码区 共享(避免内存浪费),只读

运行之后

额外增加栈区,堆区

  • 堆区 heap:自己控制申请和释放。程序泄漏问题。一般数据量较大时放堆上。
  • 栈区 stack:编译器自动申请和释放。一般栈大小固定。

全局静态区

全局静态区包含: 全局区,静态区,常量区

extern int a; //外部链接属性
static int a; //作用域在本文件内
常量区存放什么? 
const char *p = "Hello World"//字符串常量前最好加上const
const 修饰的全局变量
常量区的数据,一旦初始化,不能修改,只读内存。

栈区

不要返回局部变量的地址

int* myFun()
{
	//不要返回局部变量的地址,退出时,a的内存已经被回收
	int a=10;
	return &a;
}

void test()
{
	int *p = myFun();
	printf("*p=%d\n", *p); //虽然输出结果是*p = 10,但是 之前变量a占用的内存可能被其他变量占用
}
char* getString()
{
	char str[] = "Hello world"; //数组在栈上
	return str;
}

void test2()
{
	char *s = NULL;
	s = getString();
	printf("s = %s\n",s);
}

在这里插入图片描述

堆区

int *getSpace()
{
	int *p = (int *)malloc(sizeof(int) * 5);
	if (NULL == p) //NULL放前面习惯,因为当只有一个等号时会报错
	{
		return NULL;
	}
	//主要是连续内存空间,都能使用下标的方式方位内存
	for (int i=0;i<5;++i)  //前置++效率比较高,
	{
		p[i] = 100 + i;
	}
	//退出时在栈上的p会释放掉,但是堆上的分配的内存还在
	return p;
}

void test3()
{
	int *ret = getSpace();
	for (int i=0;i<5;i++)
	{
		printf("%d \n", ret[i]);
	}
	free(ret);
	ret = NULL;  //释放完后最好置成空
}
void allocateSpace(char *p)
{
	p = (char*)malloc(100);
	memset(p,0,100);
	strcpy(p,"hello world");
}

void test04()
{
	char *p = NULL;
	allocateSpace(p);  //函数的形参也是个局部变量,函数返回时也要释放
	printf("p=%s\n",p);
}

在这里插入图片描述

void allocateSpace2(char **p)//为什么要写两个*,便于区分指向的数据是什么数据
{
	char *tmp =  (char*)malloc(100);
	memset(tmp, 0, 100);
	strcpy(tmp, "hello world");
	*p = tmp;
}

void test05()
{
	char *p = NULL;
	allocateSpace2(&p);
	printf("p=%s\n", p);
}

在这里插入图片描述

void fun(char **p)  //对指针取地址,升一个*,变成两个**
{
	...;
	*p = xx;
}

test()
{
	char *p =NULL;
	fun(&p);
}

在这里插入图片描述在这里插入图片描述

全局区、静态区

//extern int a = 10; 默认外部链接
int a= 10;         //全局区
//静态全部变量时内部链接
static int b = 20; //静态区
//内部链接和外部链接有什么区别:
//1.static 只能在当前文件内访问
//2.外部链接,此变量可被外部访问

//1.全局静态变量和局部静态变量都存储在静态区,在程序运行期间都是合法有效
//2.局部静态变量符号的可见范围仅限于当前函数内部,全局静态变量可见范围从定义到文件结尾
//3.头文件不参与编译,每一个.c文件,叫做一个编译单元
//4.编译器独立编译每个.c文件
void test01()
{
	static int c = 30;
}

常量区、字符串常量、全局const变量

  • const全局和局部变量区别?
  1. const全局变量在常量区,不能修改(直接或间接)
const int g_c = 100;
//&g_c 的类型为const int*类型
int *p = &g_c;  //此时编译器会报warning, 不同类型的两个指针赋值,就会报错
//解决方法:
int *p = (int *)&g_c; //强转
*p = 200;  //此时编译器会报错,全局const放在常量区,一旦初始化,不能修改
  1. const局部变量
void test()
{
	//栈上
	const int a = 100;
	//a=200;  //编译器报错
	int *p = (int *)&a;  //跳过编译器的检查了,运行时把a的值修改了
	*p = 200; 
	printf("a=%d\n",a);  //a的值被修改
}

字符串常量区

void test()
{
	char *p = "Hello World";
	printf("%d\n",&"Hello World");
	printf("%d\n",p);
	//两者输出的地址是一样的
}
  • ANSI C中规定:修改字符串常量,结果是未定义的
  • ANSI C并没有规定编译器的实现者对字符串的处理,例如:
    1.有些编译器可以修改字符串常量,有些编译器不可修改
    2.有些编译器把多个相同的字符串常量看成一个,有些进行优化(节省空间),有些则不进行优化。
void test()
{
	char *p1 = "Hello world";
	char *p2 = "Hello world";
	printf("%d\n",p1);
	printf("%d\n",p2);
	//两者输出根据编译器而不同
}

3.所以尽量不要去修改字符串常量!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值