C语言集训-陈明-第五次作业

208 阅读5分钟

1>视频中的内容

一、指针

1.指针定义

  • 地址
    1.内存单元的编号
    2.从零开始的非负整数
    3.范围问题(对于4g内存)

    首先明白:一个地址=一个内存单元=一个字节 cpu和内存条之间有32根地址线 一根地址线由低电压(0),高电压(1)形式控制,则可以控制2个存储单元 而有32根,根据排列组合,则有2^32种(0和1形式),则就可以控制2^32个存储单元 又知道2^10字节=1kb,2^10kb=1M,2^10M=GB;所以1GB=2^30字节 则2^30*4字节=4GB

  • 补充知识

cpu和内存条之间具体关系

cpu,内存条之间由三种线联系
1.控制线(控制数据的传输方向(写入,存储))
2.数据线(传输数据)
3.地址线(传输地址)
他们之间具体联系:cpu接受指令,得到一系列地址,然后通过地址线发送地址找到其在内存条当中的具体位置,然后通过控制线对该地址的进行操作(操作即为读取该地址存储的数据或者是将数据写入),然后将数据传输到cpu进行处理(或者是存储数据到该地址)

  • 指针 指针就是地址,地址就是指针
    指针变量和指针是两个不同的概念
    指针变量就是存放地址(内存单元编号)的变量
    指针(地址)的本质就是一个操作受限的非负整数
    (操作受限是指指针只能进行减法(减法表示两地址之间距离(对于连续地址而言)),不能加乘除(加乘除无意义))

2.指针重要性的体现

1、表示一些复杂的数据结构
2、快速的传递数据
3、是函数返回一个以上的值(重点
4、能直接访问硬件(因为是对地址进行操作,而硬件的内涵就是地址)
5、能够方便的处理字符串
6、是理解面向对面语言中的引用基础

3.指针的分类

基本指针

  • int * p p是指针变量名字(p又名指针变量),而int * 表示p只能存放int类型变量的地址
  • p=&i 1、p保存了i的地址,因此p指向i
    2、p不是i,i也不是p,两者地址独立不同,修改p的值并不影响i的值,反之也成立
    3、如果一个指针变量指向某一其他变量,则* 指针变量 就完全等同于普通变量(即*p=i,所以只要出现了*p就可以与i互换)
  • p+1的含义 p+1表示地址加定义P的数据类型的字节数
    如double * p,加了P+1,其地址加了8

image.png

指针和一维数组

  • 为什么要和数组联系起来 因为当需要在其他函数中使用main函数中的数组时,调用是发送值,不可能我把数组元素全部发送,可想工程很大,而我们用了指针,那就只需发送两个值即可(地址,数组长度)
    ………………
  • 一维数组名 一维数组名是个指针常量(切不可当作变量)
    它存放的是一维数组第一个元素的地址即(a(常量)与a[0](变量)的地址相同)

image.png

  • 确定一个一维数组需要两个参数 第一个元素的地址
    数组长度

image.png

  • 用指针在其他函数输出主函数中的数组问题
void f(int *q, int len)
{
    int i;
    for (i = 0; i < len; i++)
    {
        printf("%d ", q[i]);//这里q[i]等价于*(q+i)   (等价于*(a+i)等价于a[i],但这里不能用,因为局部变量)
    }
}
int main()
{
    int a[5] = {1, 2, 3, 4, 5};
    f(a,5);
    return 0;
}

image.png

  • 其他注意点 1.q[i]=a[i]=*(q+i)=*(a+i),他们可以相互转换(指内容一个改变,另一个跟着改变),但要知道他们的本质还是不同的,变量地址也是不一样的
    2.p=a(数组名),则p指向的字节数由定义p的类型所决定的,如int * p就表示p指向4个字节数。而p存放的地址,为p指向的字节数中首个字节数的地址(单元编号),即int 类型变量的地址是4个存储单元其首个单元编号。

指针和函数

为什么指针和函数联系起来: 当模块化写代码时,想要通过调用函数实现改变其他函数内的已经定义变量内容。我们知道调用函数只能返回一个值进行赋值,且从一个函数到另一个函数,我们只能发送本函数变量的数据,而不能直接改变本函数变量的值。所以我们运用指针,发送该变量的地址,即可在其他函数内直接对其地址所代表的变量进行操作
总结而言,指针是函数之间的桥梁

如看下面几个例子,我们来清楚明白指针对函数的功能
经典例题(交换数值)
错误案例:

QQ图片20211203151150.jpg

错误点:形参和实参以为一致

QQ图片20211203151146.jpg

错误点(重):没有准确的理解什么是发送。调用swap_2函数,发送的是a,b地址,但在swap_2函数中,却只是将形参p,q中存放ab的地址进行交换,但并不影响a,b的内容(值)

正确案例:

QQ图片20211203151136.jpg 发送的是a,b的地址,然后在swap_2函数中p=a,q=b进行交换,则内存条中,其对应地址的内容发生改变。
如何准确理解 * p的含义,就是可以想象直接在内存条搜寻p的内容,找到后进行改变操作(管你定义时的变量在哪个函数中,我们变量所占的地址都是在内存条中,而我的指针就是对内存条进行操作)

多级指针(套娃)

QQ图片20211203152807.jpg

根据如图,做一下解释是更好理解

1p存放的是i的地址,那么*p就代表i变量
    q里面存放的是p的地址,则*q就是p(则有i的地址),则**q就是i
    r里面存放的是q的地址,则*r就是q(则有p的地址),则**r就是p(则有i的地址),则*** r就是i


2、注意他们定义时的区别点
   语法规则:
   int *类型的变量=int *类型的变量=& int类型的变量

   比如 int * q;
        int * p;
        int i;
        则p=q=&i,这样才符合语法规则
    

        int ** q;
        int * p;
        则*q=p或者q=&p;这样才符合语法规则;

指针和数据体结构

4.其他一些相关指针的问题

一定要明白的核心

1、就是p是存放的是其他变量的地址,而p就相当于是“解其中的地址”,首先p要有值必须p中一定存在地址,这样*p才能解其中的地址。这也是多级指针的核心,就是不停的代换,别被绕进去,不要单纯的互换一下,一定要明白其内在的东西,这样才不容易出错
2、当一个变量被定义时,则该变量的地址就已经确立,地址是一定不会变的(在函数,程序还在运行时)。所以我们只能改变变量的值
3.任何指针变量都只占4个字节(如double * p;int * q) (double * p表示p存放的是类型为double类型变量的地址)

*号的意义
sezeof函数(系统函数,在头文件stdio.h里)
  • 格式:sizeof(数据类型或者变量名)
  • 功能:返回值即为该数据类型或该变量所占的字节数
  • 例子:sizeof(int)=4, sizeof(char)=1
    , int i;sizeof(i)=4;
malloc函数(系统函数,在头文件malloc.h里)
  • 格式(常用于指针的空间分配) :int * p=(int *(与前面类型一致))maclloc(字节数(常量))

  • 功能:为指针变量手动分配动态空间

  • 注意点:

    使用前必须加头文件#include <malloc.h>
    malloc函数只有一个形参,并且形参根据定义的类型确定
    字节数表示请求系统为本程序分配几个字节
    malloc函数只返回第一个字节的地址
    
    例如int * p=(int *)maclloc(20)
    则这一语句系统一共为其分配了24个字节,p指针变量占4个,
    而又在内存里分配了20个动态内存单元(20个字节),
    其中p由于是int *类型,则p指向这20个字节前4个字节,
    其p里保存的就是这4个字节 的地址,也就为首个字节的地址
    
    
    

如图所示:

image.png

动态内存的分配
  • 传统数组的缺点

    1.传统定义的数组,该数据的内存程序员无法手动释放
    2.数组长度一旦定义,程序运行时就无法更改数组的长度,也就不能动态的扩充和缩小
    3.A函数中定义的数组,在A函数运行期间可以被其他函数调用,
    但A函数运行完毕后,其中的数组内存将被释放掉,无法在被利用
    
  • 为什么需要动态内存分配

    因为:解决传统数组的三个缺点
    就是当主函数想或者其他函数调用其他函数中的数组时, 如果是静态数组的话就无法实现,而如果定义一个动态数组,则就会一直存在;

  • 动态内存分配举例(动态内存数组的构造(利用指针)) 1.为什么只能用指针构造:

     因为动态内存是命令系统分配的,而我们要找到这个动态内存必须得通过其地址寻找,
     而就是用指针才能找到。所以这就是为什么没有int i=(int) malloc(4)的原因
    

2.如何构造

#include <stdio.h>
#include <malloc.h>
int main()
{
    int len;
    
    printf("输入你想要存放的元素:");
    scanf("%d",&len);
    int * p=(int *)malloc(4*len);
    int i;
    for(i=0;i<len;i++)
    {
        scanf("%d",&*(p+i));//一定不能缺地址符,或者将其换成P+i;
    }
    
    for(i=0;i<len;i++)
    {
        printf("%d\n",*(p+i));
    }
    return 0;
}

其解释如下

QQ图片20211203201525.jpg

  • 静态内存和动态内存的比较 静态变量
    程序运行时,系统自动分配,且由系统自动释放。当一个程序开始运行,从主函数进,从上至下执行语句,该过程按执行语句顺序分配内存的过程叫做进栈,而静态变量释放的时候是本函数终止时,释放该函数内所定义所有的变量的内存的过程叫做出栈,所以进栈是一个一个语句进,出栈是一个一个函数出

QQ图片20211203202742.jpg 动态内存
程序运行时,手动命令系统进行分配,可手动释放,也可系统终止运行时释放。且动态内存的分配过程叫做堆,是连续的空间。

  • 跨函数使用内存问题 1.静态变量无法跨函数,函数终止时就会被释放
    动态内存可跨函数

2.举例
静态变量跨函数:

image.png 这个答案是p会报错,虽然p存放的i的地址还在,但i的内容在函数f终止时已经被释放掉了,所以p没有内容

如何去改变已经定义好的变量的内容

根据现所学
1、直接在本函数对其变量赋值
2、跨函数用指针,必须发送该变量的地址,才能对其变量中的内容进行操作

2>作业

10.12

输出内容为:
8 8
4 8
0 8
2 8

ref有4个元素

ref地址是ref[0],
ref+1是第二个元素的地址,
++ref指向ref中的变量

a:
*ptr为12
*(ptr+2)为14 b: *ptr为 12 *(ptr+2)为16

a:
** ptr : 12
**(ptr+1): 16
b:
** ptr : 12
**(ptr+1): 14