C基础 | 【05】(内存结构以及复合类型)

内存结构

作用域

  • 代码块作用域
  • 函数作用域
  • 文件作用域
局部变量

一般在代码块内部的变量

  • 在该函数内定义,则只在该函数内部有效;
  • 在复合语句中定义,只在复合语句中有效;
    随着调用/语句的结束局部变量的声明周期也结束;
    若无赋初值,则内容随机。注意:若为赋值就将变量作为循环或其他语句判断条件的程序会出错!!!
全局变量
  • extern 数据类型 变量名; 声明一个变量,这个变量在别的文件中已经定义了,这里只是声明,不是定义。
静态变量
静态局部变量

static 局部变量的作用域也是在定义的函数内有效;
static 局部变量的生命周期和程序运行周期一样,同时 static 局部变量的值只初始化一次,但可多次赋值;
若未赋值,则系统自动赋值。int - 0char - NULL

/*使用静态*/ 
#include<stdio.h>

int main()
{
    int i = 0;
    for (i=0; i<10; i++)
    {
        static int j = 100;
        printf("%d\n", j -=i);
    }

    return 0;
}

在这里插入图片描述

/*使用静态*/ 
#include<stdio.h>

int main()
{
    int i = 0;
    for (i=0; i<10; i++)
    {
        int j = 100; 		// 运行每次都初始化j,意味着每次运算j都为100
        printf("%d\n", j -=i);
    }

    return 0;
}

在这里插入图片描述

由以上俩个运行结果可验证静态变量只能初始化一次,但可赋值。

静态全局变量
  • 在函数外定义,作用范围被限制在所定义的文件中;
  • 不同文件静态全局变量阔以重名, 作用域不冲突;
  • 静态全局变量的生命周期和程序运行周期不一样,同时静态全局变量的值只初始化一次。
全局函数和静态函数
静态函数

仅限于当前文件内调用

全局函数

可使用于当前项目中

内存布局

内存分区

在这里插入图片描述

在程序没有加载到内存前,分为代码区(test)、数据区(data)、未初始化数据区(bss)

代码区(程序指令)
数据区(静态区、全局区)

初始化数据

  • 初始化的全局变量
  • 初始化的静态全局变量
  • 初始化的静态局部变量

未初始化数据

  • 未初始化的静态局部变量,默认未0
  • 未初始化的全局变量默认初始值为0
  • 未初始化的静态全局变量,默认未0

字符串常量

栈区
  • 变量
  • 数组
  • 结构体
  • 指针
  • 枚举
  • 函数形参

栈区大小
在不同的操作系统中系统分配给每一个程序的栈区空间大小不同:不是公用,但可被访问

  • 一般windows为1-8M
  • 一般Linux为1-16M
堆区

用于动态内存分配、与物理内存有关

  • 音频文件
  • 视频文件
  • 图像文件
  • 文本文件
  • 大的数据
堆区动态分配,冒泡排序
#include<stdio.h>
#include<time.h>
#include<stdlib.h>

int main()
{
    int i=10, j, k;
    srand((unsigned int)time(NULL));	// 创建随机种子
    int *p = (int *)malloc(sizeof(int) * i);	# 开辟空间
    
    for (j=0; j<i; j++)
    {
        p[j] = rand() % 50; 
    }
    
    for(j=0; j<i-1; j++)
    {
        for(k=0; k<i-j-1; k++)
        {
            if(p[k] < p[k+1])
            {
                int temp = p[k];
                p[k] = p[k+1];
                p[k+1] = temp;
            }
        }
    }
    
    
    for(j =0; j<10; j++)
    {
        printf("%5d", p[j]);
    }
    
    return 0;
}

存储类型总结内存操作
void *memset(void *s, int c, size_t n)
  • 作用:将s的内存区域所有字节都填入c
  • 参数:
    • s:需要操作内存s的首地址;
    • c:类型必须为unsigned char 【0-255】;
    • n:指定需要设置的大小
  • 返回值:s的首地址
void *memcpy(void *dest, const void *src, size_n)
  • 功能:拷贝src中内存中前n个字节到dest内存地址上
  • 参数:
    • dest:目的内存首地址
    • src:源内存地址首地址【dest和src内存空间不可重叠】
    • n:需要拷贝的字节数
  • 返回值:dest的首地址

memcpystrcpy()的区别:

  • 前者是以个数为准,相对广泛。
  • 后者是以\0为结尾。
memmove()

功能用法和memcpy一样。区别再与当destsrc内存空间重叠时,它阔以处理。但效率低。

int memcmp(const void *s1, const void *s2, size_t n)
  • 功能:比较s1和s2所指向内存区域的前n个字节。
  • 参数:
    • s1:s1内存首地址;
    • s2:s2内存首地址;
    • n:比较前n个字节。
  • 返回值:
    • 相等: = 0;
    • 大于:>0;
    • 小于:<0。

复合类型

结构体

初步介绍

定义

struct 结构体名称
{
	// 结构体成员列表
}变量名...; // 可用,分割开定义多个变量名称

// 若前面为定义变量名,后续可用;
struct 结构体名称 变量名; 

赋值

/*第一种*/
struct 结构体名 变量名 = {按列表顺序值};

/*第二种*/
变量名.属性 =;
// 需要注意:若遇到字符串,则需要使用strcpy()进行拷贝;由于字符串常量是一个常量数组,可以读取,但不能修改。

内存

结构体需要根据数据类型进行对齐
【注意】:所以数据类型的大小在内存中存储的地址一定是它的类型的倍数。
如何结构体中节省内存

  • 找出结构体中数据类型最大的
  • 按照排序将每一个都填充满,若有剩余,则下一个刚才满足该空间的,即可在同一个空间,若不满足,则需要下一个空间

例如:

在这里插入图片描述
冒泡排序在结构体中的应用

#include<stdio.h>



/***********定义结构体**********/
struct stu2{
    char name[15];
    float score;
};


/***********冒泡排序**********/
void sortStruct(stu2 *s)
{
    for(int i=0; i<3; i++)
    {
        for(int j=0; j<4-i-1; j++)
        {
            if(s[j].score < s[j+1].score)
            {
                stu2 temp = s[j];
                s[j] = s[j+1];
                s[j+1] = temp;
            }
        }
    }
}


int main()
{
    struct stu2 s[4];       // 创建结构体

    // 循环写入多条数据
    for(int i=0; i<4; i++)
    {
        printf("请输入姓名和成绩:\n");
        scanf("%s%f", s[i].name, &s[i].score);
    }

    // 冒泡排序
    sortStruct(s);

    // 循环取出每一条数据
    for(int i=0; i<4; i++)
    {
        printf("请输入姓名:%s,成绩:%.2f\n", s[i].name, s[i].score);
    }
}

结构体指针
结构体里包含指针类型
	struct stu
	{
	    char *name;
	    float score;
	};

注意

在使用指针的时候,使用malloc为它开辟堆空间,节省了栈空间。在使用完成后需要对它进行释放free

结构体指针

定义

// 结构体
struct stuInfo
{
    char *name;
    int Num;
}stu_;

int main()
{
	// 创建堆空间结构体
	
	// 创建结构体指针
    struct stuInfo *s = &stu_;
	// 申请空间
    s->name = (char *)malloc(sizeof(char)*15);
    
    strcpy(s->name, "虾米");
    s->Num = 001;

    printf("%s, %d", s->name, s->Num);
	
	// 释放空间
    free(s->name);
    return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

struct stuInfo2
{
    char *name;
    float *scores;
};

int main()
{
    struct stuInfo2 *s = (struct stuInfo2 *)malloc(sizeof(struct stuInfo2)*3);

    for (int i=0; i<3; i++)
    {
        s[i].name = (char *)malloc(sizeof(char)*15);
        s[i].scores = (float *)malloc(sizeof(float)*2);

        strcpy(s[i].name, "小米");
        s[i].scores[0] = 20.2;
        s[i].scores[1] = 22.2;
    }

    for(int i=0; i<3; i++)
    {
        printf("%s, %.2f, %.2f\n", s[i].name, s[i].scores[0], s[i].scores[1]);

        free(s[i].name);
        free(s[i].scores);
    }
	//注意:先先释放内部的,在释放结构体
    free(s);




    return 0;
}

获取变量

可以通过 -> 获取指针变量

结构体作为参数

以下包含四种用法

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


struct stuInfo3
{
    char name[15];
    int Num;
};

// *传入结构体:值传递不会改变实参的值。
void funCom(struct stuInfo3 s)
{
    strcpy(s.name, "小米");
    s.Num = 20;

    printf("%s, %d\n", s.name, s.Num);
}

// *传入结构体指针:传递地址会改变值
void funPointer(struct stuInfo3 *s)
{
    strcpy(s->name, "小栏");
    s->Num = 90;
}

// *返回结构体
struct stuInfo3 returnS()
{
    struct stuInfo3 s = {"里斯", 29};

    return s;
};

// *返回结构体指针
struct stuInfo3 *returnPointerS()
{
    struct stuInfo3 s;
    strcpy(s.name, "小");
    s.Num = 01;

    return &s; // 返回地址后,前面的值在栈区被销毁
};

int main()
{
    //struct stuInfo3 s = {"里斯", 19};

    //funCom(s);

    //funPointer(&s);

    //struct stuInfo3 s = returnS();

    struct stuInfo3 *s = returnPointerS();

    printf("%s, %d\n", s->name, s->Num);

    return 0;
}

嵌套结构体

嵌套的意思就是在结构体内部再创建一个结构体。其调用方法只需在往内层 . 一个即可

// 内层
struct inner
{
    int inNum;
    char inName[15];
};
// 外层
struct external
{
    int exAge;
    struct inner sIn;
}; 


int main()
{
    struct external s;
    s.exAge = 20;
    strcpy(s.sIn.inName, "小米");
    s.sIn.inNum = 2;

    printf("%d, %s, %d", s.exAge, s.sIn.inName, s.sIn.inNum);

    return 0;
}

共用体(联合体)
  • 是一个能在同一个存储空间存储不同数据类型的类型;
  • 所占的内存长度等于其最长成员的长度
  • 同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用;
  • 其变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖
  • 其变量的地址和它的各成员的地址都是同一个地址

定义

union 共用体名称 成员列表 共用体变量名

#include<stdio.h>

union 共用体名称
{
	成员列表;
}共用体变量名;
 
枚举
  • 将变量的值一一都列举出来,变量的值只限于列举出来的值的范围内;
  • 枚举值是常量,不能在程序中用赋值语句再次对它赋值;
  • 举元素本身由系统定义了一个表示序号的数值从0开始顺序定义为0,1…。
#include<stdio.h>

enum 枚举名
{
	枚举值表(枚举元素);
}
typedef

自定义数据类型新名字,发生在编译阶段

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Jxiepc

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

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

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

打赏作者

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

抵扣说明:

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

余额充值