zoukankan      html  css  js  c++  java
  • C博客作业05-指针

    这个作业属于哪个班级 C语言--网络2011/2012
    这个作业的地址 C博客作业05-指针
    这个作业的目标 学习指针相关内容
    姓名 陈宇杭

    0.展示PTA总分

    1.本章学习总结

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

    • \指针定义

    众所周知,C语言中存在着许多变量,他们大多都被赋上了一个数值并存放在内存中;而指针,也是一个变量,只是他所赋上的值是(某一个)变量的地址
    指针类型变量在使用前需要声明类型,如:char *p;//声明一个字符型指针变量
    此时的指针 (*)p 并未赋值,为野指针;未防止程序报错,在声明一个指针型变量时最好给其赋上 NULL ,即为 0 ,使其成为一个空指针

    • \指针相关运算

      • 地址运算
        指针虽然也是一个变量,但是有关他的运算比较特殊
        指针所赋的值是一个地址,在进行赋值时,需要在变量前加上取地址运算符,如 p = &a//此时指针p指向a的地址
        在进行自增(p++)运算时,是将指针p所指向的地址向后移动一位;在进行自减(p--)运算时,是将指针p所指向的地址向前移动一位
        而两个指针变量无法进行相加(+)运算,但可以进行相减(-)运算,所得差值为两个指针的距离

      • 数值运算
        而指针也可以进行取所指向地址上的值来进行运算,并且可以修改所指向地址上的值
        取地址上的值需要一元运算符 * ,如 p = &a*p = *p + 1 等价于 a = a + 1

      • 小贴士
        若指针所赋的值是一个数组,那么指针所指的位置默认是指向此数组的头地址
        但需要注意的是 *p++(*p)++ 所表示的意思并不相同;
        自增符(++)与指针(*)的优先度相同,而单目运算符是从右向左进行判定;
        所以 *p++ 是p所指向的下一位的地址的值;而 (*p)++ 是p所指向的值加一;

    • \指针做函数参数
      指针变量也同样可以作为函数的参数,传入的是指针所指的地址
      void merge(int* a, int m, int* b, int n);//此时传入函数的是对应的地址

      因为传入的数据是地址,所以在函数中对此地址上的数值的修改是可以保存的;
      同时在多次调用函数时,使用指针传参通常比直接传入数值参数更加节省性能;

    1.2 字符指针

    字符串即是含有结束符的字符数组,在赋值给字符型指针时,赋给的是字符串的首地址

    指针进行自增(++)运算时,每自增一次,指针就指向原先的地址的下一位地址;

    在C 标准库中,<string.h>库中贴心地包含有关于字符串的相关函数

    序号 函数声明 函数功能
    1 void *memchr(const void *str, int c, size_t n) 在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 c(一个无符号字符)的位置。
    2 int memcmp(const void *str1, const void *str2, size_t n) 把 str1 和 str2 的前 n 个字节进行比较。
    3 void *memcpy(void *dest, const void *src, size_t n) 从 src 复制 n 个字符到 dest。
    4 void *memmove(void *dest, const void *src, size_t n) 另一个用于从 src 复制 n 个字符到 dest 的函数。
    5 void *memset(void *str, int c, size_t n) 复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。
    6 char *strcat(char *dest, const char *src) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。
    7 char *strncat(char *dest, const char *src, size_t n) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。
    8 char *strchr(const char *str, int c) 在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。
    9 int strcmp(const char *str1, const char *str2) 把 str1 所指向的字符串和 str2 所指向的字符串进行比较,返回差值。
    10 int strncmp(const char *str1, const char *str2, size_t n) 把 str1 和 str2 进行比较,最多比较前 n 个字节,返回差值。
    11 int strcoll(const char *str1, const char *str2) 把 str1 和 str2 进行比较,结果取决于 LC_COLLATE 的位置设置。
    12 char *strcpy(char *dest, const char *src) 把 src 所指向的字符串复制到 dest。
    13 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。
    14 size_t strcspn(const char *str1, const char *str2) 检索字符串 str1 开头连续有几个字符都不含字符串 str2 中的字符。
    15 char *strerror(int errnum) 从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针。
    16 size_t strlen(const char *str) 计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。
    17 char *strpbrk(const char *str1, const char *str2) 检索字符串 str1 中第一个匹配字符串 str2 中字符的字符,不包含空结束字符,当被检验字符在字符串 str2 中也包含时,则停止检验,并返回该字符位置。
    18 char *strrchr(const char *str, int c) 在参数 str 所指向的字符串中搜索最后一次出现字符 c(一个无符号字符)的位置。
    19 size_t strspn(const char *str1, const char *str2) 检索字符串 str1 中第一个不在字符串 str2 中出现的字符下标。
    20 char *strstr(const char *haystack, const char *needle) 在字符串 haystack 中查找第一次出现字符串 needle(不包含空结束字符)的位置。
    21 char *strtok(char *str, const char *delim) 分解字符串 str 为一组字符串,delim 为分隔符。
    22 size_t strxfrm(char *dest, const char *src, size_t n) 根据程序当前的区域选项中的 LC_COLLATE 来转换字符串 src 的前 n 个字符,并把它们放置在字符串 dest 中。

    1.3 指针做函数返回值

    在声明函数时,将指针作为函数的返回值声明;
    char *search( char *s, char *t );

    此时函数的返回值是对应声明类型的指针需注意返回类型

    同时在函数中若出现不需要返回指针的情况,需要返回一个空指针代替,如return NULL;

    1.4 动态内存分配

    在处理超多的数据时,直接定义数组来接收数据通常会因为栈溢出而报错,栈区的空间是有限的,此时就需要进行动态内存分配,在空间更大的堆区进行内存申请存放;

    • 定义

    栈区(stack): 函数运行时分配,函数结束时释放。由编译器自动分配释放,存放为运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
    堆区(heap): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS(操作系统)回收。分配方式类似于链表。

    • 动态内存相关函数

    动态内存申请需要用到calloc();函数或malloc();函数; calloc()函数在申请时顺便将内存初始化,而malloc()函数则是直接申请,并未初始化;
    在申请内存时,需要将返回的指针进行强制类型转换,才能将地址赋值到对应指针;

    • 示例

    若已知所需要开辟动态内存大小,即可直接使用malloc()函数;如char* str = (char*)malloc(500002);//申请了500002个字符型变量内存并赋予指针str
    若要为多个未知长度的字符串进行动态内存申请,则可通过strlen()函数来记录长度后再进行动态申请;

    char s[1000];
    while(i++ <= n)
    {
        fgets(s,1000,stdio);
        int length = strlen(s);
        char *str = (char *)malloc(length + 1);
        strcpy(str,s);
        ......(处理字符串);
        free(str);
    }
    

    在动态内存申请中,所申请下来的内存是属于堆区的,而这里的内存不像栈区不会自动释放,而是通过程序员手动释放;
    如果不及时释放内存,在内存中会出现许多内存碎片,而对于程序员来说,每一点内存都是宝贵的;
    在所申请的空间结束使用之后,需通过free(*)函数来释放堆区的内存;

    1.5 指针数组及其应用

    如同其他变量,指针变量也可以声明指针数组,声明方式如:
    char *p[10];,每一个数组元素都是一个指针,在内存中也是按序排列;

    二维字符数组表示字符串,申请时在栈区申请,无法处理过多的数据;每一行的长度都相同,可能会产生内存的浪费;
    char str[10][50];

    用指针数组表示字符串,申请时可以通过动态内存申请在堆区申请内存,可以处理大量数据,同时可以确定申请内存大小,不容易产生浪费;
    char *sp[10];;指针数组比较适合用来指向若干个字符串,使字符串处理更加方便、灵活。

    1.6 二级指针

    任何值都有地址 ,一级指针的值虽然是地址,但这个地址做为一个值亦需要空间来存放,是空间就具有地址 ,这就是存放地址这一值的空间所具有的地址,二级指针就是为了获取这个地址。

    char *p = (char*)malloc(100);
    char **sp = &p;  
    free(p);
    

    二维数组的首地址也是二级指针;
    具体用法类比指针数组;

    1.7 行指针、列指针

    • 行指针

      • 形式
        int (*p)[n];
      • 含义
        p为指向含有n个元素的一维数组的指针变量。同时p也为二级指针
        此时指针是行地址性质的指针;对标指针数组;
    • 列指针

      • 形式
        int *p; p = a[0];
      • 含义
        列指针为指向某一列数组的指针;
        *(p + i),表示在列上离a[0][0]第 i 个位置的元素

    2.PTA实验作业

    2.1 删除字符串中的子串

    2.1.1 伪代码

    
    Begin
    get 子串1(S1);
    get 子串2(S2);
    检索子串1
    if 有检索到存在子串2
        删除子串2(从该地址开始,后面的元素前移);
        back 检索子串1;
    else
        输出子串1;
    end if
    End
    

    2.1.2 代码截图

    2.1.3 余智康同学的代码

    代码通过手动str检索字符串,巧妙地通过while()函数与flag变量来进行遍历次数的控制,确保了如果删除合并后组合的字符串中不会又出现子串;

    反观我的代码,通过多次调用函数来进行遍历次数控制,占用内存较大,失败;重复内容较多,失败;不知道该说什么,总之失败中的失败;

    2.2 合并2个有序数组

    2.2.1 伪代码

    函数(子串a, 子串a长度m, 子串b, 子串b长度n ) Begin//正向重组数据,会覆盖原数据导致错误,所以逆向重组;
    int t = m + n - 1;//定位到子串a的尾部
    while(a或b到达子串头)
        比较原子串a与子串b最后一位数值;
        将较大的数据赋给子串a尾部,并将对应下标前移;
        子串a尾部前移;
    end while
    if a先到子串头
        子串b的剩余值赋给子串a;
    end if
    End
    

    2.2.2 代码截图

    2.2.3 余智康同学的代码


    代码大体相同,在遍历到一个子串头时,继续通过判断来进行赋值;
    我是在子串a判断到头的时候跳出,再通过遍历把子串b剩余的数值赋给子串a;

    2.3 说反话-加强版

    2.3.1 伪代码

    Begin
    get 一长串字符str;
    void Reverse(当前地址)//检索子串
    找到单词并定位
    移动到单词末尾
    Reverse(当前地址)//back检索子串
    输出单词//通过递归逆向输出单词
    end Reverse()//结束递归
    End
    

    2.3.2 代码截图

    2.3.3 与超星视频做法区别,各自优缺点。

    超星视频中是通过逆向扫描数组并按序输出单词;遍历数组并直接输出,内存占用率小,方便快捷;

    我的做法是正向扫描数组并通过递归函数逆向输出内存占用率大,在处理大量数据时容易溢出与超时,并不理智;

  • 相关阅读:
    关于 SAP UI5 对服务器端渲染 Server Sider Render 的支持问题
    java基础_数组_数据类型_原码反码补码_进制转换_编码_shell
    java基础_==与equals,try catch finally return ,装箱与拆箱, 字符串
    Java基础_泛型详解
    java基础_吃透Java集合系列九:HashMap
    java基础_吃透Java集合
    java基础_TCP和UDP总结及常见面试题
    java基础_由i++引发的并发问题_volatile关键词详析
    SpringBoot:SpringBoot项目的配置文件放在Jar包外加载和jar包打成exe
    对docker拉取镜像的一个知识点弥补不足
  • 原文地址:https://www.cnblogs.com/YasakaKiyokaze/p/14196722.html
Copyright © 2011-2022 走看看