第十篇,数组初始化和堆空间申请释放,以及结构体,联合体的定义,计算内存大小以及枚举类型的详细说明。

一、数组清零方式。
1、定义数组同时初始化0。
char A[10] = {0};   ---> 剩余没有赋值的成员都会赋值为0
缺点:在程序中只能定义一次数组,清零只是只有一次。

2、清空某段内存空间的数据。   --->  bzero()   --> man 3 bzero
功能: bzero - write zero-valued bytes
    //写入一些零值到内存空间上
  
  #include <strings.h>

  void bzero(void *s, size_t n);

参数:
    s:需要清空的内存空间的地址
    n:清空的长度

返回值:无
特点:多次调用,多次清零。

#include <stdio.h>
#include <strings.h>

int main(int argc,char *argv[])
{
    char A[10] = {"hello"};
    
    A[8] = 'k';
    A[9] = 'l';
    
    int i;
    for(i=0;i<10;i++)
    {
        printf("A[%d]:%c\n",i,A[i]);
    }
    
    bzero(A,sizeof(A));
    
    for(i=0;i<10;i++)
    {
        printf("A[%d]:%c\n",i,A[i]);
    }
        
    return 0;
}


二、堆区空间的申请与释放。
1、堆区空间的特点?
堆空间,不像栈区那样通过申请变量而得到栈区空间,而是通过写代码调用malloc()函数去申请得到。

代码定义了局部变量   -->  等价于在栈区申请空间。
代码定义了全局变量   -->  等价于在bss段/data段申请空间。
代码定义"hello"      -->  等价于在常量区申请空间。
代码出现malloc函数   -->  等价于在堆区申请空间。

堆区空间: 主动申请,主动释放。

注意:如果在一个函数中申请了堆区空间,请问在函数返回时,需要拿橡皮擦出来吗?
      不需要,因为堆区空间不会因为函数返回而释放。

2、如何申请堆区空间?
1)申请堆区空间  --  malloc()   -->  man 3 malloc
功能: allocate dynamic memory
    申请堆区空间

头文件:     
    #include <stdlib.h>

函数原型:
    void *malloc(size_t size);

参数:
    size:需要申请堆区空间的字节数

返回值:
    成功:堆区空间的起始地址   --> 由于这个地址是void*的,所以后面使用这个地址需要强制转换为某种类型才可以使用。
    失败:NULL

2)释放堆区空间。  --  free()  -->  man 3 free
功能: free dynamic memory
    释放堆区空间

头文件:     
    #include <stdlib.h>

函数原型:
        void free(void *ptr);

参数:
    ptr:需要释放的那段堆区空间的地址

返回值:无

用户在程序中必须主动释放堆区空间,如果不主动释放,那么在程序结束时,也会主动释放。

-------------------------------------------
#include <stdio.h>
#include <stdlib.h>

int main(int argc,char *argv[])
{
    int *p = malloc(8);  //等价于在堆区申请了4个字节的空间
    
    p[0] = 10;
    p[1] = 20;
    
    printf("p[0] = %d\n",p[0]);   //p[0] = *(p+0) = *p
    printf("p[1] = %d\n",p[1]); 
    
    free(p);
    
    return 0;
}
-------------------------------------------

3、如果堆区空间释放之后,还能不能访问这段堆空间。
能,但是那片空间的数据有可能已经更改。

#include <stdio.h>
#include <stdlib.h>

int main(int argc,char *argv[])
{
    //申请空间: 该空间从不属于你变成属于你。
    int *p = malloc(20);
    
    p[0] = 10;
    p[1] = 20;
    p[2] = 30;
    p[3] = 40;
    p[4] = 50;
    
    printf("p[0] = %d\n",p[0]);
    printf("p[1] = %d\n",p[1]);
    printf("p[2] = %d\n",p[2]);
    printf("p[3] = %d\n",p[3]);
    printf("p[4] = %d\n",p[4]);
    
    //释放空间: 该空间从属于你变成不属于你。
    free(p);
    p = NULL;   //以后写代码,为了安全起见,最好写上这句话,目的就是为了我们再次去访问该堆区空间。
    
    printf("p[0] = %d\n",p[0]);
    printf("p[1] = %d\n",p[1]);
    printf("p[2] = %d\n",p[2]);
    printf("p[3] = %d\n",p[3]);
    printf("p[4] = %d\n",p[4]);
    
    return 0;
}


    练习1: 在一个自定义函数中申请堆空间,测试看看函数返回时候,这个堆区空间还在不在。


#include <stdio.h>
#include <stdlib.h>

int *func(int x)
{
    int *p = malloc(4);
    p[0] = x;
    return p;
}

int main(int argc,char *argv[])
{
    int a = 100;
    int *p = func(a);
    printf("p[0] = %d\n",p[0]);
    
    free(p);
    p = NULL;
    
    return 0;
}

三、关于堆栈的易错点。
问题:以下代码是否有问题?如果没有问题,请画出内存图并指出每句话是什么意思,如果有问题,请指出问题所在。

1、
char A[10] = "hello";  //在内存空间中连续申请10个字节的空间,然后使用变量A间接访问这片空间,然后将常量区中的"hello"字符串拷贝到这个变量A所代表的空间上
strcpy(A,"world");     //把常量区的"world"字符串拷贝到变量A所代表的空间。

2、
char *p = "hello";
strcpy(p,"world");

3、
char A[10];
char *p = A;
strcpy(p,"hello");

4、
char A[10];
char *p = A;
p = "hello";

5、
char *p = malloc(sizeof(char)*10);
p = "hello";

6、
char *p = malloc(sizeof(char)*10);
strcpy(p,"hello");


第5与第6的区别:
第5,一开始p是指向堆区,然后接下来p就指向常量区。
第6,一开始p是指向堆区,然后接下来p依然是指向堆区,但是把常量区hello这个字符串拷贝到p所指向的堆区空间。


四、结构体。
1、什么是结构体?
多个相同类型的变量放在一个集合中,那么这个集合叫做数组。   -->  int A[5]   --> 5个int类型的数据
多个不同类型的变量放在一个集合中,那么这个集合叫做结构体。

结构体中可以包含哪些类型的数据呢?
1)基本数据类型:char/short/int/long/float/double
2)非基本数据类型:数组/指针/结构体

结构体中不可以包含哪些类型的数据呢?
不可以在结构体中定义函数。

总结: 结构体可以理解为就是一种新的数据类型,这种新类型有什么东西组成的呢?而是由我们用户自定义的。
       也就是说,你想这个结构体里面有什么东西组成,由你来决定。

2、结构体既然说是属于用户自定义的类型,那么这种类型如何定义的?
关键词:struct
模型:
struct 结构体名字{
    /* 结构体的组成变量 */
    ....;
    ....;
    ....;
    ....;
};   --> 定义结构体之后,记住{}后面有一个分号。

例如:
struct A{
    char a;
    int b;
    double c;
    char B[10];
};

这个"struct A"就是新类型的名字,这种类型是由一个字符变量,一个整型变量,一个浮点型变量,一个字符数组组成。

3、结构体的出现是为了解决什么问题?
如果一个"东西",需要描述这个东西的类型非常多,那么就可以考虑使用结构体类型。
例如:要描述关老师这个人,需要姓名,年龄,电话号码..,那么就可以考虑使用结构体。

struct person{
    char name[10];
    int age;
    char tel[12];
};
  
 “struct person”就是一种新的数据类型,专门用于描述一个人的。


4、如何定义结构体变量?
1)先声明好结构体的样子。

struct person{
    char name[10];
    int age;
    char tel[12];
};

系统就会知道"struct person"这种新类型由“char name[10] + int age + char tel[12]”这三个东西组成。
"struct person"就是新类型的数据类型的名字。

2)struct person就是新类型的数据类型的名字。

3)定义结构体变量。
定义变量公式:   数据类型     +     变量名
          struct person         ggy;     --> ggy变量里面有三个成员


5、如何定义结构体指针?
1)先声明好指针指向的目标。
   struct person ggy;

2)定义结构体指针。
   struct person *p = NULL;

3)赋值。
   p = &ggy;

4)解引用。
   *p = *(&ggy) = ggy

 
6、结构体变量如何访问成员?
1)先声明好结构体的样子。

struct person{
    char name[10];
    int age;
    char tel[12];
};

2)定义结构体变量。

struct person ggy;

3)结构体变量是使用"."来访问成员的。
strcpy(ggy.name,"ggy");
ggy.age = 10;
strcpy(ggy.tel,"10086");

7、结构体指针如何访问成员?
1)先声明好结构体的样子。

struct person{
    char name[10];
    int age;
    char tel[12];
};


2)定义结构体变量。

struct person ggy;

3)定义结构体指针。

struct person *p = &ggy;

4)结构体指针使用"->"来访问成员。
strcpy(p->name,"ggy");
p->age = 10;
strcpy(p->tel,"10086");

还可以这样:
strcpy((*p).name,"ggy");
(*p).age = 10;
strcpy((*p).tel,"10086");
           

8、结构体成员赋值。
1)使用变量或者指针来对单个成员赋值。
struct usr_data{
    char name[10];
    int age;
};

struct usr_data a;
struct usr_data *p = &a;

a.age = 18;
p->age = 18;

2)使用初始化列表。
struct usr_data a = {"ggy",18};
struct usr_data a = {"ggy"};   --> 剩余没有赋值的成员如果是普通变量,则是0,如果指针变量,则是NULL。

3)整个结构体赋值。
struct usr_data a = {"ggy",18};
struct usr_data b;
b = a;


    练习1: 研究结构体数组怎么写?

定义数组的步骤:例子1: 定义具有5个int类型数据的数组。

1)确定一个数组名。                                              A
2)确定数组中元素的个数,使用[]括号括住它,然后跟在数组名后面     A[5]
3)确定数组每一个元素是什么                                      int x
4)将第3步的结果的变量名去掉                                     int 
5)将第4步的结果写在第2步结果的前面就可以                        int A[5]   --> 最终结果

A[0]   -->  int
A[1]   -->  int
A[2]   -->  int
A[3]   -->  int
A[4]   -->  int


例子2: 定义5个struct person类型数据的数组。
struct person{
    char name[10];
    int age;
};

1)确定一个数组名                                                B
2)确定数组中元素的个数,使用[]括号括住它,然后跟在数组名后面      B[5]
3)确定数组每一个元素是什么                                      struct person x
4)将第3步的结果的变量名去掉                                     struct person
5)将第4步的结果写在第2步结果的前面就可以                         struct person B[5]

B[0]  --> struct person
B[1]  --> struct person
B[2]  --> struct person
B[3]  --> struct person
B[4]  --> struct person


    练习2: 做一个通讯录,里面存放三个同学的信息,每一个同学信息有学号,姓名,年龄,电话号码。
        程序运行之后,先依次对三个同学的学号,姓名,年龄,电话号码进行注册(键盘输入信息),全部同学的信息都注册完了之后,就输出全部同学的信息。


#include <stdio.h>
#include <strings.h>

struct person{
    int num;       //学号
    char name[10]; //姓名
    int age;       //年龄
    char tel[121]; //电话号码
};

int main(int argc,char *argv[])
{
    //1. 定义具有3个struct person类型的数组
    struct person addr_list[3];
    bzero(addr_list,sizeof(addr_list));
    //addr_list[0] -> 第一个同学   addr_list[1] -> 第二个同学  addr_list[2] -> 第三个同学
    
    //2. 依次对同学进行赋值。
    int i;
    for(i=0;i<3;i++)
    {
        printf("pls input %d std num:",i+1);
        scanf("%d",&addr_list[i].num);
        
        printf("pls input %d std name:",i+1);
        scanf("%s",addr_list[i].name);
        
        printf("pls input %d std age:",i+1);
        scanf("%d",&addr_list[i].age);
        
        printf("pls input %d std tel:",i+1);
        scanf("%s",addr_list[i].tel);
    }
    
    //3. 输出全部同学的信息
    for(i=0;i<3;i++)
    {
        printf("%d  %s  %d  %s\n",addr_list[i].num,addr_list[i].name,addr_list[i].age,addr_list[i].tel);
    }
    
    return 0;
}

五、计算结构体在内存中占用的字节数。
int             -> 4个字节
double          -> 8个字节
struct person   -> ?个字节

例子1: 
struct data{
    char name[24];     24  32          ---> 32
    long a;
};

例子2:
struct data{
    long a;           32   24     --> 32
    char name[24];
};

例子3:
struct data{
    double a;     16   9   8   --> 16
    char b;
};

例子4:
struct data{
    char a;
    short c;
    char b;
    short d;
    char e;
    char f;      8       12   2    -->  10
};

例子5:
struct data{
    char a;
    char b;
    short c;
    short d;
    char e;
    char f;     8       --> 8
};

例子6:
struct data{
    char a;
    short c;
    short d;
    char e;
    char f;
    char g;
    short h;
    char w;   11  14   16   --> 14  
};

例子7:
struct data{
    int a;
    double b;
    char c;       24   13  
};

例子8:
struct data{
    int a;
    char c;         16  
    double b;    
};

例子9:
struct data{
    char c;         16 
    int a; 
    double b;    
};

   练习4: 计算以下结构体在内存中占用多少个字节?    82   64   104   94   92   144   88   96    84   86   80    97   120   -->  96

struct data{
    int a;
    char b;
    int (*px)[5];
    char A[9];
    short c;
    double e;
    float d;
    double *p;
    int B[3];
    char (*p)(char,int);
    char q;
    short w;
    char o;
    int t;    
};

char  -> 可以放在任意一个位置
short -> 12 34 56 78   -> 不能 23 45 67
int  float  -> 1234  5678    -> 不能 2345 3456 4567
double      -> 12345678

 

六、联合体。
1、为什么会有联合体的出现?
为了解决结构体占用空间较大的情况。

2、联合体如何定义?
结构体:
struct data{
    int a;
    double b;
    char c;    -->  占用24个字节
};

联合体关键词:union,只需要将struct修改为union即可。

联合体
union data{
    int a;
    double b;
    char c;     -->  占用8个字节 
};

3、结论。
1)在联合体中,所有的成员的起始地址都是一样的。
#include <stdio.h>

union data{
    int a;
    double b;
    char c;    
};

int main()
{
    printf("%ld\n",sizeof(union data));
    
    union data ggy;
    printf("%p\n",&ggy.a);
    printf("%p\n",&ggy.b);
    printf("%p\n",&ggy.c);
    
    return 0;
}

运行结果:
gec@ubuntu:/mnt/hgfs/GZ2180/01 C语言/10/code$ ./union
8
0x7fff5fa15c60
0x7fff5fa15c60
0x7fff5fa15c60


2)联合体的大小是以联合体最大的那个成员说了算。(看看是否要补0)

#include <stdio.h>

union data{
    int a;
    char A[9];    
    char c;
};

int main()
{
    printf("%ld\n",sizeof(union data));  //12
    
    return 0;
}

3)不能同时使用联合体的成员。
#include <stdio.h>

union data{
    int a;
    double b;
    char c;        
};

int main()
{
    union data ggy = {10,3.14,'x'};
    
    printf("%ld\n",sizeof(union data)); //8
    
    return 0;
}

4)联合体与结构体一样,都是作为函数参数的,传递一个结构体变量/联合体变量过去,其实就是把整个值传递过去。

#include <stdio.h>

struct data{
    int a;
    double b;
    char c;        
};

int func(struct data *p)  // B = A
{
    //printf("%d\n",B.a);
    printf("p->a = %d\n",p->a);
    return 0;
}

int main()
{
    struct data A;
    A.a = 10;
    func(&A);
    
    printf("%ld\n",sizeof(struct data)); //8
    
    return 0;
}

注意:一般很少传递结构体变量作为函数参数,因为结构体变量太大的,一般都是传递结构体的地址,使用一个结构体的指针变量来接着它,因为无论结构体多大,结构体指针都是占用8个字节。

七、枚举类型。
1、什么是枚举类型?
其实枚举类型就是一些整型变量,意义就是给常量一个身份。
例如:
1 -> success
0 -> warning
-1-> error

2、枚举类型作用场景是什么?
1)作用于switch语句。
2)函数返回值。

旧的写法:
void func()
{
    if(xxxx)
        return 1;
    else if(xxxx)
        return 0;
    else
        return -1;
}

使用枚举类型之后:
void func()
{
    if(xxxx)
        return success;   --> 这样做的好处:别人看起来就知道返回值是什么情况。
    else if(xxxx)
        return warning;
    else
        return error;
}

3、如何定义枚举类型?
关键词:enum
模型:
enum mydata{
    success = 1,
    warning = 0,
    error = -1,
};


#include <stdio.h>

enum mydata{
    success = 1,
    warning = 0,
    error = -1,
};

int func(int n)
{
    if(n == 18)
        return success;
    else if(n < 18)
        return warning;
    else    
        return error;
}

int main(int argc,char *argv[])
{
    int age = 18;
    int ret = func(age);
    if(ret == 1)
        printf("success!\n");
    else if(ret == 0)
        printf("warning!\n");
    else
        printf("error!\n");
    
    return 0;
}

4、赋值的问题。
enum mydata{
    success = 1,    //1
    warning = 0,    //0
    error = -1,     //-1
};

enum mydata{
    success,     //0     如果没有赋值,则第一个成员从0开始。
    warning,     //1     后面没有赋值的成员都会在前一个成员基础上+1
    error,       //2
};

enum mydata{
    success = 10,     //10  
    warning = 20,     //20
    error,            //21      后面没有赋值的成员都会在前一个成员基础上+1 
};

enum mydata{
    success = 10,    //10
    warning = 9,     //9
    error,           //10
}


 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

肖爱Kun

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

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

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

打赏作者

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

抵扣说明:

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

余额充值