C语言作业-熊家旗-第五次作业

218 阅读7分钟

学习笔记

指针

一、指针的重要性

表示一些复杂的数据结构;
快速的传递数据;
使函数返回一个以上的值;
能直接访问硬件;
能够方便的处理字符串;
是理解面向对象语言中引用的基础。

总结:指针是C语言的灵魂

二、指针的定义

1.地址

内存单元的编号
从零开始的非负整数
范围:0--4G-1

2.指针

指针就是地址,地址就是指针;
指针变量就是存放内存单元编号的变量,或者说指针变量就是存放地址的变量指针和指针变量是两个不同的概念;
但是要注意:通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样; 指针的本质就是一个操作受限的非负整数。

三、指针的分类

1.基本类型的指针

int * p ; //p是变量的名字,int * 表示p变量存放的是int类型变量的地址
//int * p;不表示定义了一个名字叫做*p的变量
// int * p;应该这样理解:p是变量名,p变量的数据类型是int *类型
// 所谓int *类型实际就是存放int变量地址的类型

int i = 3;
int j;
p = &i;

1.p保存了i的地址,因此p指向i;
2. p不是i,i也不是p,更准确的说:修改p的值不影响i的值,修改i的值也不会影响p的值;
3.如果一个指针变量指向了某个普通变量,则指针变量就完全等同于普通变量。
例子:
如果p是个指针变量,并且p存放了普通变量i的地址,则p指向了普通变量i
*p 就完全等同于 i
在所有出现i的地方都可以替换成 *p
*p最准确的解释是:却表示的是以*p的内容为地址的变量

经典算法:互换两个数字

#include <stdio.h>
void huhuan_3(int *, int *);
int main(void)
{
    int a = 3;
    int b = 5;

    huhuan_3(&a, &b);
    printf("a = %d b= %d\n", a, b);

    return 0;
}
void huhuan_3(int *p, int *q)
{
    int t;

    t = *p;
    *p = *q;
    *q = t;
}

QQ截图20211201144643.png

附注:
* 的含义:
1.乘法
2.定义指针变量
3.指针运算符(该运算符放在已经定义好的指针变量的前面)

如何通过被调函数修改主调函数普通变量的值

1.实参必须为该普通变量的地址
2.形参必须为指针变量
3.在被调函数中通过
*形参名=.....的方式就可以修改主调函数相关变量的值

指针和数组

1.指针和一维数组

一维数组名:
一维数组名是个指针常量
它存放的是一维数组第一个元素的地址

2.下标和指针的关系

如果p是个指针变量,则p[i]永远等价于*(p+i)

3.确定一个一维数组需要两个参数:(如果一个函数要处理一个一维数组,则需要接收该数组的哪些信息)

数组第一个元素的地址;
数组的长度。

代码一:

#include <stdio.h>
void f(int *pArr, int len)
{
    for (int i = 0; i < len; i++)
        printf("%d ", *(pArr + i));
    printf("\n");
}

int main(void)
{
    int a[5] = {1, 2, 3, 4, 5};
    int b[6] = {-1, -2, -3, -4, -5, -6};
    int c[100] = {11, 22, 33, 44, 55, 66, 77, 88, 99};

    f(a, 5);
    f(b, 6);
    f(c, 100);

    return 0;
}

QQ截图20211202205831.png

代码二:

#include <stdio.h>
void f(int *pArr, int len)
{
    pArr[3] = 88;
    //for (int i = 0; i < len; i++)
    printf("%d ", *(pArr+3));
    //printf("\n");
}

int main(void)
{
    int a[5] = {1, 2, 3, 4, 5};
    //int b[6] = {-1, -2, -3, -4, -5, -6};
    //int c[100] = {11, 22, 33, 44, 55, 66, 77, 88, 99};

    f(a, 5);
    //f(b, 6);
    //f(c, 100);

    return 0;
}

QQ截图20211202210136.png

代码三:

#include <stdio.h>
void f(int *pArr, int len)
{
    //pArr[3] = 88;
    for (int i = 0; i < len; i++)
        printf("%d ", pArr[i]);
    printf("\n");
}

int main(void)
{
    int a[5] = {1, 2, 3, 4, 5};
    f(a, 5);

    return 0;
}

QQ截图20211202210605.png

4.指针变量的运算

指针变量不能相加不能相乘也不能相除;
如果两个指针变量指向的是同一块连续空间中的不同存储单元,则这两个指针变量才可以相减。

#include <stdio.h>
int main()
{
    int i = 5;
    int j = 7;
    int * p = &i;
    int * q = &j;
    int a[5];
    p = &a[1];
    q = &a[3];
        printf("p和q所指向的单元相隔%d个单元\n", q - p);
    return 0;
}

QQ截图20211202204713.png

5.一个指针变量到底占几个字节

sizeof(数据类型)

功能:返回值就是该数据类型所占的字节数
例子: sizeof(int)= 4 、sizeof (char) =1 、sizeof(double)=8

sizeof(变量名) 功能:返回值是该变量所占的字节数

假设p指向char类型变量(1个字节)
假设q指向int类型变量(4个字节)
假设r指向double类型变量(8个字节)
请问:pqr本身所占的字节数是否一样?

#include <stdio.h>

int main(void)
{
    int i = 5;
    char ch = 'A';
    double x = 66.6;
    char * p = &ch;
    int * q = &i;
    double * r = &x;
    printf("%d %d %d\n", sizeof(p), sizeof(q), sizeof(r));

    return 0;
}
     

QQ截图20211202203713.png

总结:

一个指针变量,无论它指向的变量占几个字节该指针变量本身只占四个字节;
一个变量的地址是用该变量首字节的地址来表示。

指针和函数

指针和结构体

多级指针

int i= 10;
int * p = &i;
int ** q = &p;
int *** p = &q;

p只能存放int类型变量的地址;
q是int *类型,所谓int *类型就是指q只能存放int 类型变量的地址;
int *
r = &q;//r是int *类型,所谓int *类型就是指r只能存放int类型变量的地址;
l/r = &p;//error 因为r是int料类型,r只能存放int *类型变量的地址;
只有**是i,r或*r或***r代表的都不是i。

专题:动态内存分配

1.数组的缺点:

1)组长度必须事先制定,且只能是常整数,不能是变量; 2)传统形式定义的数组,该数组的内存程序员无法手动释放; (在一个函数运行期间,系统为该函数中数组所分配的空间会一直存在,直到该函数运行完毕时,数组的空间才会被系统释放)
3)组的长度一旦定义,其长度就不能在更改; (数组的长度不能在函数运行的过程中动态的扩充或缩小)
4)函数定义的数组,在A函数运行期间可以被其它函使用,但A函数运行完毕之后,A函数中的数组将无法在被其他函数使用。
(传统方式定义的数组不能跨函数使用)

2.动态内存分配举例:态数组的构造

假设动态构造一个int型一维数组
t *= (int *)malloc(iint len);\

1)语句分配了两块内存,一块内存是动态分配的,总共1en个字节,另一块是静态分配的,并且这块静态内存是p变量本身所占的内存,总共4个字节

2)alloc函数能且只能返回第一个字节的地址,所以我们需要把这个无任何实际意义的第一个字节的地址(俗称干地址)转化为一个有实际意义的地址,因此malloc前面必须加(数据类型*),表示把这个无实际意义的第一个字节的地址。

3)freep (p); 表示把p所指向的内存给释放掉p本身的内存是静态的,不能由程序员手动释放,p本身的内存只能在p变量所在的函数运行终止时由系统自动释放

#include <stdio.h>
#include <malloc.h>

int main(void)
{
    int a[5];
    int len;
    int * pArr;
    int i;

    printf("请输入你的元素的个数: ");
    scanf("%d ", &len);
    pArr = (int *)malloc(4 * len);

    for (i = 0; i < len ; i++)
        scanf("%d", &pArr[i]);
    printf("一维数组的内容是 \n");
    for (i = 0; i < len; i++)
        printf("%d\n", pArr[i]);

    return 0;
}

QQ截图20211202230633.png

静态内存和动态内存的比较

1.内存是由系统自动分配,由系统自动释放静态内存是在栈分配的;
2.内存是由程序员手动分配,手动释放动态内存是在堆分配的。

跨函数使用内存的问题

1.内存不可以跨函数使用

2.内存在函数执行期间可以被其它函数使用,静态内存在函数执行完毕之后就不能再被其他函数使用了

课后作业

10.12 第二题

四个元素

第三题

地址是1512368;
ref指向该数组的首元素;
ref + 1指向该数组的1486第2个元素。

第四题

a.12、16 b.12、14

QQ截图20211202235416.png

第五题

a.**ptr=12,**(ptr+1)=16

QQ截图20211202235702.png

b.**ptr=12,**(ptr+1)=14

QQ截图20211203000013.png