• 我の第六篇博客


    这个作业属于哪个班级 C语言--网络2011/2012
    这个作业的地址 C博客作业05--指针
    这个作业的目标 学习指针相关内容
    姓名 付峻霖

    0.展示PTA总分

    1.本章学习总结

    基础知识点

    1. 指针与指针变量
      指针存放的是地址
      普通变量存放的是数据
      指针变量的类型:就是它存放的地址指向的数据类型
    2. 定义指针变量
      类型名 *指针变量名
      char *pa; //定义一个指向字符型的指针变量
      int *pb; //定义一个指向整型的指针变量
    3. 取地址运算符和取值运算符
      如果需要获取某个变量的地址,可以使用取地址运算符(&):
      char *pa = &a;
      char *pb = &f;
      如果需要访问指针变量指向的数据,可以使用取值运算符
      printf("%c, %d ", *pa, *pb);
    4. 指针变量都是四个字节,因为地址都是四个字节,与他们指向的数据类型没关系
      类型名 *指针变量名
      char *pa; //定义一个指向字符型的指针变量
      int *pb; //定义一个指向整型的指针变量
    5. 如何使用指针的相关知识?
      代码:
    #include<stdio.h>
    int main()
    {
    	char a = 'F';//定义一个指向字符型的指针变量
    	int f = 123;//定义一个指向整型的指针变量
    
    	//获取某个变量的地址
    	char* pa = &a;
    	int* pb = &f;
    
    	//‘*’号可访问指针变量指向的数据
    	printf("a = %c
    ", *pa);//a = F
    	printf("f = %d
    ", *pb);//f = 123
    
    	//间接对 a 和 f 访问
    	*pa = 'C';//通过指针间接给变量a赋值
    	*pb += 1;//通过指针间接对变量f进行运算
    
    	printf("a = %c
    ", *pa);//a = C
    	printf("f = %d
    ", *pb);//f = 124
    
    	printf("sizeof pa = %d
    ", sizeof(pa));
    	printf("sizeof pb = %d
    ", sizeof(pb));
    	//      sizeof pa = 4
    	//      sizeof pb = 4
    
    	return 0;
    }
    
    1. 注意事项
      ①避免访问未初始化的指针,否则不知道它到底哪儿
      下面这个就是“野指针”
    #include<stdio.h>
    int main()
    {
    	int* a;
    	*a = 123;
    	return 0;
    }
    

    1.1 指针定义、指针相关运算、指针做函数参数。

    (1) 指针的定义

    	char a = 'F';//定义一个指向字符型的指针变量
    	int f = 123;//定义一个指向整型的指针变量
    
    	//获取某个变量的地址
    	char* pa = &a;
    	int* pb = &f;
    

    (2) 指针相关运算

    #include<stdio.h>
    int main()
    {
    	int a = 50;//定义一个指向整型的指针变量
    	int b = 60;//定义一个指向整型的指针变量
    	int* p = &a;//获取a变量的地址
    	int* q = &b;//获取b变量的地址
    	printf("%d
    ", a);  //内容
    	printf("%d
    ", *&a);//内容
    	printf("%d
    ", *p); //内容
    	printf("%d
    ", *&p);//地址
    	printf("%d
    ", p);  //地址
    	printf("%d
    ", &a); //地址
    	printf("%d
    ", *&p);//地址
    	printf("%d
    ", (int)p - (int)q);//相隔的存储单元数目
    	printf("%d
    ", p - q);          //相隔的字节数
    }
    


    注意事项:
    ①指针类型要和指向数据类型一样
    ②不能数值作为指针变量的初值,否则不知道它到底哪儿
    ③指针变量都是四个字节,因为地址都是四个字节,与他们指向的数据类型没关系
    (3) 指针做函数参数
    例子:利用指针交换数字

    #include<stdio.h>
    void Swap(int* p, int* q);  //函数声明
    int main(void)
    {
        int i = 3, j = 5;
        Swap(&i, &j);//交换
        printf("i = %d, j = %d
    ", i, j);
        return 0;
    }
    void Swap(int* p, int* q)
    {
        int buf;
        buf = *p;
        *p = *q;
        *q = buf;
        return;
    }
    

    此时实参形参传递不是变量ij数据而是变量ij地址

    • 注意点:
      形参中变量名分别为p和q,变量类型都是int* 型。所以实参i和j的地址&i和&j是分别传递给p和q而不是传递给p和q
      Q1:为什么不用指针传递,就无法交换i,j呢?
      A1:因为实参和形参之间传递是单向的,只能由实参向形参传递。被调函数调用完之后系统为其分配的内存单元都会被释放。
      所以虽然将i和j的值给了a和b,但是交换的仅仅内存单元a和b中的数据,对i和j没有任何影响
      Q2:为什么不用 return 语句?
      A2:因为 return 语句只能返回一个值,并不能返回两个值。

    1.2 字符指针

    (1) 指针如何指向字符串

    #include <stdio.h>
    int main()
    {
    	char a[100] = "breadfruit";
    	char* p = a;
    	printf("%s
    ", p);
    	printf("%s
    ", p + 3);
    	return 0;
    }
    


    p指向的是a的首地址,而(p+3)指向的是a的第四个字母的地址

    (2) 字符串相关函数

    函数名 定义 功能 返回值
    strcmp int strcmp(char* str1, char* str2); 比较字符串 返回正值或者1;返回0;返回负值或者-1;
    strcat char* strcat(char* str1, char* str2); 连接字符串 str1字符串的首地址
    strcpy char* strcpy(char* str1, char* str2); 复制字符串 str1字符串
    strlen int strlen(char* str1); 求字符串长度 字符串长度

    (3) 字符串相关函数用法

    strcmp
    

    str1<str2,返回负值或者-1; str1=str2,返回0; str1>str2,返回正值或者1;

    strcat
    

    将字符串str2连接在str1,并且str1最后的结束字符NULL被覆盖掉,并且连接后的字符串的尾部会再增加一个NULL
    注意:str1和str2所指的内存空间不能重叠,且str1要有足够的空间容纳要复制的字符串

    strcpy
    

    str2指的字符串复制到str1指的字符串中。
    注意:src1和str2所指内存区域不可以重叠且str1必须有足够的空间来容纳str2的字符串。

    strlen
    

    计算不包括''字符串的长度

    1.3 指针做函数返回值

    (1) 格式,如何定义,如何使用
    指针做函数返回值时:应该返回的是地址

    #include<stdio.h>
    int* func() 
    {
        int n = 100;
        return &n;//返回指针,指针指向地址
    }
    int main() 
    {
        int* p = func();//调用函数
        printf("value = %d
    ", *p);//输出指针指向地址的内容
        return 0;
    }
    

    1.4 动态内存分配

    (1) 为什么?
    ①避免造成内存浪费,基本上都是有多少用多少。
    能够随时增加,减少。

    (2) 堆区和栈区区别?
    ①栈区:存放函数的参数值、局部变量等,由编译器自动分配和释放,通常在函数执行完后就释放了。
    ②堆区:就是通过new、malloc、realloc分配的内存块,编译器不会负责它们的释放工作,需要用程序区释放。

    (3) 动态内存分配相关函数及用法
    malloc函数
    原型:void *malloc(unsigned int size)
    返回一个通用指针,可以用强制转换的方法将返回的指针值转换为所需的类型
    free函数
    原型:void free(void *pi);
    形参指向分配的内存地址

    malloc与free函数的用法如下:

    #include <stdio.h>
    #include <stdlib.h>
    int main() 
    {
    	int* p = NULL;
    	p = (int*)malloc(sizeof(int));//先申请内存
    	*p = 8;   //使用内存
    	printf("%d
    ", *p);
    	free(p);//释放之前申请的内存
    	system("pause");//任意键继续
    	return 0;
    }
    
    • 注意点:
      1.如果内存开辟成功,就返回一个指向开辟好空间的指针
      2.如果内开辟失败,就会返回一个空指针

    calloc函数
    原型:void *calloc(unsigned int num,unsigned int size);
    第一个参数表示申请空间的数量第二个参数表示每个空间的字节数

    #include<stdio.h>
    #include<stdlib.h>
    int main()
    {
    	int* p = calloc(10, sizeof(int));
    	if (p != NULL)
    	{
    		//说明空间开辟成功
    		//使用动态开辟的空间
    	}
    	free(p);
    	p = NULL;
    	system("pause");
    	return 0;
    }
    
    • calloc函数申请内存,与malloc函数的区别只在于calloc函数会在返回地址之前把申请的空间的每个字节初始化为全0

    1.5 指针数组及其应用

    基础知识点

    1. 指针数组区别
      数组名只是一个地址,而指针是一个左值
    2. 指针数组数组指针
      ①指针数组数组数组指针指针
      ②int *p1[5];指针数组 **
      int (*p2)[5];
      数组指针**

    多个字符串二维数组表示
    二维字符数组一旦定义,那么每个字符串的最大长度、首地址不能改变了。

    char str[5][5]={"I","love","eat","apple","!"};
    

    多个字符串指针数组表示
    字符指针数组是存放字符指针数组。由于它仅用来存放指针,所以它指向的每个字符串的首地址可以改变,字符串最大长度也可以改变。

    char* str[5];
    str[0]="I";
    str[1]="love";
    str[2]="eat";
    str[3]="apple";
    str[4]="!";
    

    二维数组指针数组区别?

    • 指针数组表示:字符串长度无限定
    • 定义二维数组时,就已经分配给二维数组空间,但是定义一个指针数组,指针数组里面的指针不会自动初始化,此时的指针仍然是野指针,这时不能直接对其赋值

    1.6 二级指针

    (1) 概念
    如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针
    (2) 如何使用?
    题目:假设有一个 int 类型的变量 a,p1是指向 a 的指针变量,p2 又是指向 p1 的指针变量
    ①关系图

    ②代码定义:

    int a =100;  
    int *p1 = &a;  //一级指针,指向a的地址
    int **p2 = &p1;//二级指针,指向p1的地址
    
    printf("%d %d %d",a,*p1,**p2);//打印了三个100
    

    1.7 行指针、列指针

    (1) 行指针
    常用的二维数组,如:

    int a[3][5];
    

    行指针是指向数组的指针,即上面几种指针类型中的 int (*a)[5];所以,当二维数组要被当做参数进行传递时,可以这样声明

    void funcByRowPtr(int p[][5],const int row);
    
    写法 解释 指针类型
    a+0或&a[0] 指向第1个一维数组的地址(指向第1行) 行指针
    a+1或&a[1] 指向第2个一维数组的地址(指向第2行) 行指针
    a+2或&a[2] 指向第3个一维数组的地址(指向第3行) 行指针

    (2) 列指针
    对于一个二维数组:

    int a[3][5];
    

    如果用列指针进行函数传参,可以直接声明如下:

    void funcByColPtr(int * const colPtr,const int row,const int col);
    
    写法 解释 指针类型
    a[0]+0或&a[0][0] 指向第1行第1列的地址 列指针
    a[0]+1或&a[0][1] 指向第1行第2列的地址 列指针
    a[1]+0或&a[1][0] 指向第2行第1列的地址 列指针
    a[1]+1或&a[1][1] 指向第2行第2列的地址 列指针

    2.PTA实验作业

    2.1 删除字符串中的子串 (20分)

    基本思路:

    • 首先把原字符串存入s1中,再把字串存入s2
    • 查找有无字串,并且保存子串位置
    • 输出没有子串的部分

    2.1.1 伪代码

    	int i;
    	char s1[81];//原字符串
    	char s2[81];//字串
    	char s3[81];//中间产物
    	char* p;//结束标志
    
    	/*输入原字符串*/
    	for (开头直到回车)
    	{
    		给数组s1赋值;
    	}
    	s1[i] = '';//不要烫
    
    	/*输入子串*/
    	for (开头直到回车+)
    	{
    		给数组s2赋值;
    	}
    	s2[i] = '';//不要烫
    
    	while (原字符串中存在子串时)  //strstr(s1,s2) 函数用于判断字符串s2是否是s1的子串。
    	{       //p保存字串的位置     //如果存在,则该函数返回s2在s1中首次出现的地址;
                    
    		*p = '';  //切断子串                
    		跳过需要删除的字符串减剩下的拷贝到s3里面去 
    		strcat函数字符串的连接函数将s1和s3连接在一起
    	}
    
    	打印s1数组
    	return 0;
    

    2.1.2 代码截图

    2.1.3 找一份同学代码(尽量找思路和自己差距较大同学代码)比较,说明各自代码特点。



    代码特点:

    • me
      (1) 查找子串,我直接用了strstr函数查找是否有子串,并返回子串位置
      (2) 输入子串原字符串的时候,我是用for循环输入
      (3)找到子串,是把原字符串的子串位置用''切开,只留下无子串的部分
    • 进源
      (1) 查找子串,进源是用了while语句内嵌一个for循环去判断查找子串
      (2) 输入子串原字符串的时候,进源是直接调用fgets函数
      (3) 找到子串,他是用for循环左移

    2.2 合并2个有序数组

    基本思路:

    • 首先把两行内容分别存入a[]数组b[]数组
    • 比较a,b数组中最大元素的大小,即最末尾元素**
    • 如果a[]数组末尾的元素比b[]数组的大,那么把a[]数组末尾元素放到该去的地方
    • 如果b[]数组末尾的元素比a[]数组的大,那么把b[]数组末尾元素放到该去的地方
    • 如此反复,即可得到一个合并后的排好序的数组

    2.2.1 伪代码

        int i = m - 1;//a数组长度
        int j = n - 1;//b数组长度
        int k = n + m - 1;//最终数组长度
        while (a数组没排完 且 b数组也没排完)
        {
            比较两数组最末尾元素的大小,比较后把大的元素放好位置
        }
        while (a数组排完了 而 b数组还没排完)
        {  
            单独处理b数组,把b数组的元素全部放过去
        }
    

    2.2.2 代码截图

    2.2.3 找一份同学代码(尽量找思路和自己差距较大同学代码)比较,说明各自代码特点。


    代码特点:

    • me
      (1) 我是从后面开始判断,排到a数组末尾去,
      (2) 可能b数组多出来,要单独处理,而a数组多出来没关系,因为本来就是要以a数组作为输出结果,不动即可。

    • 学长
      (1) 学长是从前面开始判断,排到一个新数组中
      (2) 所以a数组多出来元素b数组多出来元素需要处理,把剩余元素排到新数组中
      (3) 最后再把新数组中的元素全部复制给a数组

    2.3 说反话-加强版

    基本思路:

    • 首先把一行内容存入a[]数组中,保存 "I love apple"
    • 从后往前遍历整个字符串
    • 如果不是空格进入,把字母保存在s[]数组中
    • 如果是空格输出刚刚保存的内容,分别输出 "apple","love","I"
    • kong用来控制是否输出空格

    2.3.1 伪代码

    int a[100];//保存 "I love apple"
    int s[100];//输出 "apple","love","I"
    for (开头到回车)
    	把内容保存到a数组中
    for (从开头遍历)
    	计算开头的空格数
    //倒着遍历
    for (从尾部到开头) 
    {
    	if (不是空格)//不是空格,必能输出字符串
    	{
    		while (不是空格)
    			保存单词
    		for (逆序输出s数组,即一个单词)
    			输出单词
    		if (循环还未到最后一个单词)
    		{
    			判断空格是否输出
    		}
    	}
    	else  //是空格
    		就不管
    }
    

    2.3.2 代码截图


    2.3.3 请说明和超星视频做法区别,各自优缺点。

    • 区别:
      我用puts(s1)输出结果,超星用printf(" %.*s", len, p)

    • 优点:
      我:puts函数strstr函数好理解,也节省空间
      超星:巧妙利用指针进行逆向遍历

  • 相关阅读:
    《2048》开发5——实现计分功能
    《2048》开发4——继续编辑GameView类,实现游戏逻辑
    《2048》开发3——编辑Card类
    robotframework(rf)中对时间操作的datetime库常用关键字
    弹框和单选框,复选框
    Selenium IDE安装与使用
    全面的功能测试点总结
    RF新手常见问题总结--(基础篇)
    常用断言关键字(rf中)
    jmeter录制(ios)app脚本
  • 原文地址:https://www.cnblogs.com/qq690775749/p/14198660.html
Copyright © 2020-2023  润新知