C博客作业05—指针
0.展示PTA总分(0----2)
展示关于“指针题目集”分数截图。
1.本章学习总结(2分)
1.1 学习内容总结
(1) 指针做循环变量做法
#include<stdio.h>
void main()
{
int arr[]={6,4,3,5,8,1};
int len = sizeof(arr);
for(int i=0;i<len;i++)
{
printf("%d\n",arr[i]);//常规遍历方式
}
for(int i=0;i<len;i++)
{
printf("%d\n",*(arr+i));//使用arr指针遍历方式
}
int *p_arr=arr;
for(int i=0;i<len;i++)
{
printf("%d\n",*(p_arr+i));//额外使用新指针来遍历数组
printf("%d\n",*p_arr++);//额外使用新指针来遍历数组
}
}
(2) 字符指针如何表示字符串
- C语言中没有特定的字符串类型,我们通常是将字符串放在一个字符数组中。字符数组归根结底还是一个数组,当然关于指针和数组的规则同样也适用于字符数组。举一个例子:
#include <stdio.h>
#include <string.h>
int main(){
char str[] = "asdfghjkl";
char *pstr = str;
int len = strlen(str), i;
/*使用*(pstr+i)形式*/
for(i=0; i<len; i++){
printf("%c", *(pstr+i));
}
printf("\n");
/*使用pstr[i]形式*/
for(i=0; i<len; i++){
printf("%c", pstr[i]);
}
printf("\n");
/*使用*(str+i)形式*/
for(i=0; i<len; i++){
printf("%c", *(str+i));
}
printf("\n");
return 0;
}
上面的三种输出形式输出的结果都是一样:asdfghjkl
除了字符数组,C语言还支持另外一种表示字符串的方法,就是直接使用一个指针指向字符串,例如:
char *str = "asdfghjkl";
或者:
char *str;
str = "asdfghjkl";
字符串中的所有字符在内存中是连续排列的,str 指向的是字符串的第 0 个字符;我们通常将第 0 个字符的地址称为字符串的首地址。字符串中每个字符的类型都是char,所以 str 的类型也必须是char *。
(3) 动态内存分配
首先,我们应该知道。所有的程序都必须留出足够的内存空间来存储所使用的数据,所以我们常常会预先给程序开辟好内存空间,然后进行操作。例如,我们经常会利用数组来储存我们需要的数据。但是利用数组的时候需要提前设置数组长度。在今后的学习中我们经常会不知道数据大小是多少,如果直接设置一个较大的量大的话会导致空间的浪费,并且,如果数据超过了数组数据类型最大长度的话,也会造成错误。对于传统数组,会遇到这种问题:
int arr[5] ;
- malloc()函数;
对这个数组我们在定义的时候必须给提前开辟好空间。而且在程序运行的过程中,这个开辟的内存空间是一直存在的。除非等到这个函数运行完成,才会将空间释放。
另一个问题就是这个数组在程序中无法被改动。这些问题给我们造成了一些使用上的不方便,所以,C中提供了malloc()函数。 关于malloc()函数。这个函数它接受一个參数:就是所需的内存的字节数。然后malloc()找到可用内存中那一个大小适合的块。在这个过程中,malloc()能够来返回那块内存第一个字节的地址。所以。也就意味了我们能够使用指针来操作。malloc()能够用来返回数组指针、结构指针等等。所以我们须要把返回值的类型指派为适当的类型。当malloc()找不到所需的空间时。它将返回空指针。例如:
int *p;
p=(int*)malloc(3*sizeof(int));
在这个程序中,首先开辟了3个int类型的空间,然后把p指向这个空间的位置。在这里的指针是指向第一个int值。并非我们所有开辟的3个int的空间。这就和数组一样,指向数组的指针式指向数组首元素的地址,并非整个数组的元素。所以,在这里我们的操作也和数组是一样的, p[0]就是第一个元素。p[2]就是最后一个元素。 至此。我们就能够掌握到一种声明动态数组的方法。(不要忘记要将该动态内存的首地址进行强制类型转化换成int型并存放在指针p中)
int arr[n];
p=(int *)malloc(n*sizeof(int));
//我们在这里使用的时候要元素个数乘类型字节长度。这样就达到了动态开辟内存空间。
-
free()函数;
当我们使用malloc()开辟完内存空间以后,我们所要考虑的就是释放内存空间,在这里,C给我们提供了free()函数。free()的參数就是malloc()函数所返回的地址,释放先前malloc()函数所开辟的空间。 如果申请了动态内存而没有及时释放的话,会造成内存的溢出; -
calloc()函数;
与 malloc() 相比有两个优点:
(1)它把内存分配为给定大小的数组;
(2)它初始化了所分配的内存,所有位都是 0。
int *pNumber = (int*)calloc(75, sizeof(int));
// 分配了包含75个int元素的数组
如果不能分配所请求的内存,返回值就是 NULL。
- relloc()函数;
realloc() 函数可以重用或扩展以前用 malloc()或 calloc()(或者realloc())分配的内存。
realloc()的两个参数:
1)、一个是包含地址的指针,该地址以前由malloc()、colloc()或realloc()返回。
2)、要分配的新内存的字节数。
realloc()函数分配第二个参数指定的内存量,把第一个指针参数引用的、以前分配的内存内容传递到新分配的内存中,传递的内容量是新旧内存中区域较小的那一个。
realloc()函数返回一个指向新内存的void指针,如果分配失败,返回NULL。如果第一个参数是NULL,就分配第二个参数指定的新内存,类似malloc()。
realloc()保存了原内存区域的内容,且保存的量是新旧内存区域中较小的那个。如果新内存区域大于旧内存区域,新增的内存就不会初始化,而是包含垃圾值。
(4) 指针数组及其应用
数组(Array)是一系列具有相同类型的数据的集合,每一份数据叫做一个数组元素(Element)。数组中的所有元素在内存中是连续排列的,整个数组占用的是一块内存。定义数组时,一定要给出数组名,数组名可以认为是一个指针,它指向数组的第 0 个元素。在C语言中,我们将第 0 个元素的地址称为数组的首地址。虽然可以认为是一个指针,但你在定义时在数组变量名前面加 * ,这就不是一个常规的数组了,而是指针数组,里面的所有元素都是指针。
#include<stdio.h>
void main()
{
int arr[] = {1,2,3,4,5,6};
//这里的变量名arr里面存放的有该数组的第一个数据的地址也就是arr[0]的地址值
//所以可以认为arr就是该数组的指针
int *p=arr;
int *arr[]={p};
(5)二级指针、行指针
- (1)二级指针
指针可以指向一份普通类型的数据,例如 int、double、char 等,也可以指向一份指针类型的数据,例如 int *、double *、char * 等。如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针。
int a =100;
int *p1 = &a;
int **p2 = &p1;
指针变量也是一种变量,也会占用存储空间,也可以使用&获取它的地址。C语言不限制指针的级数,每增加一级指针,在定义指针变量时就得增加一个星号*。p1 是一级指针,指向普通类型的数据,定义时有一个*;p2 是二级指针,指向一级指针 p1,定义时有两个*。你想是几级的指针,p前面就加几个*。
* 行指针;
行指针,顾名思义就是指向一行的指针。在二维指针中,行指针用的最多。我们通常把二维指针看成一个行列式,但是它在内存中的排序却是和一维指针一样的。比如组a[2][3]={{1,2,3}{4,5,6}},a是整个数组的首地址,同时也指向第一行元素,即a是一个行指针,它每加1,所指地址移动二维数组的一行,a+1指向第二行元素。对a取*,即*a指向第一行第一个数,*(a+1)指向第二行第一个数,可见,对行指针取值就成了列指针,此时它还是个指针。它每加1,所指地址移动一个元素,*(a+1)+1指向第二行第二个元素,也可以写成a[1]+1。**a(也可写成a[0][0])就是这个二维数组第一行的第一个元素,**(a+1)(也可写成a[1][0])就是第二行的第一个元素,*(*(a+1)+1)(也可写成a[1][1])是第二行的第二个元素。可见,对行指针取2次*就成了某个元素的值了,而不再是地址。
(6)函数返回值为指针
指针可以作为函数的参数传给函数,那么也一定可以作为函数的返回值,返回给调用函数。比如,写一个返回两者之中较长字符串的函数的示例代码:
#include<stdio.h>
#include<string.h>
char *compare(char *str1,char *str2){
if(strlen(str1)>strlen(str2)){
return str1;
}else if(strlen(str1)<strlen(str2)){
return str2;
}else{
char *r = "一样长";
return r;
}
}
void main(){
char *str1 = "123";
char *str2 = "1234";
char *r=compare(str1,str2);
printf("%s\n",r);
}
打印结果:1234
用指针作为函数返回值时需要注意的一点是,函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、局部数组和形式参数等,函数返回的指针请尽量不要指向这些临时数据,谁都不能保证这些临时的数据一直有效,它们在后续使用过程中可能会引发运行时错误。
1.2 本章学习体会
之前听说,C语言有两大利器:指针和强制转化。其中指针更是C语言的灵魂。过了指针这一关才算是真正学习了c语言。刚开始接触的时候感觉一脸懵,完全不知道指针的作用是什么。书上的文字描述也比较生涩难懂,一度不知道该如何学习C。直到上课的时候,老师讲指针就像是一个行走的箭头,它会走向你想要的对象的地址,在我脑海里形成了完整的运行图。后来对于二级指针的理解也就更加深刻了。这一部分比较难,学起来也比较吃力,尤其是在自己编写代码的时候,所以可能还是自己没有用足够多的时间在这上面才会这样吧。
时间 | 代码量 |
---|---|
十三周 | 1103 |
十四周 | 1002 |
2.PTA实验作业(7分)
2.1(指针做函数返回值) 查找指定字符
2.1.1 伪代码
函数定义
定义字符变量op;
定义数组str[M];
定义循环变量i;
定义整型变量len计算数组长度;
输入字符串存入字符数组中以及输入要查找的字符;
自定义一个Search函数搜索字符所在的位置;
if (Search(str, op) != NULL)
{
输出下标index;
}
else
{
输出"Not Found";
}
char* Search(char* str, char op)
{
char* p;//定义一个新指针存位置,并且这个指针应该为空;
p = NULL;
for str to *str=='\0'
{
if (*str == op) p = str;
}
return p;返回指针;
}
2.1.2 代码截图
2.1.3 总结本题的知识点
(1)如何利用指针做函数的返回值来解决问题;
char* p;//定义一个指针,用来储存找到字符的地址,作为返回;
p = NULL;
for (str; *str != '\0'; str++)
{
if (*str == op)
{
p = str;
}
}
return p;
(2)要知道计算数组长度要用strlen()函数,但是这个函数后面的括号中不能为空指针(NULL)。
2.1.4 PTA提交列表及说明
1.多种错误:开始的时候并没有好好理解题目,所以多种错误;
2.部分正确:之前的做法是在函数内部定义了一个整型变量,返回值的时候返回这个整数值,但是呢,这是一个指针型的函数,所以错误了;
3.部分正确:计算数组长度要用strlen()函数,但是这个函数后面的括号中不能为空指针(NULL);我开始的做法是在主函数里面定义一个index;用strlen()来计算它的下表,这样的作法会令大部分测试点过去,但是第一个测试点过不去;
4.全部正确:终于等到你。。。
2.2 删除字符串中的子串
2.2.1
定义指针*p记录子串查找结果;
定义字符数组mother[81],son[81]分别输入母串和子串;
定义整型变量len计算子串长度;
输入母串;
输入子串;
while (p = strstr(mother, son)) != NULL 如果strstr函数返回值不为NULL,证明有查找到子串,这里用了strstr函数;
*p = '\0'; 将找到子串的位置赋一个结束符‘\0’
len=strlen(son);计算字串的长度;
strcat(mother, p + len); 跳过字符串长度个数,将字符串的后半部分复制到结束符之前的部分
end while
输出母串;
2.2.2 代码截图
2.2.3 总结本题的知识点
- strstr()函数查找子串
包含文件:string.h
函数名 : strstr
函数原型:
extern char * strstr(char * str1, const char * str2);
语法:
* strstr(str1, str2)
str1: 被查找目标 string expression to search.
str2 : 要查找对象 The string expression to find.
返回值:若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL。
例子:
char * strstr(const char * s1, const char * s2)
{
int len2;
if (!(len2 = strlen(s2)))//此种情况下s2不能指向空,否则strlen无法测出长度,这条语句错误
return(char*)s1;
for (; *s1; ++s1)
{
if (*s1 == *s2 && strncmp(s1, s2, len2) == 0)
return(char*)s1;
}
return NULL;
}
2.2.4 PTA提交列表及说明
1.多种错误:没有用strstr()函数的时候觉得很麻烦,尤其是子串找完一次之后再继续找。所以多种错误;
2.编译错误:!与=之间多打了一个空格;
3.全部正确:终于等到你。。。
2.3 题目名3 用指针将数组中的元素逆序存放
2.3.1 伪代码
void inv(int* x, int n)
{
函数定义
定义一个整型指针;
定义一个整型变量i;
定义整型变量t;
for p=x to i<n/2
//进行首尾交换
t = *(p + i);
*(p + i) = *(p + n - 1 - i);
*(p + n - i - 1) = t;
}
2.3.2 代码截图
2.3.3 总结本题的知识点
指针的简单运用,利用指针的遍历解决首尾交换的问题,理解指针的用法。
2.3.4 PTA提交列表及说明
1.答案错误:没有理解交换之间的关系,导致出现了错误;
2.全部正确:。。。
3.阅读代码(-2--1分)
这是一个解决合并两个有序数组问题的一个方法。和老师讲的不一样的是,它运用了倒序进行比较的方法,大大减少了代码量。但是呢,这种方法还是无法通过较大数据量的测试点。所以,还有下面的方法:
将指针p1 置为 nums1的开头, p2为 nums2的开头,在每一步将最小值放入输出数组中。便于理解。