一个分布
进程是程序的一次执行过程,一个进程的内存进程起来之后系统会给它分配4G的虚拟内存
堆区内存的使用
-
申请内存
malloc()函数
头文件: #include <stdlib.h>
原型:void *malloc(size_t size);
功能:在堆区申请一片可供使用的内存
参数:size:申请的空间大小,以字节为单位,size_t是int的重命名
返回值:void *:指针,申请的那片空间的首地址
-
内存释放:free
void free(void *ptr);
功能:释放通过malloc申请的空间
参数ptr:申请的空间的首地址
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
//在堆区申请一片内存并且把这片内存首地址赋值给指针p
int *p=(int *)malloc(sizeof(int));
if(p==NULL)
{
printf("空间申请失败\n");
return -1;
}
*p=100;
//申请空间存放100个int 类型的数据
int *q=(int *)malloc(100*sizeof(int));
if(q==NULL)
{
printf("空间申请失败\n");
return -1;
}
int i;
for(i=0;i<100;i++)
{
q[i]=i;
}
for(i=0;i<100;i++)
{
printf("%d\n",q[i]);
}
free(p);
free(q);
//防止野指针
p=NULL;
q=NULL;
return 0;
}
static关键字
静态关键字,可以用来修饰局部变量、全局变量和函数
-
static关键字修饰的变量存储在内存的静态区,变量数据未初始化时,会被赋值为0
-
static修饰的局部变量只会初始化一次
#include <stdio.h>
int main(int argc, const char *argv[])
{
//未初始化的static变量数值为0
int i;
for(i=0;i<10;i++)
{
//变量a只被初始化一次
static int a=1;
a++;
printf("%d\n",a);
}
return 0;
}
输出结果:2 3 4 5 6 7 8 9 10 11
-
static用于限制作用域和延长生命周期
-
static修饰局部变量时,该变量作用域还是局,会限制该变量作用域在本部,但是生命周期延长至程序执行结束时
-
static修饰全局变量时文件
-
static修饰函数时,会限制作用域在本文件
-
两个文件同时编译:gcc 1.c 2.c
1.c:ex
#include <stdio.h>
//声明变量a是引用的外部变量
//extern int a;
extern int fun_add(int,int);
int main(int argc, const char *argv[])
{
// printf("%d\n",a);
printf("%d\n",fun_add(3,4));
return 0;
}
2.c:
static int a=10;
static int fun_add(int a,int b)
{
return a+b;
}
思考:想件中被static修饰的全局变量的数值如何操作?
思路:在定义全局变量的文件中定义一个全局的指针变量指向对应全局变量
只需要在对应文件中对全局的指针变量引用即可
const关键字
常量化变量,被const关键字修饰的变量不可以被做修改操作,但是我们可以通过定义指针修改变量的数值
#include <stdio.h>
int main(int argc, const char *argv[])
{
int const a= 10;
//a++;
int *p=&a;
(*p)++;
printf("%d\n",a);
return 0;
}
int const *p;//指针指向的空间的数值没办法通过指针修改,但是p的可以改指向
int * const p;//p的指向不可以改,但是指针指向的空间的数值可以通过指针修改
const int *p;//指针指向的空间的数值没办法通过指针修改,但是p的可以改指向
int const*const p;//指向不可以改,数值也不可以通过指针改
应用场景:
#include <stdio.h>
//用于保护mian函数局部变量的数值不能在别的函数中被修改
int func(const int *i,const int *j)
{
*i=100;//会报错
return (*i)+(*j);
}
int main(int argc, const char *argv[])
{
int a= 10;
int b = 11;
int c=func(&a,&b);
printf("%d\n",c);
return 0;
}
extern关键字
用于在一个文件中引用另一个文件中的全局变量和函数,在哪个文件中引用,就要在哪个文件中声明,
ex:
在2.c中定义全局变量:int a=10;
在1.c中引用2.c的全局变量:extern int a;
register关键字
register关键字修饰的变量空间是在寄存器里,寄存器是在cpu内部供CPU使用。
但是寄存器数量有限,如果寄存器不能给当前变量空间,则该类型变量会和auto修饰的变量具有相同的待遇,由系统自动在栈区给它申请和释放空间
注意:没有办法给register修饰的变量取地址
volatile关键字
作用:防止编译器优化
被volatile修饰的变量,保证每一次读取该变量的数值都是直接在内存中进行读取
应用场景:
-
同一个进程里的多个线程访问同一个共享资源时,需要把该资源用volatile修饰
-
c语言进行硬件映射地址空间读写时,需要进行volatile修饰
数据类型:基础数据类型、构造数据类型
结构体
构造数据类型,用于描述一个复杂的事物信息
定义格式:
struct 结构体名
{
数据类型 1 成员 1;
数据类型 2 成员 2;
数据类型 3 成员 3;
...
};
注意:
1.struct 是结构体的关键字,必须得写
2.{}内容是成员列表,也成为域表
3.结构体成员之间以;隔开
4.在结构体末尾一定要有一个分号,表示结构体定义结束
5.结构体成员本质上就是变量
6.结构体尽量定义在头文件中或者全局
结构体变量定义格式
1.定义结构体同时定义结构体变量
struct 结构体名
{
。。。
}变量 1;
2.先定义结构体,再定义结构体变量//用的最多的
struct 结构体名
{
。。。
};
struct 结构体名 变量名;
3.定义结构体的时候不加结构体名直接定义变量
struct
{
。。。
}变量名;
注意:
1.结构体变量访问结构体成员的时候不能整体访问,只能一个个访问
2.如果结构体成员本身又是一个结构体类型,则需要一级一级的找到最下面的一个
3.结构体成员就是正常变量,可以进行各种运算结构体变量访问结构体成员:
结构体变量的访问
结构体变量名.成员名
结构体变量的初始化
struct 结构体名 变量名={初始化的数据列表};
结构体指针
本质上就是一个指针,指向结构体变量的指针
定义格式:
struct 结构体名 *指针变量名;
结构体指针访问结构体成员:
指针变量名->成员名
结构体重命名
typedef struct 结构体名
{
。。。
}重命名后的结构体名;
结构体数组
存放结构体变量的数组
格式:struct 结构体名 数组名[长度]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct nm
{
char name1[32];
};
typedef struct student
{
//结构体成员变量也是结构体类型,一级一级访问
struct nm name;
int num;
float score;
}STU;
int main()
{
//定义结构体变量
struct student stu1;
//在定义变量时就给它初始化成员变量
STU stu2={"zhangsan",2,99.9};
//定义一个指针指向给结构体在堆区申请的空间
STU *stu3=malloc(sizeof(STU));
//结构体成员变量也是结构体类型,一级一级访问
strcpy(stu1.name.name1,"jianghao");
stu1.num=1;
printf("%s\n",stu1.name.name1);
printf("%s\n",stu2.name.name1);
//结构体指针访问成员用->
stu3->num=3;
printf("%d\n",stu3->num);
//定义结构体数组,可以存放50个结构体变量
STU stu4[50];
//给数组第一个成员进行赋值
strcpy(stu4[0].name.name1,"李红");
stu4[0].num=4;
stu4[0].score=98;
printf("%s,%d,%f\n",stu4[0].name.name1,stu4[0].num,stu4[0].score);
return 0;
}
结构体大小计算
结构体成员之间内存连续,结构体变量大小是结构体所有成员变量大小之和
结构体大小计算的时候默认遵循字节对齐原则,一般判断成员变量类型最大的变量,以此变量大小为基准,为每一个变量申请相同大小的空间,32位系统中默认最大对齐是4字节对齐,64位默认最大对齐是8字节对齐
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct student
{
char a;//4
int num;//4
short score;4
}STU;
int main()
{
printf("%ld\n",sizeof(STU));
return 0;
}
结果:12
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct student
{
//a和score使用同一个四字节空间
char a;
short score;
int num;
}STU;
int main()
{
printf("%ld\n",sizeof(STU));
return 0;
}
结果:8
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct student
{
//a和score使用一个4字节,a下面的一个字节空着,b自己使用一个4字节
char a;
short score;
char b;
int num;
}STU;
int main()
{
printf("%ld\n",sizeof(STU));
return 0;
}
//结果:12
共用体
共用体又被称为联合体,多种不同类型的成员变量共享最大成员占有的内存
1.定义格式:
union 共用体名
{
成员类型1 成员1;
成员类型2 成员2;
。。。。
};
2.共用体变量定义格式:
union 共用体名 共用体变量名;
-
共用体变量访问成员:变量名.成员变量名
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
union student
{
char a;
short score;
int num;
};
int main()
{
union student s1;
printf("%ld\n",sizeof(s1));//4
s1.a='a';
printf("%c\n",s1.a);//a
printf("%d\n",s1.num);//97
s1.num=98;
printf("%c\n",s1.a);//b
printf("%d\n",s1.num);//98
return 0;
}
注意:
-
共用体成员使用公用的区域,这个区域一定是最大的成员占据的空间
-
在共用体变量成员变量中起作用的一定是最后一次修改后的成员的数值,在存入新的数值之后,原来的数值被覆盖
-
共用体同样遵循字节对齐,以成员类型最大的为基准对齐,64位系统中最大时8字节对齐
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
union student
{
//4字节对齐,而且需要能放开一个数组的大小,所以大小是32
char a[29];
short score;
int num;
};
int main()
{
union student s1;
printf("%ld\n",sizeof(s1));
return 0;
}
结果:32
枚举
#define 对一个常量进行宏定义
枚举一般用来对一堆常量进行替换
枚举用于声明一组常量,当一个变量只有固定的几个取值时,可以将这个变量定义成枚举
比如:一天24小时,一周有7天,一年有四季,这种情况都可以定义成枚举
枚举定义格式:
enum 枚举名
{
成员1,
成员2,
。。。
};
枚举变量的定义格式:enum 枚举名 变量名
注意:
-
枚举数值默认第一成员为0,依次向下递增1
#include <stdio.h>
//定义枚举
enum week
{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
int main(int argc, const char *argv[])
{
//定义枚举变量
enum week w;
w=Monday;
printf("%d %d %d %d %d %d %d\n",Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday);
return 0;
}
结果:0 1 2 3 4 5 6
-
我们可以给枚举成员分别赋值:
#include <stdio.h>
//定义枚举
enum week
{
Monday=1,
Tuesday=3,
Wednesday=4,
Thursday=6,
Friday=10,
Saturday=11,
Sunday=12
};
int main(int argc, const char *argv[])
{
//定义枚举变量
enum week w;
printf("%d %d %d %d %d %d %d\n",Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday);
return 0;
}
结果:1 3 4 6 10 11 12
-
如果只给第一个成员赋值,下面的成员数值依次增加1
#include <stdio.h>
//定义枚举
enum week
{
Monday=1,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
int main(int argc, const char *argv[])
{
//定义枚举变量
enum week w;
printf("%d %d %d %d %d %d %d\n",Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday);
return 0;
}
结果:1 2 3 4 5 6 7
-
枚举成员是可以在全局起作用的,不能定义和它重名的全局变量
-
枚举成员相当于常量,不可以给他们赋值