0.展示PTA总分
1.本章学习总结
1.1 学习内容总结
————————————————————————————————————————————
No.1 指针变量的定义
在程序中声明一个变量并用地址作为该变量的值,那么这个变量就是指针变量。指针是变量的一种,里面是一个地址,通过这个地址找到要找的数据 (实质:单元的地址就是指针内容)
而地址是由编译器将需要存储的单元并结合存储长度来分配地址。(注:以p为例,p是代表这个指针指向的地址,而p才是具体的值。)
————————————————————————————————————————————
No.2二级指针
区别
p1:指针变量名
*p1:访问p1指向的变量
p2:二级指针变量【指向一级指针,保存的是一级指针变量的内存地址】
p2:获取一级指针的值【即一级指针指向的变量的内存地址】
**p2:获取一级指针指向的变量的值
输出p2得到的是一级指针变量的地址
输出p2得到的是一级指针变量指向的普通变量的地址
输出**p2得到的是一级指针变量指向的普通变量的值
No.3 行指针和列指针
行指针就是第一个元素的地址。也就是第一行的首地址,是指首行一整行,并不是指某个具体元素。
行指针是指向数组的指针,即 int (*a)[5];所以,当二维数组要被当做参数进行传递时,可以这样声明:
void funcByRowPtr(int (*p)[5],const int row);
或者
void funcByRowPtr(int p[][5],const int row);
而列指针是第一个元素的地址,是指具体的元素。可以直接声明如下:
void funcByColPtr(int * const colPtr,const int row,const int col);
————————————————————————————————————————————
No.4指针的运算
赋值:int a = 3 ; int *p = &a ;
和 int *p ; p = &a ;
运算:int x=3, y=0 , *px = &x;
y = *px +5; //把x的内容+5赋值给y
y = ++*px ; //px内容+1,赋值给y,
//++*px相当于++(*px)
初始化(指针变量在定义后也要先初始化再引用):例如:
int a;
int *p1=&a; /*在定义指针的同时赋值,并使它指向变量a*/
int *p2=p1; /*在定义指针的同时赋值,并使它与p1的值相同*/
(不能用数值作为指针初始化的变量,常用的是将指针变量初始化为一个空指针)
————————————————————————————————————————————
No.5指针的含义和与数组的关系
数组的指针是指数组的起始地址,数组元素的指针是数组元素的地址
int a[10]; /*定义a为包含10个整型数据的数组*/
int *p; /*定义p为指向整型变量的指针*/
p=&a[0]; 把a[0]元素的地址赋给指针变量p
也就是说,p指向a数组的第0号元素
p+i和a+i都是a[i]的地址
*(p+i)和*(a+i)都是p+i和a+i所指向的数组元素,即a[i]
(注:指针指向的数据是无法通过改变指针来改变数据的值的)
用字符串指针指向一个字符串。
int main()
{
char *string[]={”I love China!”,”I am ”};
printf("%s
",string);
}
```
char *s="C Language"; 则表示s是一个指向字符串的指针变量。
把字符串的首地址赋予s
只能对字符数组逐个赋值
``` char *ps; ps="C Language"; ```或
```char *ps="C Language"```
或者是用strcpy函数进行复制
(注:字符串指针变量本身是一个变量,用于存放字符串的首地址。而字符串本身是存放在以该首地址为首的一块连续的内存空间中并以‘ ’作为串的结束
字符数组是由于若干个数组元素组成的,它可用来存放整个字符串)
当一个指针变量在未取得确定地址前使用是危险的,容易引起错误
但是对指针变量直接赋值是可以的。因为C系统对指针变量赋值时要给以确定的地址。
因此 char *ps="C Langage";
或者 char *ps; ps="C Language"; 都是合法的
**例:输出月份英文名**
用*p[12]储存各个月份的名字,最后通过getmonth(int n)函数返回存储了n对应的月份英文名称的字符串头指针
![](https://img2018.cnblogs.com/blog/1783960/201912/1783960-20191208095448277-1373895197.png)
————————————————————————————————————————————
###**No.6动态内存分配**
** malloc()函数和free()函数**
首先,所有的程序都必须留出足够的内存空间来存储所使用的数据,所以我们常常会预先给数据开辟好内存空间来存放,然后进行操作,但事实上另一种选择,能够让内存分配自己主动进行下去。
int arr[5] ;
对这个数组我们在定义的时候必须给提前开辟好空间。而且在程序运行的过程中,这个开辟的内存空间是一直存在的。除非等到这个函数运行完成,才会将空间释放。另一个问题就是这个数组在程序中无法被改动。
因此就需要用到动态内存分配
如
double p;
p=(double)malloc(30*sizeof(double));
在这个程序中,开辟了30个double类型的空间,然后把p指向这个空间的位置
malloc()函数。这个函数它接受一个參数:就是所需的内存的字节数。然后malloc()找到可用内存中那一个大小适合的块
int arr[n];
p=(int )malloc(nsizeof(int));
//我们在这里使用的时候要元素个数乘类型字节长度。这样就达到了动态开辟内存空间。
但是当我们使用malloc()开辟完内存空间以后,需要释放内存空间,在这里,C给我们提供了free()函数。
free()的參数就是malloc()函数所返回的地址,释放先前malloc()函数所开辟的空间。
如
free(p);
###拓展
calloc动态申请内存
int n;//记录输入总个数
int *p;
scanf("%d",&n);
p=(int *)calloc(n , (sizeof(int));
realloc动态申请内存
int n;//记录输入总个数
int **p=NULL;
scanf("%d",&n);
p=(int **)calloc(p , n * (sizeof(int *));
calloc()函数和realloc()函数。 也是关于内存分配的函数
calloc()函数与malloc()函数有同样之处。也有类似之处。
例:
short *p;
newmem=(short *)calloc(1000,sizeof(short));
calloc()函数有两个參数。而且这两个函数都是size_t类型的数。
void *calloc(size_t ,size_t);
calloc()函数另一个特性。它将块中的所有位都置为0。这也是calloc()函数和malloc()函数的差别,calloc()函数和malloc()函数的另外一个差别是他们请求内存数量的方式不一样。当然。free()函数也能够来释放calloc()函数分配的内存。
realloc()函数用来改动一个原先已经分配的内存的大小。使用这个函数,你能够让一块内存增大还是缩小。当扩大时。这块内存原先的内容会依旧保留,新添加的加入到原先的后面。缩小时,该内存的尾部部分内存去掉,剩余保留。
————————————————————————————————————————————
###**No.7指针函数**
指针函数,即返回值为指针的函数,这样定义主要是常常用到返回值是一个数组的情况。
例:
![](https://img2018.cnblogs.com/blog/1783960/201912/1783960-20191204211637954-793665913.png)
##1.2 本章学习体会
指针这一章节,有点颠覆了以前的打代码的习惯,是一种新的方式,以前打比较简单的代码的时候,还是不喜欢用指针,觉得太麻烦,每次编程最讨厌的就是指针,但是有的时候需要数据的交换和循环判断的时候就需要指针,指针也恰恰是C语言的灵魂。指针的用途非常广泛,比如如果你想通过函数改变一个变量的值,就得用指针而不能用值传递。还有在很多时候变量,特别是对象的数据量实在太大,就需要指针来做形参,只需要传递一个地址就行,大大提高了效率就像刚刚接触函数一样,嫌他麻烦,老是觉得为什么偏偏要定义一个函数,而不是直接把代码打出来呢,其实道理差不多,就像是存在即合理吧,函数存在肯定是有他的意义的,当第一次编写大作业的时候,函数的定义就显得十分重要,这就让代码显得有条理,期待这次的大作业,希望能够把我对指针的理解更进一步。
##2.PTA实验作业
##2.1 (指针做函数返回值) 查找指定字符
##2.1.1伪代码
主函数
输入要查找的字符
需要查找的字符串
定义char* Found(char* str, char p)函数
if (函数返回值= NULL)
{
输出Not Found;
}
else
{
输出函数返回的下标
}
}
char* Found(char* str, char p)
{
for (遍历数组)
{
if (数组元素和需要查找到额字符相同)若字符串中没有找到p字符,则flag的值不会改变,使得直接进入else语句
{
记录下标
用flag控制是否进入循环;
}
}
if (flag == 1)
{
返回下标;
}
else
{
返回NULL;
}
}
##2.1.2 代码截图
![](https://img2018.cnblogs.com/blog/1783960/201912/1783960-20191208104234640-1748697853.png)
![](https://img2018.cnblogs.com/blog/1783960/201912/1783960-20191208104951104-700419265.png)
##2.1.3 总结本题的知识点
本题知识点首先就是要熟练的掌握用指针作为函数返回值的应用,同时还要掌握利用if (Found(str, p) == NULL)函数返回值为空指针的方式来控制输入和输出,但是其实本题不利用指针作为函数的返回值会更加简便一点,代码量会简便很多
![](https://img2018.cnblogs.com/blog/1783960/201912/1783960-20191208105245797-54350257.png)
##2.1.4 PTA提交列表及说明
![](https://img2018.cnblogs.com/blog/1783960/201912/1783960-20191208105359136-618951353.png)
提交列表说明:段错误其实就不要多说了,就是对str传递的时候出现了问题,因为这是第一道编程题嘛,调试了好
久才做出来。其实这个部分正确我还是有点疑问的,因为过不了的测试点是字符串间有空格和sample1等价的测试点
,但是我在vs上调试,输出的结果是和答案一样的。
##2.2 填充矩阵
##2.2.1伪代码
主函数
定义二维数组
定义函数tian
tian(a);
for遍历二维数组
输出数组
void tian(int(*p)[n])
{
副对角线的值令为1
右下角的值令位2
左上角的值令为3
}
##2.2.2 代码截图
![](https://img2018.cnblogs.com/blog/1783960/201912/1783960-20191208110219247-1682824667.png)
##2.2.3 总结本题的知识点
此题可以说是不需要用到指针的知识点也可以轻松的做出来,其实本体的难点就是要分别对左上角,右下角和副对角
线的值分别编程3 2 1,追主要的就是找到规律,我觉得这题很有意思,所以就拿出来分享一下。
##2.2.4 PTA提交列表及说明
![](https://img2018.cnblogs.com/blog/1783960/201912/1783960-20191208110507414-1670771644.png)
提交列表说明:因为这种题目没什么部分正确的我都是现在vs上调试完整之后再提交的,样例如果调试正确的话,一般pta就能过了。
##2.3 查找子串
##2.3.1伪代码
include <stdio.h>
define MAXS 30
char* search(char* s, char* t);
void ReadString(char s[]); /* 裁判提供,细节不表 */
主函数
ReadString(s);
ReadString(t);
pos = search(s, t);
通过对pos的返回值来判断输出内容
char* search(char* s, char* t)
{
for遍历两个数组即用内外循环
if (找到相等的元素)
{
for (从相等时的下标开始进行判断)
{
if (两个数组有一个元素不等)跳出循环
}
if (通过判断n是否等于len来判断是否为子串)返回s[j];
若跳出循环返回 NULL;
}
##2.3.2 代码截图
![](https://img2018.cnblogs.com/blog/1783960/201912/1783960-20191208111408029-668517979.png)
![](https://img2018.cnblogs.com/blog/1783960/201912/1783960-20191208111426336-1312003283.png)
##2.3.3 总结本题的知识点
其实感觉自己的做法还是比较繁琐,本体的知识点就是查找子串,利用内外循环对两个数组同时推进,然后同时进行对比,
当找到第一个元素相等时,再次进入一个循环接下去继续判断是否为完整的子串,但是其实如果本体不是函数题的话可以
直接利用到strstr函数并进行调试,应该是可以得出答案的。
##2.3.4 PTA提交列表及说明
![](https://img2018.cnblogs.com/blog/1783960/201912/1783960-20191208111838095-1132764207.png)
提交列表说明:部分正确时只过了返回值时NULL的测试点,原因时在查找子串的时候没有if (n == len)return &s[j];
这句语句,这就意味着,只要出现了和子串开头相同的元素,就会进入循环,但是没有是否跳出循环的这个判断,这就导致
了,只要出现了和子串相同的首元素就会返回它的地址。
#3.阅读代码
![](https://img2018.cnblogs.com/blog/1783960/201912/1783960-20191208113129563-1283250453.png)
![](https://img2018.cnblogs.com/blog/1783960/201912/1783960-20191208113146226-2101239869.png)
本体其实就是求原根
原根是一种数学符号,设m是正整数,a是整数,若a模m的阶等于φ(m),则称a为模m的一个原根。(其中φ(m)表示m的欧拉函数)
假设一个数g是P的原根,那么g^i mod P的结果两两不同,且有 1<g<P,0<i<P,归根到底就是g^(P-1) = 1 (mod P)当且仅当指数为P-1的时候成立.(这里P是素数)。
简单来说,g^i mod p ≠ g^j mod p (p为素数),其中i≠j且i, j介于1至(p-1)之间,则g为p的原根。
从2开始枚举,然后暴力判断g^(P-1) = 1 (mod P)是否当且仅当指数为P-1的时候成立。而由于原根一般都不大,所以可以暴力得到。