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的开头,在每一步将最小值放入输出数组中。便于理解。

posted @ 2019-12-01 11:55  不会soma  阅读(294)  评论(0编辑  收藏  举报