数据结构相关内容

1、将今天所敲课堂代码,自己手动实现一遍,并详细注释

一、值传递、地址传递、值返回、地址返回

1、左值:既能放到等号右边又能放到等号左边值,有地址空间

如:变量、堆区申请的空间、地址返回的函数返回值

右值:只能放到等号右边的值,没有地址空间

如:常量、临时值、表达式的结果、值返回的函数返回值

2、值传递:普通变量作为函数参数传递是单向的值传递,只是将实参的值复制一份给形参变量,形参的改变不会影响实参的值,因为所在内存空间不同

void swap(int m, int n)
{
    int temp;
    temp = m; //此时进行三杯水交换的是m和n
    m = n; 
    n = temp;
        printf("m=%d, n=%d\n", m, n);       //34 23
}
int main()
{
    int a=23;
    int b=34;
    swap(a,b);  //函数调用把值给形参,但a和b并没有进行交换
    printf("a=%d,b=%d\n",a,b)     //23 34  
}

如果传递的是地址,被调函数使用指针接收,如果在被调函数中,没有更改指针指向空间中的内容,只改变指向,依然是值传递

void fun(int *p, int *q)
{
    int *temp;
    temp = p; //此时p和q所指向的地址进行交换
    p = q; 
    q = temp;
    printf("*p=%d,*q=%d\n",*p,*q);   //34 23
}
int main()
{
    int a=23;
    int b=34;
    fun(&a,&b);  //函数调用把地址给形参,但a和b并没有进行交换
    printf("a=%d,b=%d\n",a,b);     //23 34  
    return 0;
}

3、地址传递:指针、数组名作为函数参数传递,是地址传递,需要在被调函数中更改指针指向空间中的内容,形参内容的改变,实参也跟着改变

要求:主调函数中传递地址,被调函数中使用指针变量接收,被调函数中更改指针指向的内容

void gun(int *p, int *q)
{
    int temp;
    temp = *p;  //此时交换的是p和q所指向地址中的值
    *p = *q; 
    *q = temp;
    printf("*p=%d,*q=%d\n",*p,*q);   //34 23
}
int main()
{
    int a=23;
    int b=34;
    gun(&a,&b);
    printf("a=%d,b=%d\n",a,b);     //34 23  
    return 0;
}

4、值返回:普通变量通过函数返回值进行返回是单向的值返回,在主调函数中,该函数的返回值只能作为右值使用,不能被重新赋值

int hun()           //int k = 100;
{
    int value = 666;

    return value;
}
int main(int argc, const char *argv[])
{
    int ret = hun();          //值返回的函数返回值只能是右值
    printf("hun() = %d\n", hun());         //666
    return 0;
}

5、地址返回:需要返回生命周期比较长的变量地址(全局变量、静态局部变量、堆区申请空间、主调函数地址传递的空间),该函数的返回值是一个左值,可以直接使用,也可以被重新赋值,被重新赋值后,被调函数中该空间中的内容也跟着改变

int *iun() 
{
    static int value = 999;    //静态变量虽然在函数体内定义,但是不占函数的内存空间
    return &value;//(int *)  //返回静态局部变量的地址
}
int main()
{
    int *ptr = iun();        //地址返回的结果可以作为右值
    *iun() = 555;            //地址返回的结果可以作为左值

    printf("*iun() = %d\n", *iun());        //555
    printf("*ptr = %d\n", *ptr);           //555
    return 0;
}

二、内存分区

数据结构相关内容_第1张图片

#include

int m;                   //未初始化的全局变量,在全局区的.bss段
int n = 520;            //已初始化的全局变量,在全局区的.data段
static int k;          //未初始化的静态变量,在全局区的.bss段
static int l = 666;   //已初始化的静态变量,在全局区的.data段

char arr[100] = "hello world";  //arr数组在全局取的.data段,而"hello world"在.ro段
char *p = "hello";           //指针在.data段,而“hello”在.ro段



int main(int argc, const char *argv[])
{
    double b = 999.0;    //局部变量在栈区申请
    int a;           //局部变量,在栈区申请,初始值为随机值
    //printf("&a = %p, &b = %p\n", &a, &b);
    static int c;         //静态局部变量,在全局区的.bss段申请
    static int d = 520;  //静态局部变量,在全局区的.data段
    char *q = "nihao";   //q在栈区申请的8字节,但是"nihao"在全局区的.ro段
    char e[100] = "hello world";  //数组在栈区申请,"hello world"在全局区的.ro段
    
    int *ptr = (int *)malloc(sizeof(int));  //ptr是在栈区,而申请的空间在堆区
    
    return 0;
}

三、动态内存分配和回收(malloc、free)

C语言中可以使用malloc和free来对堆区空间进行操作

#include          //函数头文件

void *malloc(size_t size);
/*功能:允许程序员手动从堆区空间申请内存
参数:要申请的空间大小,以字节为单位,一般格式为 sizeof(类型名)*n
返回值:是一个万能指针,可以使用强制类型转化为自己想要的类型的指针,如果申请成功,则将堆区申请的空间地址返回,如果申请失败,返回NULL

单个空间内存的申请:数据类型 *指针名    = (数据类型*)malloc(sizeof(数据类型)));
连续内存的申请:  数据类型 *指针名    = (数据类型*)malloc(sizeof(数据类型)*n));*/
int *p1=(int *)malloc(sizeof(int))
int *p1=(int *)malloc(sizeof(int)*5) //类似于数组              

void free(void *ptr);
/*功能:释放程序员从堆区申请的空间
参数:要释放的空间首地址
返回值:无*/     

//释放:free(指针名);
free(p);
p=NULL; 

四、类型重定义

1、使用格式

/*typedef  数据类型名   新名;
举个例子:*/
typedef unsigned long int  uint64;
typedef unsigned int uint32;
typedef unsigned short int  uint16;

2、所学类型

使用变量定义类型

int a;    //定义普通变量
int *p;    //定义指针类型变量
int a[5];    //定义数组类型变量
int *p[5];   //定义指针数组变量
int (*p)[5];    //定义数组指针变量
int (*fun_ptr)(int,int); //定义函数指针变量
int (*fun_ptr_arr[3])(int, int);  //定义函数指针数组的指针变量
int **pptr;  //定义二级指针变量

提取类型

//提取变量的类型就是把变量名去掉剩下的部分
int  ;   //定义普通数据类型
int *;   //定义指针类型
int [5];   //定义数组类型
int *[5];  //定义指针数组类型
int (*)[5];   //定义数组指针类型
int (*)(int,int); //定义函数指针类型
int (*[3])(int, int);   //定义函数指针数组的指针类型
int **;  //定义二级指针类型

给类型重新起名

typedef int A;  //重定义int类型为A类型
typedef int *Ptr;  //重定义int *类型为Ptr
typedef int Arr[5];  //重定义int [5]类型为Arr
typedef int *Ptr_Arr[5];  //重定义int * [5]类型为Ptr_Arr
typedef int (*Arr_Ptr)[5];  //重定义int (*)[5]类型为Arr_Ptr
typedef int (*Fun_Ptr)(int,int);  //重定义int (*)(int)类型为Fun_ptr
typedef int (*Fun_Ptr_Arr[3])(int, int);  //重定义int (* [5])(int)类型为Fun_Ptr_Arr
typedef int **PPtr;  //重定义int **类型为PPtr

给类型起多个名

typedef int *Ptr_i, int32;   
//int32是一个int类型的重命名
//Ptr_i是一个int *类型的重命名

类型重定义与宏定义的区别

宏定义只是单纯的替换,不做任何正确性检测,是一个预处理指令

类型重定义,需要做正确性检测,是一条语句

宏替换发生在预处理阶段,而类型重定义发生在编译阶段

#define ptr_i int* //会将uint32替换成int
typedef int * pptr_i; //类型重定义
int main(int argc, const char *argv[])
{
    ptr_i a,b;  //a是指针类型,b是普通int类型   int *a, b;
    pptr_i m,n; //m和n都是指针类型
    return 0;
}

五、结构体

1、定义

有相同数据类型和不同数据类型构成的集合叫结构体,属于构造数据类型

struct 结构体名
{
    //属性列表
    /*成员类型1 成员变量1;
    成员类型2 成员变量2;
    。。。
    成员类型n 成员变量n;*/
};

//声明一个英雄结构体类型
struct Hero
{
    char name[20];      //姓名
    int Hp;            //血量
    double speed;        //基础位移
    int kill;            //人头数
};//(注意要加分号)

2、结构体变量的定义及初始化

#include

//声明一个英雄结构体类型
struct Hero
{
    char name[20];      //姓名
    int Hp;            //血量
    double speed;        //基础位移
    int kill;            //人头数
}h3 = {"盖伦", 3500, 500, 5};

//练习:定义一个商品类型,成员属性:商品名称(name)、产地(position)、单价(price)、重量(weight)
struct                           //无名结构体
{
    char name[40];       //名称
    char position[40];     //产地
    double price;            //单价
    double weight;           //重量
}g1 = {"三鹿奶粉", "China", 350, 1000};


int main(int argc, const char *argv[])
{

    //使用英雄类型定义一个英雄变量
    struct Hero h1 = {"亚索", 650, 350, 0};            //此时定一个英雄变量h1

    //定义英雄变量,指定某个成员进行赋值
    struct Hero h2 = {.Hp=2000, .speed = 1000};

    return 0;
}

3、结构体变量访问成员

(1)成员运算符 ".",个人读作"的";例如h1.name 表示h1结构体变量的name成员

(2)不要企图通过结构体变量名直接输出所有成员,如果想要输出结构体变量中的成员,需要使用成员运算符一个一个找

(3)相同类型的结构体变量之间是可以之间互相赋值的

(4)也可以对结构体变量取地址运算,其地址跟第一个成员属性的地址保持一致

//声明一个英雄结构体类型
struct Hero
{
    char name[20];      //姓名
    int Hp;            //血量
    double speed;        //基础位移
    int kill;            //人头数
};//h3 = {"盖伦", 3500, 500, 5};定义一个结构体并初始化
int main()
{
    //定义一个英雄变量h1并赋值
    struct hero h1={"亚索",650,350,0};
    //输出英雄变量h1中的内容
    printf("h1.name = %s\n", h1.name);
    printf("h1.Hp = %d\n", h1.Hp);
    printf("h1.speed = %lf\n", h1.speed);
    printf("h1.kill = %d\n", h1.kill);
}

4、结构体指针访问成员

结构体指针访问成员使用运算符"->"

使用格式:指针名->属性名

//声明一个英雄结构体类型
struct Hero
{
    char name[20];      //姓名
    int Hp;            //血量
    double speed;        //基础位移
    int kill;            //人头数
};
int main()
{
 //在堆区申请一个英雄类型,完成初始化并输出相应的属性
    struct Hero *ptr = (struct Hero*)malloc(sizeof(struct Hero));
    //给英雄名字赋值
    strcpy(ptr->name,"亚瑟");    //给姓名赋值(因为是字符串赋值用到strcpy函数)
    ptr->Hp = 3000;     //给hp属性赋值
    ptr->speed = 350;      
    ptr->kill = 3;

    //输出英雄指针指向堆区空间中的内容
    printf("英雄信息为:%s  %d  %.2lf  %d\n", ptr->name, ptr->Hp, ptr->speed, ptr->kill);

}

 2、整理思维导图

数据结构相关内容_第2张图片

 

你可能感兴趣的:(数据结构,算法)