从零开始的数据结构——C语言动态内存分配、结构体复习

从零开始的数据结构

由于笔者c语言没有学好,于是选择复习一遍指针,结构体,链表等,本期复习动态内存分配和结构体。

本篇文章提取改编自参考视频

参考视频 浙江大学翁恺老师c语言

动态内存分配复习

由于C99之后可以用变量做数组定义的大小,不再讨论

本篇讨论的是C99之前的操作,重在原理理解

int *a = (int*)malloc(n*sizeof(int));

malloc()括号中要的参数不是多少个单元,而是多少个空间,是以字节为单位的。因此是n*sizeof(int)

//#include 部分底层代码
void *
malloc(size_t size);//这里只需要知道malloc返回的是void *

由于malloc()返回的结果是void*

因此一般要在前面进行一个类型转化

#include
#include
int main(void)
{
   int number;
   int *a;
   int i;
   printf("输入数量");
   scanf("%d",&number);
   a = (int*)malloc(number*sizeof(int));
   for(i=0;i<number;i++){                //指针其实就是数组,所以拿a当数组用
       scanf("%d",&a[i]);
   }
   free(a);                      //malloc要了空间以后,注意“还”空间
   return 0;

}

注意malloc要了内存后,只是一整块内存。计算机自己不能分辨出是什么类型的内存,是什么类型的内存是我们自己决定的(类型转化决定的)

malloc如果申请失败则返回0或者NULL

能分配多少空间是系统决定的


free()

把申请来的空间“还”给系统

申请过的空间,最终都应该要还

只能还申请来的空间的首地址,即原始的地址

#include
#include
int main(void)
{
    void *p;
    p = malloc(100);
    p++;
    free(p);
    
    return 0;
}

这里有个疑问,在看翁恺老师的课时,他说这段代码会提示报错“pointer being was not allocted”

但用我使用CodeBlocks编译运行并无此报错,于是问了我的专业老师,解读为:目前的系统都放宽了这个限制,不提示错误。但是如果p后移了,应该是只释放该地址之后的空间。释放不完全会造成泄露。所以程序员要自己注意保存好空间起始地址,完整释放。

free(NULL) 或者说 free(0) 是没有问题的,这里有个好习惯,任何指针在定义时赋初值为0,原因是这样如果malloc失败后,free§是没有问题的。


结构体复习

声明结构的形式

//形式1
struct point{
   int x;
   int y;
};

struct point p1,p2;

p1 和 p2都是point,里面有x和y的值

//形式2————无名结构
struct{
   int x;
   int y;
}p1,p2

p1和p2是一种无名结构,里面有x和y

//形式3
struct point{
   int x;
   int y;
}p1,p2;

p1和p2都是point,里面有x和y的值

对与第一和第三种形式,都声明了结构point

第二种没有声明point,只是定义了两个变量


#include

struct date{
    int month;
    int day;
    int year;
};                                  //这里注意末尾的;

int main(int argc,char const *argv[])
{
    struct date today;             //名字是today,类型是struck date

    today.month = 9;
    today.day = 28;
    today.year = 2023;

    printf("Today's date is %i-%i-%i.\n",today.year,today.month,today.day);

    return 0;
}

和本地变量一致,在函数内部声明的结构类型只能在函数内部使用,所以通常在函数外部声明结构类型,这样就可以被多个函数所使用


结构的初始化

结构用.运算符和名字访问其成员

today.day;
p1.x;
p1.y

初始化

struct date today = {9,28,2023};   //对应定义结构时的顺序赋值
struct date thismonth = {.month=9, .year=2023};  //不给初值,会根据默认规则填入,这里int类型是0(跟数组的规则类似)

结构运算

要访问整个结构,直接用结构变量的名字

对于整个结构,可以做赋值,取地址,也可以传递给函数参数

p1 = (struct point){5,10}; 
/*相当于 p1.x = 5;
        p1.y = 10;    */
p1 = p2;  
/*相当于p1.x = p2.x;
        p1.y = p2.y;  */
//数组无法做这两种运算

结构指针

和数组不同,结构变量的名字并不是结构变量的地址,必须用&运算符

struct date *pDate = &today;

结构作为函数参数

int numberofDays(struct date d);

整个结构可以作为参数的值传入函数

这时候是在函数内新建一个结构变量,并复制调用者的结构的值

也可以返回一个结构

这与数组完全不同


输入结构

没有直接的方法可以一次scanf一个结构

如果想写一个函数来读入结构,如下

#include

struct point{
    int x;
    int y;
};

void getStruct(struct point);
void output(struct point);
void main()
{
    struct point y = {0,0};
    getStruct(y);
    output(y);
}

void getStruct(struct point p)
{
    scanf("%d",&p.x);
    scanf("%d",&p.y);
    printf("%d,%d",p.x,p.y);
}//结果为自己scanf的两个数

void output(struct point p)
{
    printf("%d,%d",p.x,p.y);
} //结果为0,0

C在函数调用时是传值的

所以函数中的p和main中的y是不同的

在函数读入了p的数值后,没有任何东西返回到main,所以y还是{0,0}

解决方案

之前的方案问题在于传入函数的是外面那个结构的克隆体,而不是指针(传入结构和传入数组是不同的)

可以在这个输入函数中,创建一个临时的结构变量,然后把这个结构返回给调用者

#include

struct point{
    int x;
    int y;
};

struct point getStruct(void);
void output(struct point);

int main(int argc,char const*argv[])
{
    struct point y = {0,0};
    y = getStruct();
    output(y);
}

struct point getStruct(void)
{
    struct point p;
    scanf("%d",&p.x);
    scanf("%d",&p.y);
    printf("%d,%d",p.x,p.y);
    return p;
}

void output(struct point p)
{
    printf("%d,%d",p.x,p.y);
}

但通常,结构指针作为参数(传指针)是更好的方式


指向结构的指针

用 -> 表示指针所指的结构变量中的成员

struct date{
    int month;
    int day;
    int year;
}myday;

struct date *p=&myday;

(*P).month = 12;
p->month = 12;        //这两种写法表示的含义一致

结构数组

struct date dates[100];   //这个数组的每一个元素是一个date的结构变量
struct date dates[]={{4,5,2005},{2,4,2005}} //这是dates[0]和dates[1]的初值

结构中的结构

结构里的变量也可以是结构

struct dateAndTime{
    struct date sdate; //第一个成员变量是结构date
    struct time stime; //第二个成员变量是结构time
};
struct point{
    int x;
    int y;
};
struct rectangle{
    struct point pt1;
    struct point pt2;
};
/*如果有变量
    struct rectangle r;
就可以有:
r.pt1.x  r.pt1.y   对于第一个 . 和第二个 . 它们的左边都是结构
r.pt2.x  r.pt.y */
//如果有变量定义
struct rectangle r,*rp;
rp = &r; // 定义了一个指针指向r
//那么下面的四种形式是等价的:
r.pt1.x;
rp->pt1.x;
(r.pt1).x;
(rp->pt1).x;
//但是没有rp->pt1->x(因为pt1不是指针)

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