自学内容网 自学内容网

C语言——段管理

一、复习一下

1.指针的概念?

  • 存储地址的基本数据类型

2.什么是数据类型?

  • 在内存空间上框出一定空间的模子,比如int在内存空间上框出4个字节,int就是基本的数据类型

3.基本数据类型,多个数据类型,多个同样的基本数据类型?

  • 基本数据类型:类似于int,float,char等,c语言内部已经定义好(包括大小和名字)的模子
  • 多个数据类型:有多个基本数据类型组成的连续空间,比如struct结构体
  • 多个同样的数据类型:多个同样的基本数据类型组合成的连续空间,c语言认为这是数据结构要研究的东西,不属于c语言的研究范围,所以c里面没有这个概念。
  • 这里涉及到一个概念叫语法糖,编译器帮程序员封装好的一些内存访问和内存设计的行为。

二、数组

1.定义: 多个同样数据类型组成的连续空间。

  • 数组名是首地址标记,标签,是地址常量,不能被赋值,只能初始化。
  • 数组不是动态生成的,而是程序一旦编译好后,就已经固定的

2.初始化和赋值:数据一旦初始化便不可以改变,赋值可以改变数据

    //赋值
    int arr[3];
    arr={10,20,30};//赋值{}常量空间、连续空间的语法糖
    //初始化
    int arr[3]={10,20,30};

3.默认和重载:相同的东西在不同的语境下表达的意思不同,比如运算符“+”的默认和重载,在默认语境下是数学意义上的加法(默认),在字符串语境下是字符串的连接(重载)。

int a=10+b;//加法行为  默认定义好的 c语言
           //运算符重载  用户自定义  c++

?4.约束空间的结束标志(字符,非字符):

  • 字符连续空间:0  双引号" "自带一个结束标志0。
  • void test03(){
        int a=0x616263;
        printf("%x\n",a);
        //字符连续空间
        // 无结束标志
        char b[]={[0]=0x64,[1]='c',[2]='b',[3]='a'};//没有结束标志,输出dcbacba
        printf("%s\n",b);
        //有结束标志
        char c[]={[0]=0x64,[1]='c',[2]='b',[3]='a',[4]=0};//有结束标志,输出dcba
        printf("%s\n",c);
    }

  • 非字符连续空间:读到0时不会停止,但必须要有长度。比如读取xls文件时,遇到0并不会停止读取数据,但xls 文件有长度限制。

5.为什么需要约束空间的结束标志?

  • c语言一旦知道了地址就可以任意访问,为了保证数据的正常访问,需要约束空间的结束标志。

6.C语言的维护连续空间的方法(字符,非字符):

字符连续空间   按照字节逐一处理,并且由语言来约定结束标志是0
非字符连续空间  按照任意长度来处理,结束标志没有要求,但必须约定长度

7.strcpy和memcpy:

  • strcpy():是字符类型连续空间,有两个参数,没有长度参数
  • memcpy(): 非字符类型连续空间,有三个参数,有长度参数(即约束空的间的结束标志)

8.C语言就是玩内存,内存管理大师

9.sizeof大小:

int a,b,c;
struct abc a1,a2,a3;

int c[5];     sizeof(c)=20 //所有int元素的大小5*4    
              sizeof(c[2])=4//一个int元素的大小
              sizeof(c+2)=8 //首地址偏移2个元素地址后的地址的大小
 
int *d;       sizeof(*d)=8//指针(即地址)的大小,一般是4位或8位,由操作系统决定


//写成代码
test06(){ 
int c[5];
int *d;
//指针(即地址)的大小,一般是4位或8位,由操作系统决定
printf("c: %p\n", (void*)&c); // 打印数组c的地址
printf("c[2]: %p\n", (void*)&c[2]); // 打印数组c中第三个元素的地址
printf("c+2: %p\n", (void*)(c+2)); // 打印数组c首地址偏移2个int元素后的地址
printf("*d: %p\n", (void*)d); // 打印指针d指向的地址的内容

printf("Size of c: %zu\n", sizeof(c)); // 打印数组c的大小
printf("Size of c[2]: %zu\n", sizeof(c[2])); // 打印数组c中第三个元素的大小
printf("Size of c+2: %zu\n", sizeof(c+2)); // 打印数组c首地址偏移2个int元素后的大小
printf("Size of *d: %zu\n", sizeof(*d)); // 打印指针d指向的地址的内容的大小
}

10.?sizeof和strlen():

  • sizeof():是字符类型连续空间的关键词,
  • strlen():是非字符类型连续空间的函数,

三、内存的分段管理

1.经典的段错误(面试的时候可能会问到):

#include<stdio.h>

//经典段错误
void test01(){
    char s[]="abcd";
    //char*s="abcd";//经典段错误
    s[1]='1';
    printf("%s\n",s);//输出a1cd

    //*(s+1)和s[1]是一个意思
    *(s+1)='1';
    printf("%s\n",s);//输出a1cd
}

int main(){
    test01();

    return 0;
}

2.越界访问就是段错误

3.分段

  • 只读段:存储常量,代码,函数。只要程序不终止,永远存在。
  • 读/写段:存储全局变量。只要程序不终止,永远存在。
  • 段:

4.func1和func2的例子:

//典型错误
int *func1(){
    int abc;//abc进栈
    return &abc;//返回abc的时候, abc的地址已经还给系统(可以理解为,abc去别的地方了)
//此时&abc,取到的地址实际上已经不是abc的地址了
//return &abc可以理解为,abc的地址换了,但还是用原来的地址去找abc,所以找不到很正常)
}

//正确的例子
int *func2(){
//用malloc从堆区申请100字节,将首地址传给p
    int *p=malloc(100);
    return p;//返回p时,虽然p的地址返已经还给系统,
//但p传给函数的是malloc的首地址(不是变量*p的地址),
//而malloc此时还在,所以函数可以接收到malloc的地址
}

5.内存泄漏与内存溢出(面试时可能会问到):

  • 内存泄漏:变量内存被释放后,越界访问变量地址。一直执行程序,没有释放内存
  • char *rec=func2();//申请内存
        rec[x]=xxxxxxx;//利用内存
        free(rec);//释放内存,如果一直不释放内存就会造成内存泄漏

  • 内存溢出:
    网卡传递消息
    用户能接收的消息大小: char buf[10] //大小为10B
    通过函数传递消息:     ssize_t recv(参数1,void *buffer,参数2, 参数3,参数4)
    网卡要传递的消息大小: 200B
    
    这种情况下,网卡传递消息给用户,就形成了内存溢出

6.分段管理:每个段都有自己的权限,由操作系统分配:

  • 一旦应用程序非法访问某个段,os(操作系统)就会终止这个程序,并抛出“segmentation fault”错误

原文地址:https://blog.csdn.net/m0_73669390/article/details/143699943

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!