数据机构涉及C语言知识

一、内存管理

内存四大区:

  1. 代码区:

存放函数体的二进制代码,由操作系统进行管理。当.exe可执行程序执行时,加载到内存代码区。字符串常量就存在这个区。注意:代码区只可读,不可修改。

  1. 栈区:

局部定义的变量,函数的形参等存放于栈区。此区特点:由编译器自动分配释放,函数返回时,内存会被自动回收。

  1. 堆区:

相对于栈区最大的区别就是,该区需要程序员手动分配和释放,若程序员不释放,在程序完全结束后也会被系统回收。例如:用户malloc手动申请的空间,用完需要手动free()释放。

  1. 静态区:

全局变量,static修饰的局部变量。

二、一维数组

例如int a[100];

定义一个数组,100表示这个数组存储100个int类型的元素

数组的特点

  1. 数组开辟的是连续的空间

数组是多个相同类型的数据按照一定顺序排列所得,数组的长度一旦确认,就不能修改。这和java的集合有很大的区别,但Java中的集合底层原理其实也是数组,只是底层通过链表的扩容实现的动态数组。

  1. 数组的访问方式

下标访问: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;

当我们在定义时候看到 *仅仅是告诉编译器这是一个指针;

  1. 给指针赋值

int a;

p=&a; &这里是取地址的意思

即p这个指针指向a的内存地址

当我们学习过c++可以知道&在c++有另一种意思,就是引用。这是c++为了简化c语言中函数调用要写好多*所提出的简便写法。

  1. 取指针的所指的内容

*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手动申请的空间在堆内存,需要手动释放内存。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

北硝

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

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

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

打赏作者

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

抵扣说明:

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

余额充值