Loading

C博客作业05-指针

0.展示PTA总分(0----2)

1.本章学习总结

1.指针变量的定义

  • 一般形式: 类型名 *指针变量名
  • 类型名是指针变量所指向的变量的类型,例如:int *p;/*定义一个指针变量p,指向整型变量*/
  • 定义指针变量要使用指针声明符*,表示被定义的变量是指针,这里的*不是运算符,故想要对指针做处理时,前面不需要带*
  • 定义多个指针变量时,每个指针变量前面都要加上*

2.指针的基本运算

  • 2.1 取地址运算
    • 运算符&用于给出变量的地址,例如int *p,a=3; p=&a;这里将整形变量a的地址赋给整形指针p,是指针p指向变量a,p的值就是a的地址;
    • scanf函数中的&也是取地址运算符,例:scanf("%d",&a);中的操作就是取变量a的地址,把输入的值保存到变量a中去;
  • 2.2 间接访问运算
    • *除了作指针声明符,还可以作为间接访问运算符,用于访问指针所指向变量的值。例如int *p,a=3; p=&a; printf("%d",*p);的输出结果为3,当p指向a的时候,p和a访问的是同一个单元,*p的值就是a的值;
  • 2.3 指针间的加减运算
    • *的优先级和自增自减的优先级相同,但是结合方向是从右到左,故表达式p++等价于(p++),先求表达式p++,然后在取*(p++)的值。
    • 相同类型的指针之间不可以做加法运算!
    • 指针p加上(或者减)一个整数i都是可以的,表示访问该类型后(或前)的第i个变量;
    • 相同类型的指针之间做减法运算时,会产生一个int型的值,该值的意义表示两个指针指向的内存位置之间相隔多少个元素注意是元素,并不是字节数,该运算在数组指针中经常使用,可以用来找某个值所在的位置。
  • 2.4 赋值
    • 指针变量要先赋值再使用,这点特别要注意!。例如int *a; scanf("%d",a);还未对指针a进行赋初值值就使用它,程序会出现错误,为了避免引用为赋值的指针所造成的危害,在定义指针时,可先将它的初值置为空,不能用数值作为指针变量的初值。
    • 对指针p进行赋值p=0;p=NULL都表示指针p为空指针,空指针不指向任何一个单元;这里的0是ASCII字符NULL的值;

3.指针和数组

3.1 数组和指针之间的关系

int *p;
int a[10];
p=a;
  • 在数组那一章节我们学到数组名表示该函数所分配连续内存空间中第一个单元的地址,即首地址。这里指针p指向的是a数组的第一个元素;
  • 指针p可以做自增自减运算,而数组a不可以,这里可以把a看作指针常量,常量是不可以做自增自减运算的;

3.2 指针做循环变量做法
如果指针指向一个数组,那么可以用指针做循环变量对数组进行遍历,例如:

/*在字符数组a中查找字符ch*/
定义字符指针p;  
定义字符ch;
定义字符数组a[10];
定义变量pos保存查找到的ch的位置,初始化为-1

输入字符ch;
p=a;//使指针指向数组第一个元素;
for p to p指向a数组a的最后一个元素
  if(*p==ch)
     pos=p-a;
  end if
end for
if pos<0
   说明没有找到,输出没有找到语句;
else
   说明找到,输出位置;

运行结果:

4.用字符指针表示字符串

  • 字符串常量实质上是一个指向该字符串首字符的指针常量,如果定义一个字符指针接收字符串常量的值,该指针就指向字符串的首字符;
  • 调用printf函数,以%s的格式输出字符串的时候,从该地址所指定的单元开始连续输出其中的内容,直至遇到'\0'为止,例如:
  • 指针加上或减去一个整数i,表示指向当前指针地址指向的后第i个字符或者前第i个字符,例如:

5.常用的字符串处理函数

头文件:#include <string.h>

6.动态内存分配

  • 6.1 从栈区(stack)上分配
    • 存放函数参数值,局部变量
    • 在执行函数调用时,系统在栈上为函数内的局部变量及形参分配内存,函数执行结束时自动释放这些内存。
  • 6.2 从堆区(heap)上分配
    • 在程序运行期间,用动态内存分配函数来申请的内存都是从堆上分配的,动态内存的生存期由程序员决定。
    • 动态内存分配函数

7.指针数组及其应用

  • 7.1概念
    • 一般格式:类型名 *数组名 [数组长度]
    • 指针数组中的每个元素都是指针类型,用于存放内存地址。
  • 7.2指针数组保存字符串
    字符串的冒泡排序
定义二维字符数组str[N][11]来保存多个字符串;
定义静态指针数组p[N];
定义变量k保存扫描次数;
定义指针变量ptemp暂存地址;
定义变量n保存字符串个数;

输入字符串个数n和扫描次数k;
for i=0 to n
  输入字符串保存到str[i]中;
end for
for i=0 to n
  给指针p[i]动态分配内存
  使指针p[i]指向字符串str[i]的首地址;
end for
for i=1 to k
  for j=0 to n-1
     if (strcmp(p[j],p[j+1])>0)  //说明p[j]和p[j+1]所指向的字符串需要交换;
          ptemp=p[j];
          p[j]=p[j+1];
          p[j+1]=ptemp;
     end if
   end for
end for
输出交换后的字符串顺序;

运行结果:


多个字符串可以用指针数组和二维数组来保存,但是运用指针数组明显比二维数组要方便许多,如果是二维数组交换字符串的话,需要配合使用循环来交换两个字符串中的所有字符,运算量很大,但是指针数组只需要交换所指单元就可以,非常方便。而且如果有些输入的字符串长度非常大,有些输入的字符串长度又很小时,二维数组的列长度是固定,无法合理分配内存,会占用很多没有用的空间,而指针可以通过动态分配内存函数来分配空间,可以提高空间的使用效率。

8.二级指针、行指针

-8.1二级指针
一般形式:类型名 ** 变量名

char * color [5]={"red","blue","yellow","green","black"};
char **pc;
pc=color;
printf("%s\n",*(pc+1));
printf("%c\n",*((pc+1)+1);

运行结果:

  • 8.2行指针
    一般形式:类型名 (*变量名)[数组长度]
char color[5][10] = { "red","blue","yellow","green","black" };
char (*pc)[10];
pc = color;
printf("%s\n", *(pc + 1));
printf("%c\n",*(*(pc+1)+2));

运行结果:

比较二级指针和行指针的输出语句,可以看出行指针也是一个二级指针。

9.函数返回值为指针

函数返回值的类型可以是指针,但是一定要注意的是不能再实现函数时返回在函数内部定义的局部数据对象的地址,这是因为所有的局部数据对象在函数返回是就会消亡,其值不再有效!因此,返回指针的函数一般都返回全局数据对象或者指向字符串常量指针或堆区指针>,例如:输出月份英文名
指针数组:

返回的是字符串常量指针,可以返回,其所指的值不会消失;
二维数组

返回的是局部变量的地址,但是局部变量离开函数后,起地址中保存的值就消亡了;

1.2本章学习总结

  • 指针中有许多知识点特别相似,但是又有区别,如果不注意及时总结的话很容易搞混,我已经晕了TAT,离开课本的我就是一条废狗,刷pta的时候明显感觉到有些力不从心,有些题目一点思路都没有,后来发现是自己知识点掌握的不够熟,很多东西都已经学过了但是不会学以致用,还有太多相似的知识点很容易就搞混,要多去梳理整合。
  • 两周代码量:1291

2.PTA实验作业

2.1查找子串(函数题)

2.1.1 伪代码

/*函数search在字符串s中查找子串t,返回子串t在s中的首地址,若未找到,则返回NULL*/
定义变量i从字符串s中找到t的第一个字符开始遍历字符串s和字符串t;

while (*s)
   初始化i为0;
   while (*(s+i)==*(t+i)&&*(s+i)!='\0'&&*(t+i)!='\0')  //这里从字符串s中找到t的第一个字符开始,对i进行自增处理,保留当前s的地址,不断遍历字符串s和字符串t,直到不相等或者遍历到字符串s和t的结束标志;
          i++;
   end while
   if  遍历到字符串t的结束标志'\0'
       表示找到字符串t,直接返回s的地址;
   end if
   s++; //字符串s往下一个字符移动;
end while
程序运行到最后,还没结束,说明没有找到字符串t,返回NULL;

2.1.2 代码截图

2.1.3 总结本题的知识点

  • 设计返回指针类型的函数:pos=search(s,t);
  • 利用两个指针相减来求出位置:printf("%d\n", pos - s);

2.1.4 PTA提交列表及说明

编译错误:return s 语句后面忘记加分号

2.2说反话-加强版

2.2.1 伪代码

/*在函数ReverseStr中,将字符串中的每个单词倒序输出*/
定义字符指针endPtr用来寻找字符串的最后一个字符,首先对endptr赋初值为beginPtr
定义字符指针p;
定义变量len来保存单词长度;
定义变量flag来判断是否为要输出的第一个单词,对其赋初值为1;

while(*endPtr!='\n'&&*endPtr)  //逆向扫描到最后一个字符
     endPtr++;
end while
p=--endPtr; //使p指向最后一个非结束符的字符;

while(p!=beginPtr) //当p指向短剑的首地址时结束循环
  若*p不是空格,则统计单词长度;len++
  若*p不是空格,但前一个是空格,则找到单词
     根据flag的值判断是否为第一个要输出的单词,输出从p开始的len长度字符串;
     len=0;重新开始找单词;
   p--
end while

如果第一个字符(*beginPtr)不是空格的话,要考虑到短句中第一个单词的输出。

2.2.2 代码截图


2.2.3 总结本题的知识点

  • 如何查找单词:即当前字符非空格而前一个字符为空格;
  • 逆向扫描字符:while(p!=beginPtr) {p--}
  • 字符串指针表示字符串:printf("%.s",len,str);

2.2.4 PTA提交列表及说明

部分正确:短句中第一个单词前面没有空格时,第一个单词不会输出;
部分正确:短句中第一个单词的输出有问题,长度判断错误导致第一个单词的最后一个字符无法输出;
部分正确:最长句子,字符数组的长度应该为500002以上。

2.3删除字符串中的子串

2.3.1 伪代码

/*在函数DeleteB中删除子串*/
定义指针ptra指向a的首地址,后边对ptra进行操作;
定义字符数组c来保存字符串中出现的子串后面的字符;
定义指针locPtr来表示出现子串的位置;
定义lenB来保存子串B的长度;

while(loPtr=strstr(a,b)!=NULL)
    ptra=locPtr;
    ptra='\0';//因为在使用strcat函数时,被连接的字符串结尾应该要有结束标志'\0'
    strcpy(c,ptra+lenB); //用字符数组c来保存后面的字符;
    strcat(ptra,c); //将子串删去,连接除去子串后的剩下的字符;
end while
输出字符串a

2.3.2 代码截图

2.3.3 总结本题的知识点

  • 掌握字符串处理函数
    strstr函数可以帮找到子串,熟练掌握字符串处理函数可以帮助我们省略很多代码;
  • 这里也要区分输入函数,gets函数和fgets函数,用fgets函数吸收字符串时,如果长度小于可输入的最大长度,字符串结尾会带换行符,在使用字符串处理函数时要把换行符去掉;

2.3.4 PTA提交列表及说明

部分错误:最长字符串错误,数组长度没有设置好;
多种错误:格式错误,最后输出语句换行了,没有处理好换行符;
编译错误:pta中不承认gets_s函数,我从vs复制过来时忘记改了;
部分错误:自己定义了一个找子串的函数,然后运行超时。
部分错误:不熟悉字符串处理函数,在找到子串的位置应该加上'\0',否则在进行函数连接时会出错;

3.阅读代码



代码优点

  • 1.运用了dict数组作为辅助数组,dict函数组其实就是上一章我们学习的hash数组,这里用来映射ASCII码;
  • 2.边遍历边判断是否为最长无重复字符串,巧妙设计变量left来分别保存上一个和当前位置上字符重复的字符的位置和,这样可以方便求出无重复字符串的长度;
posted @ 2019-12-01 14:44  仙儿边  阅读(720)  评论(0编辑  收藏  举报