一、内存管理
内存四大区:
代码区:
存放函数体的二进制代码,由操作系统进行管理。当.exe可执行程序执行时,加载到内存代码区。字符串常量就存在这个区。注意:代码区只可读,不可修改。
栈区:
局部定义的变量,函数的形参等存放于栈区。此区特点:由编译器自动分配释放,函数返回时,内存会被自动回收。
堆区:
相对于栈区最大的区别就是,该区需要程序员手动分配和释放,若程序员不释放,在程序完全结束后也会被系统回收。例如:用户malloc手动申请的空间,用完需要手动free()释放。
静态区:
全局变量,static修饰的局部变量。
二、一维数组
例如int a[100];
定义一个数组,100表示这个数组存储100个int类型的元素
数组的特点
数组开辟的是连续的空间
数组是多个相同类型的数据按照一定顺序排列所得,数组的长度一旦确认,就不能修改。这和java的集合有很大的区别,但Java中的集合底层原理其实也是数组,只是底层通过链表的扩容实现的动态数组。
数组的访问方式
下标访问:a[i] i的取值范围是0-99 注意数组下标是从0开始的
地址偏移方式访问: *(a+i) 此时等于a[i] 数据结构书上经常见到*(a+i)这样的访问方式,可以通过替换成a[i]这种形式来便于理解
其实a可以理解成 第一个元素的指针
它指向第一个元素,数据类型是 int*
注意对于数组,指针指向的是内存中第一个数据类型的地址。32位操作系统的一般情况下,不管什么类型的指针,都只占4个字节。
#include <stdio.h>
int main() {
//(一)、数组的两种访问方式
//1.下标方式访问a[i]
//2. *(a+i)访问
int a[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d\r\n", a[0]);
printf("%d\r\n", *(a+0));
int i = 0;
printf("下标方式遍历访问a[i]:");
for (i = 0; i < 10; i++) {
printf(" %d", a[i]);
}
printf("\r\n");
printf("*(a+i)方式遍历访问a[i]:");
for (i = 0; i < 10; i++) {
printf(" %d", *(a + i));
}
printf("\r\n");
return 0;
}
通过这个运行结果可以看出*(a+i) 此时等于a[i]
三、基本变量的指针
例如 int *p;
当我们在定义时候看到 *仅仅是告诉编译器这是一个指针;
给指针赋值
int a;
p=&a; &这里是取地址的意思
即p这个指针指向a的内存地址
当我们学习过c++可以知道&在c++有另一种意思,就是引用。这是c++为了简化c语言中函数调用要写好多*所提出的简便写法。
取指针的所指的内容
*p 其实这里的*可以简单理解,就是取指针所指内容的意思
所以这里的*p就等于a,对*p的操作就等于对a的操作
#include <stdio.h>
int main() {
//(二)、普通变量的指针;
//1. int *p; 是指针声明,声明为int 类型的指针
//2. p = &a; 是指针赋值,取变量a的地址
//3. *p; 是取指针的内容,
//4. *p = 100;是改变p所指对象a的值改为100
int a = 10;//声明并 初始化一个int变量a
int* p; //声明一个int 类型的指针
p = &a;//指针赋值,取变量a的地址
printf("*p是取指针的内容 %d\r\n", *p);
*p = 100;//是改变p所指对象a的值改为100
printf("更改后a的值为 %d\r\n", a);
printf("\r\n");
return 0;
}
四、结构体类型和通过typedef自定义类型
1.直接定义结构类型,并声明结构体变量
struct student{
int age;
char name[10];
char phone[20];
};
struct student stu1; ---- 结构体变量stu1;
struct student stu2; ---- 结构体变量stu2;
2.第二种常用方式:配合typedef自定义类型:
typedef struct{
int age;
char name[10];
char phone[20];
} student; -----这里是自定义了一种类型
student stu1; ----结构体变量stu1;
student stu2; ---- 结构体变量stu2;
3.结构变量通过.方式访问结构体元素
结构体指针通过->方式访问结构体元素
例如:
student stu1; ----结构体变量stu1;
student *p;---结构体指针变量p;
p=&stu1; --为指针赋值;
访问结构体stu1里面的元素:
比如为年龄赋值:
方式1: stu1.age = 10;//结构体对象用.访问
方式2: p->age = 10;//结构体指针用->访问
#include <stdio.h>
#include <string.h>
int main() {
//(三)typedef定义 自定义新类型
//结构体变量.方式 访问结构体元素
//结构体指针->方式 访问结构体元素
typedef struct {
int age;
char name[10];
char phone[20];
char idCard[20];
} Student; //自定义一个结构体类型
//声明并初始化一个结构体对象
Student stu1 = { 10,"张三","17809186909", "1233212234455" };
//结构体变量 通过.方式 访问结构体元素:
printf("stu1对象的年龄为:age=%d;\r\n", stu1.age);
printf("stu1对象的名字为:name=%s;\r\n", stu1.name);
printf("stu1对象的手机号为:phone=%s;\r\n", stu1.phone);
printf("stu1对象的身份证号为:idCard=%s;\r\n", stu1.idCard);
//声明一个结构体指针p
Student* p;
//为结构体指针 赋值,就是取结构体对象的地址
p = &stu1;
//结构体指针 通过->方式 访问结构体元素:
printf("\r\n");
printf("stu1对象的年龄为:age=%d;\r\n", p->age);
printf("stu1对象的名字为:name=%s;\r\n", p->name);
printf("stu1对象的手机号为:phone=%s;\r\n", p->phone);
printf("stu1对象的身份证号为:idCard=%s;\r\n", p->idCard);
//结构体变量 通过.方式 修改结构体元素的值;
//结构体指针 通过->方式 修改结构体元素的值;
stu1.age = 20;
strcpy_s(stu1.name,10, "李四");
strcpy_s(p->phone,20, "111111");
strcpy_s(p->idCard,20, "222222");
//两种方式修改完元素值,再打印一下
printf("\r\n");
printf("stu1对象的年龄为:age=%d;\r\n", p->age);
printf("stu1对象的名字为:name=%s;\r\n", p->name);
printf("stu1对象的手机号为:phone=%s;\r\n", p->phone);
printf("stu1对象的身份证号为:idCard=%s;\r\n", p->idCard);
//小知识点:
char name[10] = "owen";
//字符数组 只有初始化时候可以直接赋值字符串
//其他地方赋值时 只能用字符串函数去拷贝
printf("\r\n name: %s", name);
strcpy_s(name,10, "tom");//字符数组更改值
printf("\r\n name: %s", name);
return 0;
}
Visual Studio编译器为了确保数据的安全性,经常在原有上加一个_s。例如这个代码中的strcpy,在Visual Studio要写成strcpy_s,需要加入一个参数来确定安全范围。
五、malloc 动态申请内存
比如申请100个int空间大小内存:
p = (int *)malloc(sizeof(int) * 100);
1.malloc特点
1.malloc()里面是指定需要的字节的数目;
2.申请的也是连续的存储空间;
返回的是首个元素的地址,返回值类型一定要强转进行明确。
3.类比于一个p指针开始的一维数组;
它可以通过p[i]方式访问里面数据;
也可以使用 *(p+i)方式访问里面数据;
4.空间不够了,还能使用realloc();动态扩充
5.空间不用时候,通过free(p); 手动释放。
2.使用一维数组a[100]与使用malloc区别:
1. 数组是定义时候,编译必须要明确它的大小,
并且当数组空间使用完时,不能再动态去改变其
空间大小;
2. 存储位置不一样
局部变量数组开辟在栈;
全局变量数组开辟在静态区;
malloc开辟空间在堆;
证明malloc开辟空间在堆
#include <stdio.h>
#include <string.h>
#include <malloc.h>
//malloc 手动申请内存与手动释放
typedef struct {
int* p;
} Node; //自定义一个结构体类型
//malloc开辟内存。
//验证一下 函数返回后,开辟的内存还是存在的,
//直到自己手动释放了,申请的内存才会清除
void initNode(Node *node_p) {
//手动开辟内存 并赋值给node的p
node_p->p = (int*)malloc(sizeof(int) * 10);
if (node_p->p == nullptr) {
return;
}
//并把申请出来的连续的内存 赋值一下:
int value = 1;
//把申请出来的10个连续的int的空间 赋值1-10
for (int i = 0; i < 10; i++) {
*(node_p->p + i) = value++;
}
}
int main() {
Node node;//声明一个node对象
//调用函数,
initNode(&node);///注意这里传入的是node对象的地址
//打印一下node变量的值
printf("node的p连续的值为:");
for (int i = 0; i < 10; i++) {
printf(" %d", *(node.p + i));
}
//虽然initNode函数返回了但是 证明申请的内存还是存在的
//使用完,这里手动释放一下;
free(node.p);
}
运行结果:
通过该程序运行结果可以看出37行 initNode(&node)调用后,41-43行依然可以得到运行结果,所以可以证明验证malloc手动申请的空间在堆内存,需要手动释放内存。