likes
comments
collection
share

c语言集训-刘佳雨-第五次作业

作者站长头像
站长
· 阅读数 43

学习笔记

指针:

  1. 指针的重要性:
  • 表示一些复杂的数据结构
  • 快递的传递数据
  • 使函数返回一个以上的值
  • 能直接访问硬件
  • 能够方便的处理字符串
  • 是理解面对对象语言中引用的基础 总结:指针是c语言的灵魂
  1. 指针的定义:
#include <stdio.h>

int main()
{
    int *p;     //p是变量的名字int *表示p变量存放的是int类型变量的地址
    int i = 3;
    int j;
    p = &i;     //p保存了i的地址,因此p指向i  
                //p不是ii也不是p,准确来说:修改pi的值并不会影响两者的值
                //如果一个指针变量指向了某个普通变量,则*指针变量就完全等同于普通变量
                      即:*p = i,在任何地方*pi都可互相替换
                //*p就是以p的内容为地址的变量
    j = *p;//即j = i;

    printf("i = %d, j = %d\n", i, j);

    return 0;
}


  • 地址:
    • 内存单元的编号
    • 从零开始的非负整数
    • 范围:4G (0--4G-1)
  • 指针:
    • 指针就是地址,地址就是指针
    • 指针变量就是存放内存单元编号的变量,或者说指针变量就是存放地址的变量
    • 指针和指针变量是两个不同的概念,虽然我们叙述时通常会将指针变量简称为指针,但实际两者含义并不相同
    • 指针的本质就是一个操作受限的非负整数
  1. 指针的分类:
  • *基本类型指针
    • 指针常见错误:
#include <stdio.h>

    int main(void)
{
    int i = 5;
    int *p;
    int *q;

    p = &i;
    p = q;             //q是垃圾值,赋值给p,p也变成垃圾值
    printf("%d\n", *p);/*q的空间属于本程序所以本程序可以读写q的内容,但是如果q内部是垃圾值,则本程序就不能读写*q的内容
                       因为此时*q所代表的内存单元的控制权限并没有分配给本程序,所以程序运行到11行就会出错*/
    return 0;
}

运行结果c语言集训-刘佳雨-第五次作业 互换两个数字

#include <stdio.h>
void f(int *p, int *q)
{
    int t;         //如果要互换*p和*q的值,则t必须定义成int,不能定义成int *,否则语法将会出错

    t = *p;        //p是int *,*p是int类型
    *p = *q;
    *q = t;
}

int main(void)
{
    int a = 3;
    int b = 5;

    f(&a, &b);
    printf("a = %d, b = %d\n", a, b);  //f(*p, *q)是错误的,f(a, b)也是错误的

    return 0;
}

运行结果c语言集训-刘佳雨-第五次作业 附注: * 的含义: 1. 乘法 2. 定义指针变量: int *p; //定义了一个名字叫p的变量,int 表示p只能存放int类型的变量 3. 指针运算符: 该运算符放在已经定义好的指针变量的前面,如果p是一个已经定义好的指针变量,则p表示以p的内容为地址的变量。 如何通过被调函数修改主函数普通变量的值: 1. 实参必须为该普通变量的地址 2. 形参必须为指针变量 3. 在被调函数中通过: *形参名 = ...... 的方式就可以修改主调函数相关变量的值

  • 指针和数组
数组元素的地址   
数组定义: int a[8]={1,2,3,4,5,6,7,8};    
指针定义: int *p; p= &a[0];//p指向数组首元素     
指针定义: int* p; p= a;//p仍指向数组首元素      
指针定义: int * q; q= &a[2]; // q指向a[2]     
希望表达pq之间的联系       
它们都指向同一数组中的元素    
指针与整数加减运算    
p为指向整数数组中某元素的指针,i为整数,则p+i表示指针向后滑动i个整数,p -i表示指针向前滑动i个整数
  • 指针和一维数组:
    • 数组名: 一维数组名是个指针常量,它存放的是一维数组第一个元素的地址

    • 下标和指针的关系: 如果p是个指针变量,则p[i]永远等价于*(p+i)

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

    • 一个指针变量到底占几个字节: sizeof(数据类型) 功能:返回值就是该数据类型所占的字节数 例如:sizeof(int) = 4 ,sizeof(char) = 1 , sizeof(double) = 8. sizeof(变量名) 功能:返回值是该变量所占的字节数

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

  • 指针和二维数组
  • 指针和函数
  • *指针和结构体
  • 多级指针: int i = 10; int *p = &i; int **q = &p; int k = &q; 则k = i

专题:动态内存分配

1. 传统数组(静态数组)的缺点:

  • 数组长度必须事先制定,且只能是常整数,不能是变量 例如:int a[5]; //ok int len = 5; int a[len]; //error
  • 传统形式定义的数组,该数组的内存程序员无法手动释放 在一个函数运行期间,系统为该函数中数组所分配的空间会一直存在,直到该函数运行完毕时,数组的空间才会被系统释放
  • 数组的长度一旦定义,就无法再被改变 数组的长度不能再函数运行的过程中动态的扩充或缩小
  • A函数定义的数组,在A函数运行期间可以被其它函数使用,但A函数运行完毕后,A函数中的数组将无法再被其它函数使用 传统方式定义的数组不能跨函数使用 代码示例:
#include <stdio.h>

void g(int * pArr, int len)
{
    pArr[2] = 88;
}

void f(void)
{
    int a[5] = {1, 2, 3, 4, 5};   //20个字节的存储空间程序员无法手动编程释放它,它只能在本函数运行完毕时由系统自动释放

    g(a, 5);
    printf("%d\n", a[2]);
}

int main(void)
{
    f();

    return 0;
}

2. 为什么需要动态分配内存: 动态数组很好的解决了传统数组(静态数组)的这4个缺陷

  • malloc函数的用法1:
#include <stdio.h>
#include <malloc.h>      //malloc是memory(内存)allocate(分配)的缩写

int main(void)
{
    int i = 5;           //分配了4个字节(静态分配)
    int*p = (int *)malloc(4);//动态分配
    
    *p = 5;              //*p代表的是一个int类型的变量,只不过*p这个整形变量的内存分配方式和int i = 5的分配方式不同
    free(p);             //free(P)表示把p所指向的内容释放掉,但p变量本身的内存是静态分配的,不能由程序员手动释放
    printf("释放\n");

    return 0;
}
/*1. 要使用malloc函数,必须添加malloc.h头文件
  2. malloc函数只有一个形参,并且是整形形参  
  3. 4表示请求系统为本程序分配4个字节   
  4. malloc函数只能返回第一个字节的地址  
  5. int *p一行分配了8个字节,其中p变量占4个字节,p所指向的内存也占4个字节  
  6. p变量本身所占的内存是静态分配的,p所指向的内容是动态分配的
*/
  • malloc函数的用法2:
#include <stdio.h>
#include <malloc.h>

void f(int *q)
{
    *q = 100;
    free(q);        //把q所指向的内存释放掉,   本句必须注释掉,否则会导致第17行的代码出错
}

int main()
{
    int *p = (int *)malloc(sizeof(int));    //sizeof(int)返回值是int所占的字节数
    *p = 10;

    printf("%d\n", *p);
    f(p);       //p是int *类型
    printf("%d\n", *p);

    return 0;
}

c语言集训-刘佳雨-第五次作业

c语言集训-刘佳雨-第五次作业 3. 动态分配内存举例_动态一维数组的构造:

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

int main(void)
{
    int a[5];      //如果int占4个字节的话,则本数组总共包含有20个字节,每4个字节被当做了一个int变量来使用
    int len;
    int *pArr;
    int i;

    //动态的构造一维数组:
    printf("请输入你要存放的元素的个数:");
    scanf ("%d", &len);
    pArr = (int *)malloc(4 * len);     //本行动态的构造了一个一维数组,该一维数组的长度是len,数组名是pArr,每个元素的类型是int类型    类似于int pArr[len]

    //对一维数组进行操作:(对动态一维数组进行赋值)
    for (i = 0; i < len; ++i)
        scanf("%d", &pArr[i]);

    //对一维数组进行输出:
    printf("一维数组的内容是:\n");
    for (i = 0; i < len; ++i)
        printf("%d\n", pArr[i]);
    //free(pArr);     //可通过该行手动的释放掉动态分配的数组

    return 0;
}

运行结果: c语言集训-刘佳雨-第五次作业 4. 静态内存和动态内存的比较: 静态内存是由系统自动分配,自动释放 且静态内存是在分配的

而动态内存是由程序员手动分配,手动释放 动态内存是在分配的 5. 跨函数使用内存的问题: 静态内存无法跨函数使用

#include <stdio.h>

void f(int **q)    //q是个指针变量,无论q是什么类型的指针变量,都只占4个字节
{
    int i = 5;
    *q = &i;   //等价于p = &i;
}

int main(void)
{
    int *p;

    f(&p);
    printf("%d\n", *p);   //本句语法没有问题,但逻辑上有问题

    return 0;
}

运行结果: c语言集训-刘佳雨-第五次作业 动态内存可以跨函数使用

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

void f(int **q)   
{
    int i = 5;
    *q = (int *)malloc(sizeof(int));  //sizeof(数据类型)返回值是该数据类型所占的字节
    **q = 5;      //等价于p = (int *)malloc(sizeof(int))
}

int main(void)
{
    int *p;

    f(&p);
    printf("%d\n", *p);   

    return 0;
}

运行结果: c语言集训-刘佳雨-第五次作业

代码实操

指针使函数返回一个以上的值:

#include <stdio.h>

void g(int *p, int *q)
{
    *p = 1;
    *q = 2;
}

int main(void)
{
    int a = 3, b = 5;
    g(&a, &b);
    printf("%d %d\n", a, b);
    return 0;
}

运行结果c语言集训-刘佳雨-第五次作业

确定一个一维数组需要几个参数:

#include <stdio.h>

void f(int * pArr, int len)
{
    pArr[3] = 88;
}

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

    printf("%d\n", a[3]);             //第五行的pArr[3]与12、14行的a[3]是同一个变量
    f(a, 6);
    
    return 0;
}

运行结果c语言集训-刘佳雨-第五次作业

#include <stdio.h>

void f(int *pArr, int len) //f函数可以输出任何一个一维数组的内容
{
    int i;

    for (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] = {1, 99, 22, 33};

    f(a, 5);          //a是int *类型
    f(b, 6);
    f(c, 100);
    return 0;
}

运行结果c语言集训-刘佳雨-第五次作业 结论: 确定一个一维数组需要两个参数: 数组第一个元素的地址; 数组的长度

课后习题:

1. 输出结果如图: c语言集训-刘佳雨-第五次作业 2. ref有4个元素 3. ref的地址为0X61FE00 ref是数组名,指向数组的首元素,ref + 1表示指针向后滑动一个整数即指向第二个元素 ++ref不能表示什么,因为ref是常量而不是变量 4. a. *ptr的值是12 , *(ptr + 2)的值是16 b. *ptr的值是12 , *(ptr + 2)的值是14 5. a. **ptr的值是12 , **(ptr + 1)的值是16 b. **ptr的值是12 , **(ptr + 1)的值是14

转载自:https://juejin.cn/post/7037493943709204510
评论
请登录