0.展示PTA总分
1.本章学习总结
1.1学习内容总结
指针变量的定义
类型名: *指针变量名
例如:
int *p;//定义一个整型变量p,指向整型变量;
指针和数组的赋值与初始化
一般情况下,数组的地址不能修改,内容可以修改;而指针的内容可以修改,指针指向的内容也可以修改,但这之前要为指针初始化。
如:
int p[5];
p=p+1; //错误
p[0]=1; //正确
int *p;
p=p+1; //正确
p[0]=1;//错误,指针没有初始化;
//
int i;
int *p=&i;
p[0]=1;//正确
对于字符指针还有比较特殊的情况。
如:
char * p="abc";
p[0]='d';//错误
指针做循环变量做法
例如:对数组元素求和
int a[100];
int *p;
int sum=0;
p=a;
for(p=a;p<=&a[99];p++)
{
sum+=*p;
}
在循环中,指针变量p的初值是数组a的基地址,P连续取值&a[0],&a[1],…&a[99]。
字符串指针如何表示字符串
- 对指向字符变量的指针变量应赋予该字符变量的地址。
例如:
char c, *p=&c;//表示p是一个指向字符变量c的指针变量。
char *s="apple";//表示s是一个指向字符串的指针变量。把字符串的首地址赋予s。
- 用字符串指针指向一个字符串。
例如:
#include <stdio.h>
int main(){
char *string = "I love China!";
printf("%s\n", string);
return 0;
}
动态内存分配
- (1)函数malloc()
原型:void* malloc(unsigned size);
1.在内存的动态存储区中分配一块长度为size字节的连续区域,参数size为需要内存空间的长度,若申请成功,则返回指向所分配内存空间的起始地址的指针;若申请不成功,则返回NULL.
2.函数malloc不能初始化所分配的内存空间,而函数calloc能.如果由malloc()函数分配的内存空间原来没有被使用过,则其中的每一位可能都是0;反之, 如果3.这部分内存曾经被分配过,则其中可能遗留有各种各样的数据.
调用函数malloc()时,应该利用sizeof计算储存块的大小,不能直接写数值,因为不同平台数据类型占用空间大小可能不同。
- (2)函数calloc()
1.原型:void* calloc(size_t n, size_t size);
2.参数size为申请地址的单位元素长度,n为元素个数,即在内存中申请n*size字节大小的连续地址空间.
3.函数calloc()在内存的动态储存区中分配n个连续空间,每一个储存空间的长度为size, 并将所分配的内存空间中的每一位都初始化为零,如果是字符类型或整数类型的元素分配内存,那么这些元素将保证会被初始化为0;如果是为指针类型的元素分配内存,那么这些元素通常会被初始化为空指针;如果是实型数据分配内存,则这些元素会被初始化为浮点型的零.
- (3)函数free()
1.原型:void free(void *ptr);
2.释放之后由动态储存分配函数申请的整块内存空间,ptr为指向要释放空间的首地址。free之后,申请内存的那个指针就会变成野指针,为避免出现野指针错误, 尽量在操作之后将指针置为NULL
3.注意:申请和释放是一起的,所以程序是不能进行多次free同一个指针,否则程序会出错。释放之后不可以再通过该指针去访问已经释放的块,否则可能引起灾难性的错误
指针数组及其应用
-
非常容易混淆的两个概念:“指针数组”与“数组指针”
指针数组:表示的是一个数组,数组中每一个变量都是指针型变量;
数组指针:表示的是一个指针类型的变量,这个指针变量指向的是一个数组。 -
数组指针
定义int (*)data[10] = NULL;//定义了一个指向长度为10的int数组(int [10])的指针.
-
指针数组:
定义 int *p[n];
[]优先级高,先与p结合成为一个数组,数组名为p,该p数组中有n个元素,分别为p[0]、p[1]、……、p[n-1],每个元素均为指向int类型的指针变量(即(int *) p[n]),即元素的值为地址。
指针数组应用:
例:查找奥运五环色的位置,用指针数组实现
#include<stdio.h>
#include<string.h>
int main()
{
int i;
char *color[5]={"red","blue","green","black","yellow"};
char str[20];
scanf("%s",str);
for(i=0;i<5;i++)
if(strcmp(str,color[i])==0)
break;
if(i<5)
printf("position:%d\n",i+1);
else
printf("Not Found\n");
return 0;
}
二级指针、行指针
- 二级指针
定义:类型名 **变量名;
例如:
int a=10;
int *p=&a;
int **pp=&p;
一级指针p指向整型变量a,二级指针pp指向一级指针p。由于p指向a,所以p和&a的值一样,a和*p代表同一个单元;由于pp指向p,所以pp和&p的值一样,pp和**p代表同一个单元
- 行指针
定义:int (*p)[3];
*(p+i);//即a[i],第i行数组名,指向第i行0列的int型元素
*(p+i)+j;//指向第i行j列的int型元素
*(*(p+i)+j);//取出第i行j列的内容
函数返回值为指针
- 使用指针作为函数参数返回多个值的示例
-运行结果
1.2本章学习体会
- 本章学习了指针,包括数组指针,指针数组,字符指针,一级、二级指针等重要内容,内容很多,相对来说也比较难,知识点抽象不易理解,做题无从下手。但是指针是学习C语言最重要的部分,务必要掌握,不能急躁,先理解它的概念,之后熟悉它的格式以及使用方法,多打代码练习。
- 代码量有1000以上。
2.PTA实验作业
2.1题目7-2 藏尾诗
2.1.1伪代码
for i=0 to N
输入字符串
动态分配:p[i]=(char*)malloc(sizeof(str)+1)
将输入的字符串复制到p[i]
end for
for i=0 to N
求字符数组p[i]的长度
输出最后一个字组成的字符串
end for
2.1.2代码截图
2.1.3总结本题知识点
动态分配:p[i]=(char*)malloc(sizeof(str)+1)
将输入的字符串复制到p[i]:strcpy(p[i],str)
求字符数组p[i]的长度:len=strlen(p[i])+1
输出最后一个字组成的字符串:printf("%s",p[i]+len-2)
2.1.4PTA提交列表及说明
答案错误:一开始用fgets输入字符串,后来改为用fets输入,还是答案错误。
答案错误:输出字符串的时候没有减去2。
格式错误:刚开始用puts循环输出
答案错误:改为用printf循环输出,写成了%c答案错误,再改为%s
格式错误:在printf()里面加上了\n
2.2题目6-10 填充矩阵
2.2.1伪代码
for i=0 to n
for j=0 to n
if(是副对角线上的元素)
*(p[i]+j=1;
end if
if(是副对角线下方的元素)
*(p[i]+j=2;
end if
if(是副对角线上方的元素)
*(p[i]+j=3;
end if
end for
end for
2.2.2代码截图
2.2.3总结本题知识点
副对角线上的元素:(i+j)==n-1
副对角线下方的元素:(i+j)>n-1
副对角线上方的元素:(i+j)<n-1
取出第i行j列的内容:*(p[i]+j)
2.2.4PTA提交列表及说明
运行超时:不知道哪里错了,试试在每个if语句后面加个break,还是一样的结果。
运行超时:一开始写的是*(*(p+i)+j),后来发现可以改成*(p[i]+j)。
运行超时:找了很久发现if语句里面i+j没有加上括号,改成if((i+j)==n-1)
答案错误:副对角线上方下方的元素是2还是3搞混了
答案错误:写成了(i+j)==n,运行发现1都不在副对角线上,就改成(i+j)==n-1
2.3题目6-6 查找子串
2.3.1伪代码
i=0,j=1;
while(s[i])
if(在字符串中找到子串的第一个字符)
while(s[i+j]==t[j]&&t[j]!='\0')
j++;
end while
if(t[j]=='\0')
返回返回子串t在s中的首地址
end if
end if
i++;
end while
2.3.2代码截图
2.3.3总结本题知识点
如果在字符串中找到子串的第一个字符,则开始从当前位置扫描子串
if(s[i]==t[0]
{
while(s[i+j]==t[j]&&t[j]!='\0')
j++;
}
返回子串t在s中的首地址:return s+i;
2.3.4PTA提交列表及说明
部分正确:搞不清楚j是从0开始还是从1开始。
部分正确:错误写成当t[j]不等于0时返回子串t在s中的首地址。
部分正确:不知道t在s中的首地址怎么表示。
3.阅读代码
- 代码功能:计算比赛中问题的最大期望值,并能实现找出字典上最小的序列,使预期数目的解决问题最大。
- 代码优点:
使用using namespace std调用命名空间std内定义的所有标识符,这样可以在不同命名空间里,起同样的变量名,防止变量名用尽或者冲突。
调用多个头文件,赋予了调用某些函数的权限,便于进行类型检查,增加程序可读性。
使用多次宏定义,实现了一些简单的函数功能,提升了程序编写的灵活性。
使用typedef定义类型的别名,可以减少错误的发生,而且直观简洁。