C博客作业05--2019-指针
由于“✳”在博客中的效果会使字体倾斜,并且“✳”将显示不出来,因此此篇博客将用中文拼出的“✳”来代替*
,请读者明白后阅读。
0.展示PTA总分
1.本章学习总结
1.1 学习内容总结
指针的概念:
变量的指针就是变量的地址。变量的值和变量的地址是不同的概念,变量的值是该变量在内存单元中的数据。
用来存放指针(地址)的变量就称为指针变量。
知识点:
1、若把某变量的地址赋值给指针变量p,则称指针变量p指向该变量。
2、定义指针变量的一般形式为:类型名✳指针变量名;其中“✳” 为说明符,而不是运算符。
3、通常指针变量可以通过以下几种方法获得地址:通过地址运算"&” 赋值;指针变量的初始化,通过其他指针变量赋值用NULL给指针变量赋空值;以及通过调用标准函数赋值。
4、“✳”称为指针运算符(单目运算符),也称取内容运算符。当指针变量p指向-个变量x时,可以用 ✳p的形式存取该变量的值。此时,✳p与变量x相互等价。
5、取地址运算符“&”与指针运算符“✳” 作用在一起时,有相互”抵消”的作用。对于变量x, ✳&x与x相互等价。
6、比如,若定义了一维数组a和指针变量,且p=a; 则以下四种表示相互等价: a[]、 p[i]、 ✳(a+i)、 ✳(p+i)。
7、未对指针变量p赋值即p没有指向时,而就对✳p赋值,该值就代替了内存中某单元的内容,有可能出现不可意料的错误。
8、一个数组的元素在内存中是连续存放的,数组第一个元素的地址称数组的首地址。在C语言中,数组名是该数组的首地址,因此,数组名是指针常量。
9、当指针变量p指向数组元素时,p加上一个正整数n, 则当前指向为相对p向前移动n个元素的位置; p减去一个正整数n,则当前指向为相对p向后移动n个元素的位置。
10、C语言的二维数组由若干个一维数组构成。若有定义语句:int a[M][N],i, j;则以下元素的四种表示相互等价:a[i][j]、✳(a[i]+j)、 ✳(✳(a+i)+j)、(✳(a+i))[j]。
11、字符指针表示字符串:比如char *p="hello";输出的时候这样就可以了:printf("%s",p);这里p指的是"hello"的首字符的地址,用%s可以连接后面的字符一起输出,直到遇到'\0';
12、指针做函数参数:返回指针的函数一般都返回:全局数据对象、主调函数中数据对象的地址;比如定义在main函数上面的全局变量,函数中的static型的变量。
要记住的是:不能在实现函数时返回在函数内部定义的局部数据对象的地址。(因为局部变量数据对象在函数返回时就会消亡,其值不再有效)
13、二级指针、行指针:这里可以联系二维数组,比如a[n][m],则a就是一个二级指针,指的是a[0]、a[1]、a[2]...的地址,而a[0]、a[1]、a[2]是一级指针,指的是a[0][0]、a[1][0]、a[2][0]的地址。
1.2 本章学习体会
-
一开始听指针时,什么都不懂,就把它当作是一个指着某个位置的“针头”,这个位置就是指针所指的地址,就这样形象的把指针给可视化了,慢慢的,做到指针的时候就会想象出一个“针头”,指在某个位置,尤其是字符串的时候,会想象出某个字符下面有一个针头指着,还好我用这种形象化的理解,不然真不知道该怎么运用指针。还有就是课前预习、课后复习那些依旧是不变的真理。
-
本章代码量约600行。
2.PTA实验作业
2.1 题目:7-3 字符串的冒泡排序
2.1.1伪代码:
定义n,k;
定义 字符型指针数组strP[102];
char Temp[12];//冒泡交换字符串时的中间变量
scanf("%d %d", &n, &k);//输入n和k
用getchar();吸收换行符
for
申请动态储存空间给strP[i];
输入一串字符串给strP[i]所指向的地址
当i=n-1时end for
for (i = 0; i < k; i++)//两层for,冒泡排序,外循环到k
for (j = 0; j < n - i-1; j++)
if(strcmp(strP[j], strP[j + 1]) > 0)//如果strP[j]所指向的字符串大于strP[j+1]所指向的字符串
交换strP[j]和strP[j+1]的内容(是一个地址)
循环换行输出strP[i];
2.1.2 代码截图
2.1.3 总结本题的知识点:
1、用字符指针数组输入字符串时,要申请动态储存空间。
2、冒泡排序:两层for循环,内循环比较两者然后交换,外循环把最大的“沉下去”。
3、交换字符串,要利用3个strcpy交换,而不是像数字那个简单的交换;
4、比较字符串,要用strcmp比较,不能用‘>’或者‘<’;
5、用到操作字符串的函数,要定义头文件string.h
2.1.4 PTA提交列表及说明
提交列表说明:
1、段错误:在输入字符串的时候,在for里面没有定义strP[i] = (char✳)malloc(sizeof(char) ✳ 12);于是出现了错误,无法再次正常输入。
2、段错误:在for里面scanf("%s", strP[i]);对了一个&号:scanf("%s", &strP[i]);编译过了,但是输入不正常。
3、格式错误:输出的时候缺少\n,补为:printf("%s\n", strP[i])。
2.2 题目名2:7-4 说反话-加强版
2.2.1伪代码
char str[N];//定义一个字符串str
int flag = 1;//判断是否是第一个输出的单词
int word = 0;//计算空格之后有几个字符;
int i, len, sublen;//len为str的长度,sublen为某个单词的长度
输入字符串;
获得字符串的长度:len = strlen(str);
for循环(从i=len-1开始,到i=-1结束循环)
if(str[i]是空格并且word不为0)
{
*(str + i + word + 1) = 0;//将单词后面的一个字符置为'\0'
判断是否为第一个输出的单词,是则输出%s(str + i + 1)//从空格开始,到单词后面的一个字符'\0';
不是第一个输出的单词,则输出%s(str+i)//从单词后开始,到单词末(无空格);
无论是否为1第一个输出的单词,都要将word置0;
}
else 如果str[i]不为空格,则word++;
end for
//for结束后,可能还有一个单词(输入的首单词)
if(word!=0)
则判断是否为第一个输出的单词,然后根据判断输出%s或者 %s(有空格)
2.2.2 代码截图
2.2.3 总结本题的知识点
1、用strlen可以获得str的长度,要声名string.h头文件。
2、倒序输出单词,要"抓"那个单词前的空格,在for循环内,从末尾往前面搜索空格,然后计算输出
3、✳(str+i)等价于str[i];用%s输出时,要将单词的末尾置为'\0',即✳(str+i+word+1)=0;
4、在PTA上gets可以通过,而gets_s不能过,在VS上,gets_s能过,而gets不能过。
2.2.4 PTA提交列表及说明
提交列表说明:
1、部分正确:空格输出控制判断不完善,第一个单词输出前面不能有空格,要考虑3部分,第一部分是空格后面的单词,第二部分是输入的首单词,还有仅输入一个单词后借空格的情况。这些情况分别考虑后就可以了
2、部分正确:在单词后面结尾置0时指针位置错误:✳(str + i + word ) = 0;应该为:✳(str + i + word +1) = 0;将单词最后一个字符输出
3、部分正确:一开始for循环是这样的:for (i = len; i >= 0; i--),导致有一个测试点过不了:一个词,末尾有空格,于是将它改为:for (i = len-1; i >= 0; i--)
2.3 题目名3:7-5 删除字符串中的子串
2.3.1伪代码
定义变量:
char str[N];
char SubStr[N];
char TempStr[N];
int i, j, k;
char* p = NULL;
输入2串字符串
计算子串的长度len = strlen(SubStr) - 1;(-1是为了扣除'\n')
将子串的最后一个字符\n删除:*(SubStr + len) = 0;
用strstr查找字串在母串中相同字符串的地址,记作p
for循环
*p=0;
strcpy(TempStr, (p + len));//将(p+len)赋予TempStr
strcat(p, TempStr);//将TempStr连接到p后面
输出母串str;
2.3.2 代码截图
2.3.3 总结本题的知识点
1、查找子串在母串中的位置:用strstr()函数查找,返回的值是一个地址;然后对地址进行操作
2、在母串处连接另一个字符串:用strcat()函数实现连接,注意连接时要时母串在连接处的数据为'\0';
3、判断是否要继续查找删除的条件是(p = strstr(str, SubStr)) != NULL;找得到就继续循环,找不到就退出循环;
4、fgets会吸收换行符,用strstr时换行符会进入判断字符是否相等的条件,所以用strstr之前,要把子串的最后一个字符置为'\0';
2.3.4 PTA提交列表及说明:
提交列表说明:
1、部分正确:没有将子串的最后一个字符置为'\0',导致查找出现错误;
2、部分正确:一开始拼接母串与除了字符的母串部分用的是:strcat(p, p+len);拼接错误,于是引入TempStr,先储存p+len后的内容,在拼接p与TempStr;
3、部分正确:字串的长度计算错误:一开始len = strlen(SubStr);这是包含了\n的长度,后面删除\n后这个长度就错误了,于是改为:len = strlen(SubStr)-1;
3.阅读代码
虽然官方题解并没有用到C语言的代码,但是其思路是非常巧妙的,我们一起来欣赏一下:
这里官方题解用到了3个指针来进行排序:
第一个指针指着0的最右边界;
中间,第二个指针指着当前考虑的元素;
第三个指针指着2的最左边界;
思路大致是:在循环中(移动第二个指针到达第三个指针的过程):如果第二个指针指着的数值为0,则与第一个指针所指的数据交换,第一个指针++;如果第二个指针指着的数值为2,则与第三指针所指的数据交换,第三个指针--;
最后,0将被排在数组的最左面,2被排在数组的最右边,而1将被剩下排在中间;
在这里3个指针同时移动、操作,顺利的仅使用常数空间的一趟扫描算法把一个数组给排序了。
然后还找到了一个有趣的C语言代码:
他这里用到了一种 先计数 然后赋值的方法,思路大致是:
1、定义一个储存数据个数的数组count[3] = {0,0,0},下标代表数据:0、1、2,数组元素则是0、1、2的个数;这里初始化为0;
2、for(历遍数组),用数组count统计数据0、1、2的个数:count[nums[i]]++;
3、for(数据0、1、2的赋值),从nums[0]开始赋值count[0]的个数为0;紧接着赋值count[1]的个数为1;然后赋值count[2]的个数为2;
这种解法并没有给数组排列,而是通过计数重新赋值;虽然有点违背题目的意思,但却顺利的完成了题目的要求;思路非常的巧妙,这简短得令人吃惊的代码量也是这段代码突出的特点之一,思路方法可以学习下来。