1、struct
- C语言中的struct可以看作变量的集合
1.1 结构体与柔性数组
-
柔性数组即数组大小待定的数组
-
C语言中可以由结构体产生柔性数组
-
C语言中结构体的最后一个元素可以是大小未知的数组
- array[]柔性数组作为标识符,不占用内存空间
struct SoftArray { int len; int array[]; };
1.2 柔性数组应用实例
struct SoftArray
{
int len;
int array[];
};
struct softArray* create_soft_array(int size)
{
struct SoftArray* ret = NULL;
if(size > 0)
{
ret = (struct SoftArray*)malloc(sizeof(struct SoftArray) + sizeof(int) * size);
ret->len = size;
}
return ret;
}
void delete_soft_array(struct SoftArray* sa)
{
free(sa);
}
void func(struct SoftArray* sa)
{
int i = 0;
if( NULL != sa )
{
for(i = 0; i < sa->len; i++)
{
sa->array[i] = i + 1;
}
}
}
int main()
{
int i = 0;
struct SoftArray* sa = creat_soft_array(10);
func(sa);
for(i = 0; i < sa->len; i++)
{
printf("%d\n", sa->array[i]);
}
delete_soft_array(sa);
return 0;
}
2、union
-
C语言中的union在语法上与struct相似
-
union只分配最大成员的空间,所有成员共享这个空间
struct A { int a; int b; int c; }; union B { int a; int b; int c; }; int main() { printf("%d\n", sizeof(struct A));//12 printf("%d\n", sizeof(union B));//4 return 0; }
-
union的使用受系统大小端的影响
- 小端模式低地址存储低位数据
- 大端模式低地址存储高位数据
2.1 利用union的性质判断系统大小端
int system_mode()
{
union SM
{
int i;
char c;
};
union SM sm;
sm.i = 1;
return sm.c;
}
int main()
{
printf("System Mode: %d\n", system_mode());
//返回1为小端,返回0为大端
return 0;
}
3、函数类型
- C语言中的函数有自己特定的类型
- 函数的类型由返回值,参数类型和参数个数共同决定
int add(int i, int j)
的类型为int(int, int)
- C语言中通过
typedef
为函数类型重命名typedef type name(parameter list)
- 例:
typedef int f(int,int);
-
typedef void p(int);
- 值传递:普通变量作为函数参数传递是单向的值传递,只是将实参的值复制一份给形参,形参的改变不会影响实参的值,因为所在内存空间不同,如果传递的是地址,被调函数使用指针接收,如果在被调函数中,没有更改指针指向空间中的内容,只改变指向,不改变内容
- 地址传递:指针、数组名作为函数参数传递,是地址传递,需要在被调函数中更改指针指向空间的内容,形参内容的改变,实参内容也随之改变
- 值返回:普通变量通过函数返回值进行返回是单向的值返回,在主函数的调用中,只能作为右值使用,不能重新赋值
- 地址返回:需要返回生命周期比较长的变量地址(全局变量、静态局部变量、堆区空间地址、只掉函数地址传递的空间),该函数的返回值是一个左值,可以直接使用,也可以重新赋值,重新赋值后,被调函数中的变量也随之改变
4、malloc 和 free
- malloc所分配的是一块连续的内存
- malloc以字节为单位,且不带任何的类型信息
- free用于将动态内存归还系统
void* malloc(size_t size);
void free(void* pointer);
4.1 注意点
- malloc 和 free是库函数,而不是系统调用
- malloc实际分配的内存可能会比请求的多
- 不能依赖于不同平台下的malloc行为
- 当请求的动态内存无法满足时malloc返回NULL
- 当free的参数为NULL时,函数直接返回
5、calloc 和 realloc
- malloc的同胞兄弟
void* calloc(size_t num, size_t size);
void* realloc(void* pointer, size_t new_size);
- calloc的参数代表所返回内存的类型信息
- calloc会将返回的内存初始化为0
- realloc用于修改一个原先一绝分配的内存块大小
- 在使用realloc之后应该使用其返回值
- 当pointer的第一个参数为NULL时,等价于malloc
6、typedef
- typedef用于给一个已经存在的数据类型重命名
- typedef本质上不能产生新的类型
- typedef重命名的类型
- 可以在typedef语句之后定义
- 不能被unsigned和signed修饰
- 语法
typedef type new_name;
7、进程在内存中的分配
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static int g_init = 255;
static float g_uninit;
static void text()
{
}
int main(int argc, const char *argv[])
{
static double s_init = 0.255;
static double s_uninit;
int i = 0;
int* p = malloc(4);
printf("argv[0] = %p\n", argv[0]);
printf("&i = %p\n", &i);
printf("p = %p\n", p);
printf("&g_uninit = %p\n", &g_uninit);
printf("&s_uninit = %p\n", &s_uninit);
printf("&g_init = %p\n", &g_init);
printf("&s_init = %p\n", &s_init);
printf("text = %p\n", text);
free(p);
return 0;
}
/*输出结果
argv[0] = 0x7fff3ff00192 进程入口
&i = 0x7fff3feffb6c 栈区
p = 0x558eb65b9260 堆区
&g_uninit = 0x558eb54f2028 .bass区
&s_uninit = 0x558eb54f2030 .bass区
&g_init = 0x558eb54f2010 .data区
&s_init = 0x558eb54f2018 .data区
text = 0x558eb52f173a 代码段区
*/
8、内存分区
- 一个进程启动后,系统会为该进程分配4G的虚拟内存
- 0-3G是用户空间,程序员写代码操作部分
- 3-4G是内核空间,主要与底层驱动打交道
- 所有进程会共享3-4G的内核空间,但每个进程独立拥有0-3G的用户空间
- 0-3G用户空间又被分为三个部分:栈区、堆区、静态区(全局区)
- 全局区又分为四个段:.bss段、.data段、.ro段、.txt段
- 应用层主要操作0-3G的用户空间,底层主要操作3-4G的内核空间
8.1 程序中的栈
- 栈是现代计算机程序里最为重要的概念之一
- 栈在程序中用于维护函数调用上下文
- 函数中的参数和局部变量存储在栈上
- 栈保存了一个函数调用所需的维护信息
- 参数
- 返回地址
- 局部变量
- 调用上下文
- …
- 函数调用时,对应的栈空间在函数返回前是专用的
- 函数调用结束后,栈空间将被释放,数据不再有效
8.2 程序中的堆
- 堆是程序中一块预留的内存空间,可由程序自由使用
- 堆中被程序申请使用的内存在被主动稀释前将一直有效
- C语言程序中通过库函数的调用获得堆空间
- 头文件:
malloc.h
malloc
以字节的方式动态申请堆空间free
将堆空间归还给系统
- 头文件:
8.3 程序中的静态存储区
- 静态存储区随着程序的运行而分配空间
- 静态存储区的生命周期直到程序运行结束
- 在程序的编译器静态存储区的大小就已经确定了
- 静态存储区主要用于保存全局变量和静态局部变量
- 静态存储区的信息最终会保存到可执行程序中
小结
- 栈,堆和静态存储区是程序中的三个基本数据区
- 栈区主要用于函数调用的使用
- 堆区主要是用于内存的动态申请和归还
- 静态存储区用于保存全局变量和静态变量