• 我的第六篇博客--指针


    这个作业属于哪个班级 C语言--网络2011/2012
    这个作业的地址 C博客作业05--指针(https://i.cnblogs.com/posts/edit)
    这个作业的目标 学习指针相关内容
    姓名 唐宇悦

    0.展示PTA总分

    1.本章学习总结

    1.1 指针定义、指针相关运算、指针做函数参数。

    指针定义:类型名 *指针变量名

    例如:

    • int *s是指向整型变量的指针变量

    • float *d是指向浮点变量的指针变量

    • char *g;是指向字符变量的指针变量
      注意一个指针变量只能指向同类型的变量

    定义多个指针变量时,每一个指针变量前面必须加上

    指针变量必须赋值后才能使用

    指针相关运算

    通过取地址运算符&和间接访问运算符*完成
    如:int *p,a=3;
    p=&a
    表示将整形变量a的地址赋给整形指针p,使指针p指向变量a。
    指针的类型和它所指向变量的类型必须相同

    • 指针 +/- 整数 = 指针所对应的内存空间与它所指向的类型乘以整数相加减。eg. p++ --> p = p + 1

    • 指针 - 指针 = 两个指针相差的数据个数。

    • 指针的比较:如果两个指针变量指向同一个数组的元素,那么指向前面元素的指针变量小于指向后面元素的指针变量。

    • 指针加指针没有实际意义

    指针做函数参数

    如:交换两个变量的普通做法

    void swap(int a, int b)
    {
        int temp;  //临时变量
        temp = a;
        a = b;
        b = temp;
    }
    int main()
    {
        int a = 66, b = 99;
        swap(a, b);
        printf("a = %d, b = %d
    ", a, b);
        return 0;
    }
    

    但是运行后发现a,b的值并未交换,这是因为 swap() 函数内部的 a、b 和 main() 函数内部的 a、b 是不同的变量,占用不同的内存,它们除了名字一样,没有其他任何关系,swap() 交换的是它内部 a、b 的值,不会影响它外部(main() 内部) a、b 的值。
    所以改用指针变量作函数参数后就可轻松解决这个问题

    void swap(int *p1, int *p2){
        int temp;  //临时变量
        temp = *p1;
        *p1 = *p2;
        *p2 = temp;
    }
    int main(){
        int a = 66, b = 99;
        swap(&a, &b);
        printf("a = %d, b = %d
    ", a, b);
        return 0;
    }
    

    调用 swap() 函数时,将变量 a、b 的地址分别赋值给 p1、p2,这样 p1、p2 代表的就是变量 a、b 本身,交换 p1、p2 的值也就是交换 a、b 的值。函数运行结束后虽然会将 p1、p2 销毁,但它对外部 a、b 造成的影响是“持久化”的,不会随着函数的结束而“恢复原样”。

    需要注意的是临时变量 temp,它的作用特别重要,因为执行*p1 = *p2;语句后 a 的值会被 b 的值覆盖,如果不先将 a 的值保存起来以后就找不到了

    1.2 字符指针

    定义:指向字符型数据的指针变量,每个字符串在内存中都占用一段连续的存储空间,并有唯一确定的首地址。即将字符串的首地址赋值给字符指针,可让字符指针指向一个字符串。
    字符串相关函数

    strlen()
    计算字符串s的长度
    
    strcmp
    int strcmp (const char* str1,const char* str2)
    功能:字符串比较
    返回值:若参数s1和s2字符串相同则返回0,s1若大于s2则返回大于0的值,s1若小于s2则返回小于0的值
    
    字符串复制函数:strcpy
    

    1.3 指针做函数返回值

    函数返回值必须用同类型的变量来接受,也就是说,返回值为指针值的函数的返回值必须赋值给同类型的指针变量。

    1.4 动态内存分配

    • 首先为什么要动态分配内存

    1:因为内存太宝贵。

    2:如果全部是静止内存不能释放,对于小的程序可以运行完毕。但是对于大的程序,还没运行完,内存就要被占用完,此时就要发生内存泄露。

    3:给定一个占用内存可变大小的变量(假设是数组的长度len),给该变量通过函数动态分配内存后,分配内存的大小是根据数组的长度len决定的,假定用户输入len的大小是5,系统就会动态的给该数组分配长度为5的内存,该段代码运行结束后,系统调用free()函数释放分配的内存,然后接着运行剩下的程序。换句话说,动态分配内存可以根据需要去申请内存,用完后就还回去,让需要的程序用。

    1.malloc()

    void * malloc(size_t size)
    1).malloc()函数会向堆中申请一片连续的可用内存空间

    2).若申请成功 ,,返回指向这片内存空间的指针 ,若失败 ,则会返回NULL, 所以我们在用malloc()函数开辟动态内存之后, 一定要判断函数返回值是否为NULL.

    3).返回值的类型为void * 型, malloc()函数并不知道连续开辟的size个字节是存储什么类型数据的 ,所以需要我们自行决定 ,方法是在malloc()前加强制转 ,转化成我们所需类型 ,如:
    (int)malloc(sizeof(int)n).

    4).如果size为0, 此行为是未定义的, 会发生未知错误, 取决于编译器
    例如:

    int *p = NULL;
    int n = 0;
    scanf("%d", &n);
    p = (int*)malloc(sizeof(int) * n);
    if(p != NULL){
        //....需要进行的操作
    }
    

    2.free()

    void free(void* ptr)
    在堆中申请的内存空间不会像在栈中存储的局部变量一样 ,函数调用完会自动释放内存 , 如果我们不手动释放, 直到程序运行结束才会释放, 这样就可能会造成内存泄漏, 即堆中这片内存中的数据已经不再使用, 但它一直占着这片空间, (通俗说就是就是占着茅坑不拉屎), 所以当我们申请的动态内存不再使用时 ,一定要及时释放 .

    1).如果ptr没有指向使用动态内存分配函数分配的内存空间,则会导致未定义的行为.

    2).如果ptr是空指针,则该函数不执行任何操作。

    3).此函数不会更改ptr本身的值,因此它仍指向相同(现在已经无效)的位置(内存)

    4).在free()函数之后需要将ptr再置空 ,即ptr = NULL;如果不将ptr置空的话 ,后面程序如果再通过ptr会访问到已经释放过无效的或者已经被回收再利用的内存, 为保证程序的健壮性, 一般我们都要写ptr = NULL

    注意 : free()不能重复释放一块内存, 如:

    free(ptr);
    free(ptr);
    

    这个是错的, 已经释放过的内存不能重复释放, 会出现内存错误 .

    free()具体用法, 举个例子 :

    int *p = NULL;
    int n = 0;
    scanf("%d", &n);
    p = (int*)malloc(sizeof(int) * n);
    if(p != NULL){
        //....需要进行的操作
    }
    //操作完成 ,不再使用这片内存空间
    free(p);
    p = NULL;
    

    堆区和栈区区别。

    1)栈(satck):由系统自动分配。例如,声明在函数中一个局部变量int b;系统自动在栈中为b开辟空间。

    (2)堆(heap):需程序员自己申请(调用malloc,realloc,calloc),并指明大小,并由程序员进行释放。

    1.5 指针数组及其应用

    在实现排序功能的时候,一般都是通过交换值的形式,通过循环逐渐得到我们想要的顺序。但是有时候排序通过值的交换实现起来比较麻烦,因此我们可以引用指针数组,通过交换地址的方式来得到我们想要的顺序。

    例:char a[4][10]={"CHINA","china","ABC","abcdef"} 将四个字符串通过从小到大的顺序依次排序,如果用交换值的形式,实现起来就会比较麻烦。但是通过指针数组实现起来就很容易。

      char  *b[4]={a[0],a[1],a[2],a[3]}   定义一个指针数组分别依次指向字符数组,如下图所示
    

    通过交换指针地址来实现从小到大的排序,指针数组b[0],指向的是最小字符串,b[3]指向最大字符串,如图所示:

    1.6 二级指针

    二级指针也是一个变量,它指向的一定是“指针的地址”。对比下,一级指针指向的是普通变量的地址,二级指针指向的是“一级指针的地址”,以此类推,三级指针对应的是“二级指针的地址”。举例代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char *argv[]) {
    	//定义普通变量、一级指针、二级指针,不初始化 
    	int a;
     
    	int *p1;
     
    	int **p2;
     
    	//对前面的普通指针、一级指针、二级指针进行初始化赋值 
    	a = 10;
     
    	p1 = &a;
     
    	p2 = &p1;
    	
    	printf("a = %d
    ", a); 
    	printf("&a = %d
    ", &a); 
    	printf("p1 = %d
    ", p1);
    	printf("*p1 = %d
    ", *p1);
    	printf("p2 = %d
    ", p2);
    	printf("*p2 = %d
    ", *p2);
    	printf("**p2 = %d
    ", **p2);
    	printf("&p2 = %d
    ", &p2);
     
    	return 0;
    }
    

    1.7 行指针、列指针

    • 行指针:指向某一行,不指向具体的元素。

    • 列指针:指向行中具体的元素。

    也就是说,列指针只要在同一行,不管它们指向行中的哪个元素,它们的行地址都是在同一行的地址,所以它们的行地址都是一样的。

    所以,

      &列指针--->行指针
    

    相反地可以推出:

      *行指针---->列指针
    

    2.PTA实验作业

    2.1
    字符串反正序连接
    2.1.2 代码截图

    2.1.3 找一份同学代码(尽量找思路和自己差距较大同学代码)比较,说明各自代码特点。

    同学的代码调用了strlen函数减少了很多繁琐的部分,比较起来更通俗易懂且可读性强。

    2.2 题目名2
    合并2个有序数组
    2.2.2 代码截图

    2.3 题目名3
    说反话-加强版
    2.3.2 代码截图


    2.3.3 请说明和超星视频做法区别,各自优缺点。

  • 相关阅读:
    对于 redux 的一些理解-1.基础
    css 优化
    HTML 理解标签
    css 理解盒模型
    json2.js JSON解析程序
    DOM 核心
    居中
    Director.js
    jquery-2.0.3 源码分析 整体架构
    Zookeeper安装
  • 原文地址:https://www.cnblogs.com/CHINATYY/p/14199132.html
Copyright © 2020-2023  润新知