• C博客作业05--指针


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

    0.展示PTA总分

    1.本章学习总结

    1.1 基础知识

    1.1.1 指针定义

    指针可以直接进行物理操作,是c语言的特色之一。使用指针可以对复杂数据进行处理,能对计算机的内存分配进行控制,在函数调用中使用指针还可以返回多个值。

    在程序中定义了一个变量 x 来存放密码,再定义一个特殊的指针变量 p ,用于存放变量 x 的地址,这样既可以通过变量名 x 直接得到数据,也可以通过指针变量 p 所存放 x 的地址间接得到数据。

    //直接访问:
    int x = 20,y = 1,z = 155;
    printf("%d",x);
    //间接访问:通过地址访问存放地址的变量
    int *p = &x;//把 x 的地址赋给 p
    printf("%d",*p);
    

    指针变量:存放地址的变量

    类型名 * 指针变量名      // * 是指针声明符
    int *ptr;      //p是整型指针,指向整型变量
    int *fptr;      //fp是浮点型指针,指向浮点型变量
    char *cptr;      //cp是字符型指针,指向字符型变量
    //指针变量的类型和它所指向变量的类型相同
    

    1.1.2 指针变量初始化

    //指针变量先定义,赋值必须是数组
    int a;
    int *p;
    p = &a;
    //在定义指针变量时,可以同时对它赋初值
    int *p = &a;
    //不能用数值作为指针变量的初值,但可以将一个指针变量初赋值为一个空指针
    int *p;
    p = 0;
    p = NULL;
    p = (int*)1732;//使用强制类型转换来避免编译错误,不提倡该种做法!!!
    

    1.1.3 指针相关运算

    指针的值:所指变量的地址(&是取地址运算符)
     & 表示取地址, * 表示取内容 
     &*p 与 &a 相同,是地址
     *&a 与 a 相同,是变量
     (*p)++ 等价于 a++  // 将 p 所指向的变量值加一
     *p++ 等价于 *(p++)  // 先取 *p ,然后 p 自加,此时指针 p 不再指向变量 a 
     q - p 指针 p 和 q 之间相隔的存储单元数目
     (int)q - (int)p 指针 p 和 q 之间的字节数 
     p + 1/p - 1 指向下一个/上一个存储单元
     p < q 相同类型指针可以用关系运算符比较大小
    
    • 指针相加,相乘和相除,加减浮点数等其他操作都是违法的
      注意点:
    • 指针变量类型不是指针变量本身类型
    • 指针变量类型要和指向变量类型一致
    • 占用内存空间与类型无关,不同类型指针变量所占内存空间大小一样
    • 指针一定要有指向,否则为野指针,会导致段错误
    • 相同类型的指针才可以相互赋值

    1.1.4 指针做函数参数

    在c语言中实参与形参之间的数据传输是单项的值传递,函数中形参无法影响实参,而return一次只能返回一个值,使用指针变量可以直接改变实参指针变量所指向的变量的值,也就是说明,传地址可以返回多个值。
    优点:传地址,数据量少,直接对地址操作,可以改变多个变量值

    形参:指针变量    int *p
    实参:变量的地址或变量的指针,数组名        &a  某个指针
    
    代码示例:
    swap(&a, &b);//函数调用
    void swap(int* px, int* py)//函数定义
    {
       //使用指针交换a,b的值
    	int t;      
    	t = *px;
    	*px = *py;
    	*py = t;
    }
    

    1.1.5 指针做函数返回值

    • 优点:一次可改变和返回多个变量值
    • 示例:输入年和天数,输出对应的年,月,日(用两个指针作为参数,带回两个结果)
    //函数调用
    month_day(year, yearday, & month, & day)
    //函数定义
    void month_day(int year, int yearday, int* monthptr, int* dayptr)
    {
    	int k, leap;
    	int tap[2][13] =
    	{
    		{0,31,28,31,30,31,30,31,31,30,31,30,31},//非闰年每月天数数据
    		{0,31,29,31,30,31,30,31,31,30,31,30,31},//闰年每月天数数据
    	};
    	//判断是否闰年
    	leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;//闰年为1,非闰年为0
    	for (k = 1;yearday > tap[leap][k];k++)//剩余天数大于这一个月天数时,继续下一月
    	{
    		yearday = yearday - tap[leap][k];
    	}
    	*monthptr = k;
    	*dayptr = yearday;
    }
    

    1.1.6 指针与数组

    数组名代表一个地址,它的值是数组首元素 a[0] 的地址
    a + i 是数组 a 首地址的第 i 个偏移量,指针加一,实际下移一个数据类型存储单元
    &a[i] = a + i;
    *a[i] = *(a + i);
    

    提示:

    • 任何由数组下标来实现的操作都能够用指针来完成
    指针做循环变量,务必了解初始和结束地址
    示例:
    
    int a[100];
    int* p;
    int n;
    ······
    p = a;//或p=&a[0]
    int sum = 0;
    for (p = a;p < a + n;p++)//初始:首地址  结束:首地址+数组长度
    {
    	sum = sum + *p;
    }
    

    注意点:在循环等操作中,注意是否需要数组初始位置,一般要保存数组初始位置,可重新定义一个指针进行循环等操作

    1.2 字符指针

    //定义形式
    char sa[] = "This is a string";
    char *sp = sa;
    
    //const定义的是只读变量,相当于常量,不允许给它重新赋值,且必须在定义的时候给它赋初值
    //使用const约束,提高程序健壮性
    const char* sp = "This is a string";
    
    数组名sa,指针sp和字符串"string"的值都是地址
    
    输出:printf("%s",地址);      //%s输出从指定地址开始,''结束的字符串
    

    1.3 字符串相关函数

    常见函数:

    函数名 函数定义格式 函数功能 返回值
    strcat char* strcat(char* s, char* t) 把字符串t连接到s,使s成为包含s和t的字符串 字符串s
    strcmp int strcmp(char* s, char* t) 逐个比较字符串s和t中的对应字符,直到对应字符不等或比较到串尾 相等:0 不等:不相等字符的差值
    strcpy char* strcpy(char* s, char* t) 报字符串t复制到s中 字符串s
    strlen unsigned int strlen(char* s) 计算字符串s的长度(不包括'') 字符串长度
    strchr char* strchr(char* s, char c) 在字符串s中查找字符c首次出现的地址 找到:相应地址 找不到:NULL
    strstr char* strstr(char* s, char* t) 在字符串s中查找字符串t首次出现的地址 找到:相应地址 找不到:NULL

    注意事项:

    • 加入const提高程序健壮性
    • strcpy 和 strcat:源字符串要足够大,否则会溢出崩溃

    strncpy函数

    • char* strncpy(char* dest,const char* src,size_t n)
    • 把src中的字符串复制到dest,最多复制n个字符,当src的长度小于n时,dest的剩余部分将用空字符补充

    strncat函数

    • char* strncat(char* dest,const char* src,size_t n)
    • 把src所指向的字符串追加到dest所指向字符串的结尾,最多追加n个字符

    strcmp函数:(按照ASCII码序)

    • 如果str1等于str2:返回0 如:sea 与 sea
    • 如果str1大于str2:返回1 如:sea 与 saa
    • 如果str1小于str2:返回-1 如:sea 与 sead
    • 比较字符串内容 if(strcmp(str1,str2)>0) { }

    strlen函数

    • 计算字符串长度时不包括'' (fgets会包括' ') //不要放循环内重复操作

    strpbrk函数

    • 检索字符串str1中第一个匹配字符串str2中字符的字符

    strchr函数

    • 参数str所指向字符串中搜索最后一次出现字符c的位置

    islower函数

    • 如果c有相对应的小写字母,返回c的小写字母,否则c保持不变

    toupper函数

    • 如果c有相对应的大写字母,返回c的大写字母,否则c保持不变

    1.4 动态内存分配

    原因:

    • 堆区申请的空间,想要多少申请多少
    • 数组指定数组长度,会造成空间浪费
    • 栈区空间有限
      相关函数:

    void* calloc(unsigned n,unsigned size)

    • 在内存动态存储区堆中分配n个连续空间,每一存储空间的长度为size,并且分配后储存块里全部初始化为0
    • 申请成功:返回起始地址
    • 申请失败:返回NULL

    void* malloc(unsigned size)

    • 在内存的动态存储区分配一连续空间,长度为size
    • 申请成功:返回起始地址
    • 申请失败:返回NULL
    • 不会对存储块初始化

    free()

    • 释放动态申请到的整块内存空间,ptr为指向要释放空间的首地址
    • 当某个动态分配的存储块不再用时,要立即释放
    //赋给指针要注意强制转化
    p = (int*)calloc(n, sizeof(int));
    p = (int*)malloc(n*sizeof(int));
    free(p);
    

    1.5 指针数组及其应用

    • 使用数组需要先申请较大内存存放,浪费空间
    • 指针数组表示多个字符串,节省空间
    • 一维指针数组定义:
      类型名* 数组名[数组长度]; int*p[n];
    • 指针数组可以相互赋值
    • 可以间接访问操作数组元素所指向的单元内容
    char* color[5]={"red","blue","yellow","green","black"}
    * color是一个数组,有五个元素
    * 每个元素的类型都是字符指针
    * &color[0],&color[1],&color[2]···
    * 一维指针数组输入
    for(i=0;i<n;i++)
    {
          scanf("%s",color[i]);
    }
    * 一维指针数组输出
    for(i=0;i<5;i++)
    {
          printf("%s",color[i]);
    }
    

    1.6 二级指针

    定义:指向指针的指针
    类型名变量名
    int
    pp = &p;

    • p变了,pp也跟着改变
    • 地址加数值还是地址
    • 二级地址,一个后是一级地址,2个后才是内容

    1.7 行指针、列指针

    1.7.1 行指针

    定义:int *(p)[n];
    含义:p为指向含有n个元素的一维数组的指针变量

    • 行指针可以和数组名互换使用
    int a[4][5];
    int*(p)[5];
    p=a;
    //(*p)[0]=a[0][0],(*p)[1]=a[0][1],(*(p+1))[0]=a[1][0],(*(p+1))[1]=a[1][1]
    p+i:表示第i行首地址a[i]
    

    a[i][j]=((p+i)+j)=(*(p+i))[j]=p[i][j]

    1.7.2 列指针

    定义:

    int a[3][3],*p;
    //p=a;列指针,移动指向下一个元素
    p=a[0];
    *(p+i):表示离a[0][0]第i个位置的元素
    

    2.PTA实验作业

    2.1 删除字符串中的子串

    题目详情

    代码思路

    想要删除字符串中的子串,首先要找到子串,所以使用strstr函数能够快速直接寻找字符串中的子串,找到子串的首地址之后,就要开始删除子串,因为子串前后都可能有字符,所以再定义一个数组,使用strcpy函数将子串之后的字符数据复制储存到新数组中,再使用strcat把它拼接连接到字符串在字串之前的数据,从而达到删除目的,再以此循环,直到字符串中不存在子串。
    

    2.1.1 伪代码

    定义字符串str1,子串str2,中途储存数据的新数组temp;
        遍历str1的变量i,遍历str2的变量j;
    使用while和getchar为str1和str2输入数据并赋给结束符;
    while (strstr找到子串首地址p)
          *p赋值为'';
          strcpy复制子串之后的数据(p + j)到新数组temp中;
          strcat把temp数据拼接到str1中;
    end while
    输出(str1);
    

    2.1.2 代码截图

    2.1.3 代码比较

    两种代码对比分析:

    我的代码更多的是采取字符串函数来进行代码的寻找,复制,拼接,把需要删除的字符串的前后重新拼接为一个新字符串,从而达到删除字符的目的,再以此循环,直到字符串中没有出现子串为止。
    而该同学的代码是使用一个数组p记录子串所在的初始位置,然后使用len来计算子串长度,将指针指向内容作为while循环条件,并对p指针所指向的内容进行修改,将子串过后的数据内容重新赋予指针p到p+len,等于把子串过后的数据向前挪,最后赋予结束符,从而达到将子串删除的目的。
    

    学习知识点

    • 学习取内容和取地址的灵活使用,明确什么时候用地址,什么时候用指针所指内容
      (如该做法中重新赋值和判断循环条件都灵活使用指针所指内容)
    • 学习使用指针将数据向前挪)
      (如使用*p=*(p+len)重新把后面的赋值到前面

    2.2 合并两个有序数组

    代码思路

    要想把a,b两个数组内的数据按序排列合并到a中,可以从a数组的最后倒排到最前面,将最大的数放在合并后的最后一个数的位置,再找出第二大的数放在倒数第二个位置中,不断按从大到小的顺序倒排在数组a中,依此类推,当到a[0]时数据已经全部按顺序合并好了。
    

    2.2.1 伪代码

    a数组中有m个数,b数组中有n个数
    定义:a数组下标为j,b数组下标为k,合并后a数组下标为i
    //用三种下标可取的最大下标赋值,便于下方倒排
        i = m + n - 1;
        j = m - 1;
        k = n - 1;
    for i to i=0
          if j 大于 0 和 a[j]大于b[k]      //如果a数组中还有数,比较此时a数组与b数组的数据大小
                a[i]等于a[j]      //重构数组中的数等于大的那个数
                     j--;      //数组向前移动一位,继续比较
           else  a[i]等于b[k]
                     k--;
    end for
    

    2.2.2 代码截图

    2.2.3 代码比较


    两种代码对比分析

    * 我们两个人都是按照依次比较两个数组中的数据大小,并对数据进行排序整合。
    * 我的代码是将两个数组中的数据重新储存到数组a中,从合并后数组a的最大下标开始重新对数组a进行赋值,数组a,b倒排向前比较,将数据从大到小给数组a赋值,达到按顺序合并
      的目的。
    * 而该同学的代码则是通过重新定义一个数组,通过对数组a,b中的数据正序比较,将较小的数放入新定义的数组中,最后将新数组中的数据写入数组a中。
    

    学习知识点

    • 学习动态申请内存的方法,不造成储存空间浪费
    • 同学的下标使用非常灵活巧妙,学习下标用法,灵活使用下标
      (如while循环的条件i + j < m + n,表示数组a和数组b中的数据还没有完全遍历,等等···)
    • 考虑问题要周全
      (如果数组a或b有一个到达最后一个数了,就把另一个数组的数全部储存进去,使用j >= ni >= m来控制条件)

    2.3 说反话-加强版

    2.3.1 伪代码

    void ReverseStr(char* beginPtr)
    {
       定义尾部指针endPtr = beginPtr;判断是否为第一个输出单词flag = 1;
       while (*endPtr)	endPtr++;//指针定位到字符尾部
       遍历指针p = --endPtr;//尾部指针前一个位置
       while(p!=beginPtr)
       {
          if p 不等于空格	len++;
                if 前一个* (p - 1)等于空格
                      if flag等于1	输出(前不带空格);flag = 0;
    		  else   输出(前带空格);
    	    len = 0;
    	    end if
          p--;
       }
          输出第一个单词;
    }
    

    2.3.2 代码截图

    2.3.3 代码比较

    因为我一开始使用字符指针写代码并提交,总有两个测试点不过,我就使用了之前学的方式,没有使用指针。
    使用getchar一个个字符输入,如果输入并存储每个单词和一个空格,之后再根据遍历并输出。
    看过超星视频后学习老师使用指针的写法,使用字符指针,可以使用%.*s来表示从某个位置开始子串并输出的长度,从而控制长度输出。
    而且使用函数分装,使代码更加清晰明了。
    

    知识点

    • 定义指针指字符串,更能动态了解当前字符及位置
    while (* endPtr&&* endPtr != '
    ')      endPtr++;
    
    • 逆向扫描字符串
    while (p != beginPtr)      { p--; }
    
    • 寻找字符串中单词
    if(* p != ' '&&* (p-1) == ' ')//当前字符非空格而前一个字符是空格
    
    • 字符串指针灵活表示某个子串
    printf(" %.*s ", len , p)//方便表示某个地址开始子串,使用len控制输出长度
    
  • 相关阅读:
    iOS-开发日志-UIButton
    苹果API常用英语名词
    iOS-开发日志-UIimageView
    IOS-开发日志-UILabel相关
    iOS-开发日志-UIPageControl
    Maven-生成可执行的Jar包
    RabbitMQ不讲武德,发个消息也这么多花招
    Azure Service Bus(三)在 .NET Core Web 应用程序发送ServiceBus Queue
    windows server 2012 R2里IIS配置.net core2.1遇到的坑
    VScode中配置C++运行环境
  • 原文地址:https://www.cnblogs.com/Huangjing-2920/p/14155938.html
Copyright © 2020-2023  润新知