C语言博客作业05——指针
0.展示PTA总分
1.本章学习总结
1.1学习内容总结
a.地址和指针
- 在C语言中,指针被认为是计算及内存地址的代名词之一,指针变量本身就是变量,本身有一个地址,不同的是指针的内容是地址,即存放的是地址。指针中有两个重要的符号,即*和&,其中 *号第一次使用的时候,是用来定义一个指针变量,第一次使用后,再次使用,则表示这个指针的内容,也就是某个地址,而&号则是表示取地址的意思,在scanf()函数里,则是我们对&号的第一次接触,就是取地址的意思。
b.指针的定义
-
int *p;//用*表示定义一个指针变量,p就是一个指针。 int i; char *cp; p=NULL; cp=&i;
-
Tip:指针变量被定义后,必须将指针变量和一个特定的变量进行关联后才可以使用它,不能在没有指向具体的某个变量情况下,对指针进行操作。
c.指针的基本运算
-
*p++
-
先将*p作为表达式的值,再将指针p的值加一,运算后,p不在指向变量a,即*p=*(p+1)。 表达式 *(p++)和 *p++等价。
-
++*p
-
- ++*p的意思是将p所指的变量的值加一,和 *p= *p+1,( *p)++表示的意思是一样的。
-
指针的赋值运算
-
指针之间的赋值运算必须是同类型的
-
利用指针计算数组元素的个数和数组元素的存储个数
-
double *p,*q; double a[2]; p=&a[0]; q=p+1; printf("%d",q-p);//计算p,q之间的元素个数 printf("%d",(int)q-(int)p);//计算p,q之间的字节数
d. 利用指针作为函数参数返回多个函数值
题目:给定年份和天数,判断是一年中的哪一月和哪一天。
代码如下:
- Tip:在这个代码里,我觉得有两点是我们要学习的地方,在函数里的leap变量和利用数组存放非闰年和闰年的天数,以及计算哪月哪天时的思路时利用减法,以及比较剩下的天数。
e.指针做循环变量
-
首先,对数组进行循环,代码如下:
-
int sum=0; int a[100]; for(i=0;i<100;i++) { sum+=a[i]; }
-
使用指针,有以下几种方法,代码如下:
-
example1:
-
for(i=0;i<100;i++) { sum+=*(a+i) }
-
example2:
-
int *p=a; int sum; for(i=0;i<100;i++) { sum+=p[i]; }
-
example3:
-
int sum=0; for(p=a;p<=&a[99];p++) { sum+=*p; }
f.字符串的读入
-
example1:利用fgets()函数
-
格式:fgets(数组名str,数组长度n,stdin); 注意事项: *有效字符长度:n-1; 如果输入的字符长度小于n-1,则会把\n读入,并且最后自动加上'\0' *可以读入空格 *遇到换行符,文件尾,或以读了size-1个字符为止
-
example2:利用gets()函数
-
格式:gets(数组名str); 注意事项: *gets()函数输入的时候要避免数组越界的情况。 *gets()接受字符时,会把最后的'\n'转换为'\0',把换行符丢弃; *gets()可以接收空格。 *gets()遇到换行符时停止
-
example3:利用%s输入字符串
-
格式:scanf("%s",str); 注意事项: *%s遇到空格,回车,tab键时停止读取; *%s不会读取末尾的'\n',并且会自动补0
e.字符数组和字符指针
-
char sa[]="This is a example"; char *sp="This is a example";
-
二者都可表示字符串,但是字符数组的每个数组元素放字符串的一个字符,而字符指针sp值占用一个存放地址的内存单元,即首字符的地址,而不是讲字符串的内容存放到字符指针变量中。
f.关于字符串的函数
- 复制函数strcpy();
- 相关代码:
-
Tip:strcpyss(s1,s2),数组s1要有足够打的空间,并且把s2复制到s1,s1中的内容会被覆盖,s1必须时字符型数组的基地址,s2可以是数组名或者是字符串常量。
-
连接函数strcat(s1,s2);
-
代码如下:
-
比较函数strcmp()
-
格式:strcmp(s1,s2); 比较结果: 1.若s1和s2相等,返回0; 2.若s1小于s2,返回一个正数; 3.若s1小于s2,则返回一个负数 比较规则: 从字符串的首字符开始,一次比较相对应得字符,知道出现不同得字符或遇到'\0'为止,'\0'也会参与比较。
-
字符串长度函数strlen()
-
格式:strlen(s1) 注意事项: strlen()函数计算的是字符串有效字符的个数,其中不包括'\0'
g.用指针实现内存动态分配
-
法一:
-
格式: int *p=(int*)malloc(n*sizeof(int));
-
法二:
-
格式: p=calloc(n,sizeof(int);
-
对于以上两种方法,不同的区别在于calloc会给储存块里的内容全部初始化为0,相同的都是,如果申请成功,返回的是地址,申请不成功,那么返回NULL,并且,对于动态分配内存,我们应该要记住的是,在申请完空间后,一定记得释放空间,利用free()函数释放,必进有借有还。
h.指针数组的应用
-
定义: 指针数组中的各个元素都是指针类型,用于存放内存地址。 格式: 类型名 *数组名[数组长度]
-
用指针数组将字符串从小到大排序后输出
-
可以看到是通过指针数组,是在地址上进行交换,改变元素数组所指向的方向,更快捷高效。
-
利用指针数组输出藏头诗
-
代码如下:
- Tip:在这里面,*(poem[i])可以改写成poem[i] [0], *(pome[i]+1)改写成poem[i] [1].
- 用指针数组输出藏尾诗
- 法一:代码如下
- 法二:代码如下:
- 这两个方法,都动态内存分配申请空间,但是就是最后输出的方式不一样,我觉得第二个是对指针数组有更好的理解,同时对%s的意义也了解的更透彻。
i.二级指针,行指针.
-
二级指针的定义: 指向指针的指针,即指向地址,本身又存放一个地址的变量 格式: 类型名 **变量名
-
二级指针,一级指针,普通变量之间的关系
-
int a=10; int *p=&a; int **pp=&p;
-
二级指针 一级指针 内容 pp p a &p &a 10 &&a *pp **p 或 * p -
上面是对不同事物的不同表示方法,我们定义了一个二级指针pp,里面存放的是地址,指向了一个叫p的东西,刚好这个p也是一个存放地址变量,即指针,所以叫做指向指针的指针,然后这个p又指向一个普通变量,即a,p里面存放的是a的地址,pp里面存放的是p的地址.所以对其使用相应的*号,即表示其相应的内容.
-
行指针,列指针
-
例如 char a[3][4]; 在二维数组中,虽然a和*a表示的值相同的,但是含义不同,a是行元素数组的首地址,即行指针,是二级指针,而*a是首行第一个元素的地址,是列地址,是以及指针.a[i][j]等价于*(*(a+i)+j),也可以写成 *(a[i]+j).
j.函数返回值为指针
-
函数指针的定义: 定义一个函数,函数返回的是地址 格式: char*str(char *S);
-
应用:字符定位:代码如下:
1.2本章学习体会
本章的内容是指针,指针是c语言的灵魂,但是归根是对地址的操作,如果可以弄懂地址如何操作,那么学指针也没什么大问题,我在学指针进阶的时候,老师叫我们预习了,但是我看错地方,于是那两节课讲的二级指针,等等,那时是真的很绝望,还要搞清楚以及指针,以及它们不同的表示方法,对我来说,其实是很容易混淆的,记性不太好,所以就总是要想一下,看自己这样写的代码是否是我要它表示的哪个意思,总之,一句话,熟能生巧,多练习,即容易懂了。
代码量 |
---|
800左右 |
2.PTA实验作业
2.1题目名:查找子串
2.1.1伪代码
-
定义函数指针strstr(char*s,char*t)来查找子串 定义字符型数组s[],t[];指针pos指向NULL 主函数 用fgets(s,80,stdin)读入字符存到数组里 t[]数组也一样 pos=strstr(s,t);//利用指针pos来记录母串中子串的首地址 if(p!=NULL) { 说明母串中存在子串 printf("%s",pos-s);//输出第几个位置 } else { printf("-1");//如果没有,则说明不存在子串 } strstr()函数 定义指针变量s1,s2指向子串和母串的首地址 指针变量cp指向母串的地址 while(*cp不是‘\0')//第一个while循环,以cp作为循环变量,遍历整个母串 { s1=cp; s2=str2; while(*s2不是’\0'和!=(*s1-*s2)两个字符是一样的) { 则接下去扫描 s1++,s2++; } if(*s2的内容变成了'\0',则说明s2已经被全部扫描,则说明找到子串) 返回cp的位置 若没找到,则cp移到下一个位置,继续扫描; } 否则就返回空
2.1.2代码截图
2.1.3总结本题知识点
1.我对于这道题,最想学习的就是strstr()函数里代码的操作,因为其实仔细看一下,strcpy(),strcmp()与strstr()函数都有异曲同工之妙,它们都有相似的地方,并且对于NULL的利用,也是很巧妙的。
2.对于strstr()的内容而言,巧妙地利用了指针位置移动的关系,定义三个指针变量,cp可以看成是循环变量,也可以看作是用来存储子串第一个字符在母串的位置,s1和s2则是第二层循环里用来判断是否有子串,以及子串是否完整,不过我在想,如果题目中有多个子串,那么我只能返回第一个子串的位置,后面的子串就不会再去判断了,如果要判断后面子串,应该怎么改
2.1.4 PTA提交列表说明
Q1:部分正确:做这道题的时候,我还不知道strstr()函数,于是自己用了好几个循环,当时是sample1正确,结果sample2不行,后来改了,发现sample2正确,sample1不正确,其实是对一些循环变量打错,加上循环用的太多,并没有利用到指针的做法,当时调试也不好调试
Q2部分正确:其它错误改完了,发现还有个测试点一直都过不了,就是测试点4:只差一个字符找不到,说实话,我到现在都没理解这个测试点的意思,所以我就造不出错误测试点
Q3:答案正确:因为我在上课的时候,知道了strstr()函数,所以在做这道题的时候,直接调用了这个函数,当其实本质上,strstr()只不过是帮我写好了,我自己也得再去领会它的意思
2.2 合并两个有序数组
2.2.1伪代码
主函数:
定义m,n确定数组大小,循环遍历i;定义指针数组*a,*b;
定义函数:merge(int*a,int m,int*b,int n);合并数组
输入m,n;
动态申请空间
a=(int*)malloc((m+n)*sizeof(int));
b=(int*)malloc(n*sieof(int));
通过for循环,对数组a,b赋值
调用函数merge()合并数组
然后打印数组,释放空间
merge()函数内容:
merge(int*a,int m,int*b,int n)
{
定义c[]数组,用来存放合并后的新数组
定义i为数组a的下标,j表示b数组的下标,k表示c数组的下标
for(i=j=k=0;i<m&&j<n;)
{
if(a[i]<b[j])
{
a[i]放入c数组,i++,k++。
}
else
{
b[j]放入c数组,j++,k++;
}
}
while(i<m)a剩下的数组元素放入c
while(j<n)b剩下的数组元素放入b
把c数组赋值到a数组中
释放c数组
}
2.2.2代码截图
这道题也有一个更精炼的办法,不过思路大致是一样的
2.2.3总结本题知识点
1.做这道题的时候,其实每个人一开始的思路都是先把数组合并,然后再通过选择排序法或冒泡排序法,对数组进行有序的排序,但是提交后会发现,其实有一个测试点过不去,就是运行超时,看到这个,其实就可以明白题目要我们的就是要把两个数组的数一个一个比,因为用选择排序法和冒泡排序法,两层循环,如果在数量很大的情况下,是很消耗时间的
2.接下的问题,就是如何一个一个插,其实在老师没有讲之前,我是没有思路了,就是要开辟 一个新的数组,然后通过比较大小,一个一个放进去。在把新数组里的数一个一个赋值给a
2.2.4 PTA提交列表说明
Q1:编译错误:题目里面有两个函数,所以我九八另外一个打印函数也写上去了,提交发现是编译错误
Q2:部分正确:我一开始用的是选择排序法,所以发现运行超时,就换成了冒泡排序法,想着时间会不会更短点,结果发现还是过不了
Q3:答案正确:用了老师的方法,就答案正确
2.3删除字符串中的子串
2.3.1 伪代码
定义数组a,b,c
定义循环i,j,求数组b的长度len,指针变量p指向子串的首位置
利用gets()读入数组a,b
len=strlen(b);
while(a中有b时)
{
把子串在母串的首地址赋为0
strcpy(c,p+strlen(b));//把子串后面的字符全部付给c
strcat(a,c);把c连接到a后面,则是一个新的数组
再循环判断a中是否有子串c
}
return 0;
实例:
a:Tom0at is a male ccatat
c:is a male ccatat
b:cat
2.3.2代码截图
2.3.3本题知识点总结
1.这个是老师上课把同学的优秀代码展示出来给大家看的,我觉得其中很好的地方,巧妙的利用与字符串有关的函数,并且对这道题有深刻的认识,结合这道题的要求,加上不同函数之间的左右,只要一点点的代码量就可以完成任务
2.3.4 PTA提交列表及说明
Q1:部分正确:这个题目,我一开始的代码值完成了一个任务,就是能找到一个子串,并把那个子串删掉,但是我的代码不能完成的功能是继续判断剩余的字符串里是否还有子串,所以放进去的话,好像只对了两个测试点,后来找到了strstr()函数。才知道可以把这个当作条件来判断是否还有子串。
Q2:编译错误:在PTA里面,是不能够用gets()函数的,但是可以用的是
gets_s函数,所以我复制以后,忘记把下划线弄掉了,就出现了编译错误的情况,至于那么多的编译错误,是因为我会多点几下。
阅读代码
题目:
代码如下:
题目理解:
这道题是给出一个方阵,里面有一个数丢失,然后我们用代码把这个丢失的数据移到右下角,并且在移动的过程中,将其它数按小到大排序。
函数功能:
1.void init()//输入函数,x对应位置为9。
2.int h(Node& a)//计算每个数字回到相应位置的距离之和。
3.Node getchild(int a, Node currents)//a为移动方向,该函数计算x移动后的图形。
4.bool ida()//用来保存图形状态。
5.void print()//输出路径。
感悟:
我觉得这个代码的特点在于里面用到我们最近在学的结构体部分的内容,并且在对结构体的应用时,又夹杂了对二维数组知识点的结合,比如在h()函,getchild()函数中,并且,这里面还结合了栈的知识,运用到关于栈的库函数,也可以让我大致了解一下栈的具体内容。而且,对于这道题,我觉得更多的体会是一种动态的思维,移动一次后,要保留移动后的位置变化,并且,在这里面用的dir[4][2]也是非常巧妙的,因为它记录了上下左右四个方向,用了一个巧妙的二维数组。还有的就是h()这个函数,计算每个数字回到相应位置的距离之和,当返回值是0时,就说明每个数字都回归到了自己所在的位置,