C博客作业05--2019-指针

0.展示PTA总分


1.本章学习总结


1.1 学习内容总结

1.1.1 指针做循环变量做法

1.1.2 字符指针如何表示字符串

  • 代码示例
void encrypt(char *str)
{
   char *p=str;
   for(;*p;p++)
   *p=*p=='z'?'a':*p+1
}

char sa[]="This is a string";
char *sp=sa;

  • 字符数组与字符指针的区别:
    • 如果要改变数组sa所代表的字符串,只能改变数组元素的内容
    • 如果要改变指针sp所代表的字符串,通常直接改变指针的值,让它指向新的字符串

1.1.3 动态内存分配

  • 为什么要动态内存分配

    • 堆区申请的空间
    • 数组长度很大
    • 前面内容都要先定义变量、数组,指针才能使用
    • 动态内存分配:自己申请,不要其他变量、数组
  • malloc

    • 向malloc申请的空间的大小是以字节为单位的
    • 返回的结果是*void,需要类型转换为自己需要的类型
    • (int*)malloc(n*sizeof(int))
#include<stdlib.h>
void *malloc(size_t size);
  • calloc
    • 在内存的动态存储区堆中分配n个连续空间,每一存储空间的长度为size,并且分配后还把存储块里全部初始化为0
    • 若申请成功,则返回一个指向被分配内存空间的起始地址的指针
    • 若申请内存空间不成功,则返回NULL
#include<stdio.h>
void* calloc(unsigned n,unsiged size)
  • free()
    • 把申请得来的空间还给“系统”
    • 申请过的空间,最终都应该要还
    • 只能还申请来的空间的首地址

1.1.4 指针数组及其应用

  • 1.p1-p2是什么?

  • p1指向数组a的首元素a[0],p2指向数组a的a[1],p1-p2代表的是a[0]与a[1]之间相隔几个元素。

  • 输出结果:

  • 计算机自动处理为p2-p1=地址之差/数组元素的长度 而不是简单的地址之差=4

1.1.5 二级指针、行指针、列指针

  • 指针数组:

    • 形式:int *p[n],int **p;p=pp;
    • p[i]是指针
    • p是二级指针
  • 行指针:

    • 形式:int(*p)[n]
    • 含义:p为指向含有n个元素的一维数组的指针变量,是二级指针
    • 行指针p是行地址性质的指针,此时p+i=a+i≠a[i]
    • a[i][j]=((p+i)+j)=(*(p+i))[j]=p[i][j]
  • 列指针:

    • 形式:int *p;p=a[0];
    • 含义:*(p+i),表示离a[0][0]第i个位置的元素

1.1.6 函数返回值为指针

  • 代码示例:
char *March(char *str,char ch)
{
   char *p;
   char *locPtr;
   locPtr=NULL;
   p=str;
   while(*p&&*p!='\n')
  {
      if(*p==ch)
     {
         locPtr=p;
     }
     p++;
  }
return locPtr;
  • 注意事项:
    • 不能返回在函数内部定义的在栈区局部数据对象的地址,这是因为所有的栈区局部数据对象在函数返回时就会消亡,其值不再有效
    • 回指针的函数一般都返回全局数据对象或或指向字符串常量指针或堆区的指针或主调函数中数据对象的地址

1.1.7 指针变量作为函数参数

  • 输入三个整数按从大到小的顺序输出
    • 错误解法:
#include<stdio.h>
void swap(int *p1,int *p2,int *p3)
{
    int *p;
    if(*p1<*p2)
    {
        p=p1;
        p1=p2;
        p2=p;
    }
    if(*p1<*p3)
    {
        p=p1;
        p1=p3;
        p3=p;
    }
    if(*p2<*p3)
    {
        p=p2;
        p2=p3;
        p3=p;
    }
}
int main()
{
    int a,b,c;
    scanf("%d %d %d",&a,&b,&c);
    printf("%d %d %d\n",a,b,c);
    
    int *point_1=&a,*point_2=&b,*point_3=&c;
    
    swap(point_1,point_2,point_3);
    printf("%d %d %d\n",*point_1,*point_2,*point_3);
    return 0;
}

输入样例:7 8 9
输出结果:

正确做法

/*输入三个整数按从大到小的顺序输出*/
#include<stdio.h>
void swap(int *p1,int *p2,int *p3)
{
    int p;
    if(*p1<*p2)
    {
        p=*p1;
        *p1=*p2;
        *p2=p;
    }
    if(*p1<*p3)
    {
        p=*p1;
        *p1=*p3;
        *p3=p;
    }
    if(*p2<*p3)
    {
        p=*p2;
        *p2=*p3;
        *p3=p;
    }
}
int main()
{
    int a,b,c;
    scanf("%d %d %d",&a,&b,&c);
    printf("%d %d %d\n",a,b,c);
    
    int *point_1=&a,*point_2=&b,*point_3=&c;
    
    swap(point_1,point_2,point_3);
    printf("%d %d %d\n",a,b,c);
    return 0;
}

输入样例:7 8 9
输出结果:

  • 函数值是单向传递。swap函数中的指针变量p1,p2,p3交换地址,这样的变化并不会传递回原函数使原函数的指针变量的值发生改变。原函数中指针变量point_1/2/3所存储的内存地址并没有改变。
  • 错误样例的错误在于:
    原函数传递三个变量的地址到swap函数,企图通过swap交换形参p1,p2,p3这些指针变量所存储的地址,来交换实参point_1/2/3地址。然而忽略了函数调用时形参和实参是采用单向传递的值传递方式。在函数中改变了名称为p1,p2,p3的内存单元所存储的内存地址,但是主函数中名称为point_1/2/3的内存单元所存储的内存地址并没有改变。
  • 使用指针来处理的优点:能够改变多个值。而普通的函数只能有一个返回值。

1.1.8 字符串处理函数

函数名|函数功能
-😐:-😐-:
fgets|从文件中最多读n-1个字符(遇到'\0'终止)到字符串s中
strcat|把字符串t连接到s
strcmp|逐个比较字符串s和t中的对应字符,直到比较到对应字符不等或比较到串尾为止
strcpy|把字符串t复制到s中
strlen|计算字符串s的长度(不包括'\0')
strstr|在字符串s中查找字符串t首次出现的地址

1.2 本章学习体会

  • 学习感受

指针真的好难555,最初开始预习的时候就发现,面对指针问题,我果然是懵懵的,取地址or取内容,一级指针or二级指针,指针指向哪里,要返回什么值,都说指针是C语言的灵魂 但是它同时也要了我这条狗命 and这周突然变得很忙碌,首先是线性代数的期末考,然后是高数的小测,俺做不到面面俱到,导致PTA的刷题进度有点落后,还是考完试以后和舍友一起熬夜刷的题,虽然作为一个优秀的IT人熬夜必不可少,but头发掉的多了我也开始有点慌张

  • 代码量计算
PTA 题目集 代码量
10 指针 385

2.PTA实验作业


2.1 合并两个有序数组

要求实现一个函数merge,将元素个数为m的升序数组a和长度为n的升序数组b合并到数组a,合并后的数组仍然按升序排列。假设数组a的长度足够大。
其中a和b是按升序排列的数组,m为数组a中元素的个数,n为数组b的长度;合并后的升序数组仍然存放在a中。
输入样例:
7 11
1 2 14 25 33 73 84
5 6 17 27 68 68 74 79 80 85 87
输出样例:
1 2 5 6 14 17 25 27 33 68 68 73 74 79 80 84 85 87

2.1.1 伪代码

void merge(int* a, int m, int* b, int n) /* 函数接口定义 */
{
	定义循环变量 i,j,k;
	用动态内存分配开辟一个新数组c;
        i表示数组a下标,j表示数组b下标,k表示数组c下标;
	i = j = k = 0;
	for (;i < m && j < n;)
             if(a[i]<b[j])
		a[i]放入c数组,i++,k++;
             else
                b[j]放入c数组,j++,k++;
	end for
	while(i<m)
             a剩下数据元素放入c数组;
        while(j<n)
             b剩下数据元素放入c数组;
	for (i = 0;i < m + n;i++)
		将c数组的内容赋给a数组;
	free(c);
}

2.1.2 代码截图

2.1.3 总结本题的知识点

  • 运用动态内存分配的方法给开辟一个指针数组
    • (int*)malloc((m + n) * sizeof(int));
  • 构造一个新数组 运用循环结构,将两个数组排序写入另外一个新数组
  • 将一个数组中的值传递给另外一个数组

2.1.4 PTA提交列表及说明

  • 这题是在机房听了老师的讲解之后才做出来的www 一开始拿到题是没有头绪的,尽管知道要开辟新数组but不知道开辟完之后要干什么,就是看到题目就一顿乱敲代码,听了老师的讲解就是茅塞顿开的感觉!

2.2 查找指定字符

本题要求编写程序,从给定字符串中查找某指定的字符。
输入样例1:
m
programming
输出样例1:
index = 7
输入样例2:
a
1234
输出样例2:
Not Found

2.2.1 伪代码

#include<stdio.h>
#define N 100
char* Match(char* str,char ch);//函数声明
int main()
{
	定义输入想要查找的字符变量ch;
	定义指针变量p,locPtr;
	定义输入的字符串str[N];
	读入想要查找的字符ch;
	getchar();//吸收空格
	读入字符串;
	locPtr = Match(str, ch);
	if (找不到)
		printf("Not Found");
	end if
	else
		printf("index = %d", locPtr - str);
	end  else
	return 0;
}
char* Match(char* str, char ch)
{
	定义指针变量p,locPtr;
        p用于记录数组的位置,locPtr用于记录找到想要查找字符的位置;
	locPtr = NULL;
	p = str;
	while (*p && *p != '\n')
		if (找到了)
			将p的值赋给locPtr;
		end if
		p++;
	end while
	返回 locPtr;
}

2.2.2 代码截图

2.2.3 总结本题的知识点

  • 指针做函数返回值
  • 因为题目要求的是最大下标,那么不能一找到就返回,通过定义另外一个指针变量储存起来
  • scanf后用getchar吸收换行符
  • 要寻找下标对应的数字可以用找到的下标减去数组名,即可得数字

2.2.4 PTA提交列表及说明

1.部分正确:忘记在scanf后面加入getchar来吸收换行符 然后printf里面的格式输入错误

2.3 查找子串

2.3.1 伪代码

char* search(char* s, char* t)//函数定义
{
	定义空指针p;
	定义循环变量i,j;
        定义变量k来储存找出的子串的第一个位置;
	定义主串长度l1,子串长度l2;
	计算长度l1,l2;
	for (i = 0; i < l1; i++)
		if (字符主串的某个位置与子串的首位一样)
			将该位置的值赋给k;
			for(j=0;j<l2;j++)
                        继续找,不对应就break;
			if (找完之后j等于子串长度) 
				p = &s[k];
				return p; 
			end if
		end if
	end for
	return NULL;
}

2.3.2 代码截图

2.3.3 总结本题的知识点

  • 计算字符串长度
  • 返回子串在主串中的首地址 用语句p = &s[k];return p;
  • 用一个变量暂存子串出现的首位置

2.3.4 PTA提交列表及说明

1.部分正确:没有在找到第一个子串首字母时就赋值
2.部分正确:长度超过MAXS的条件没有控制好
3.部分正确:做法会导致只差一个字符但是仍然找得到
4.完全正确:放弃flag的做法,用找到的第一个子串字母对应的主串位置来赋值,再用循环对后面的字符进行判断,如果对上,就继续,对不上提前退出,退出后判断是否符合子串长度,符合则返回位置,不符合返回NULL

3.阅读代码


  • 题目:

  • 题解:

  • 所选代码优点及可以学习地方
    1.运用深度优先算法dfs
    深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.
    其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次
void dfs(int dep)	dep表示深度 

{
    判断边界,如果到了边界返回
    尝试每一种可能结果for(i=0;i<n;i++)
    {
        处理当前步
        继续下一步dfs(dep + 1)
    }
    return ;
}

2.在数组下标处运用指针并运用动态内存分配

3.运用sscanf函数:
从一个字符串中读进与指定格式相符的数据
函数原型:

int sscanf( string str, string fmt, mixed var1, mixed var2 ... );
int scanf( const char *format [,argument]... );

sscanf与scanf类似,都是用于输入的,只是后者以屏幕(stdin)为输入源,前者以固定字符串为输入源。 简单地说就是输入的类型不同。

sscanf函数的返回值:

成功则返回参数数目,失败则返回-1,错误原因存于errno中。返回0表示失败,否则,表示正确格式化数据的个数

4.运用判断语句来控制条件 每三个点进行处理并判断特殊数字 进行剪枝转换等操作 以及熟练运用字符串处理函数

posted @ 2019-12-01 13:29  陈璧君  阅读(376)  评论(0编辑  收藏  举报