zoukankan      html  css  js  c++  java
  • C博客作业05-指针

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

    0.展示PTA总分

    1.本章学习总结

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

    ※为何使用指针

    通常,管理大量数据的有效方法不是直接处理数据本身,而是使用指向数据的指针。例如,如果需要对大量的大型记录进行排序,对指向该记录的指针列表进行排序效率要比直接对记录排序高得多,因为这样无需在内存中移动数据。

    1.指针的定义

    • 假设变量p位于2000单元,该单元中存放变量x的地址1000,若取出变量p的值1000就可以访问内存1000单元,实现对变量x的操作,也就是通过p间接访问变量x。这种专门用来存放变量地址的变量是指针。

    • 一般形式:类型名 *指针变量名

    • 当指针声明符*在定义指针变量时被使用,说明被定义的变量是指针。

    • 星号不是运算符,故想要对指针做处理时,前面不需要带*

    • 定义多个指针变量时,每个指针变量前都必须加上*。

    2.指针相关运算

    ①取地址运算和间接访问运算

    int* p,a=3;
    p=&a;
    

    将整型变量a的地址赋给整型指针p,使指针p指向变量a。也就是说用取地址符&取变量a的地址,并将该地址值作为p的值,使p指向a。
    指针的类型和它所指的类型需要相同。

    ②赋值运算

    int a=3,*p1,*p2;
    p1=&a;
    p2=p1;
    

    将a的地址给p1,再将p1的值给p2,所以指针p1与p2都指向a,即p1=p2=a。

    ③指针间的加减运算
    一个指针加上一个整数代表着,访问在当前指针所指的位置挪动整数个位置后的数。
    星号与自增自减优先级相同,但因为结合方向是从右到左,所以需要注意*p++(*p)++的区别。前者是指针移动后的元值,后者是当前指针所指元素增一后的结果。

    ④赋值
    指针变量要先赋值再使用。为了避免引用为赋值的指针所造成的危害,在定义指针时,可先将它的初值置为空,不能用数值作为指针变量的初值。
    对指针p进行赋值p=0;和p=NULL都表示指针p为空指针,空指针不指向任何一个单元

    3.指针做函数参数

        void Swap1(int *p, int *q)
        {
            int buf;
            buf = *p;
            *p = *q;
            *q = buf;
            return;
        }
        void Swap2(int a, int b)
        {
            int buf;
            buf = a;
            a = b;
            b = buf;
            return;
        }
    

    函数swap2不能进行数值的交换,因为当返回主调函数后,函数中的定义全部消亡,并没有将交换后的值传回实参。
    而函数swap1中p指向a,q指向b,在改变*p的值后,就改变了该存储单元的内容,因此在函数中调换*p*q的数值,主调函数的a、b也相应发生了变化。

    1.2 字符指针

    ①字符指针:指向字符型数据的指针变量。每个字符串在内存中都占用一段连续的存储空间,并有唯一确定的首地址。即将字符串的首地址赋值给字符指针,可让字符指针指向一个字符串

    char *ptr = "Hello";//将保存在常量存储区的"Hello"的首地址赋值给ptr
    与
    char *ptr;
    ptr = "Hello";//是等价的,注意不能理解为将字符串赋值给ptr
    

    char str[10] = "Hello";
    char *ptr = str;//数组名代表数组的首地址
    /*等价于*/
     char *ptr;
     ptr = str;//等价于ptr = &str[0];将数组的首地址赋给字符指针ptr
    

    ④对于数组名str,不能使用str++操作使其指向字符串中的某个字符,因为数组名是一个地址常量,其值是不能被改变的。

    *(ptr+i):字符串中第i+1个字符,相当于*(str+i),即str[i]
    也可以用ptr++,移动指针ptr,使ptr指向字符中的某个字符
    

    ⑤字符串的长度(指字符串所含的字符个数,但不包括最后的’’)与字符数组的大小(指字符串所包含的字符个数加上’’,故+1)不一样。

    for(i = 0;str[i] != ''; i++ )
    {
         printf("%c",str[i]);//常用借助字符串结束标志''识别字符串的结束
    }
    scanf("%s",str);//表示读入一个字符串,直到遇到空白字符(空格、回车符、制表符)为止,如果输入带有空格的字符串,只会读到空格以前而空格以后不会读入
    printf("%s",str);//表示输出一个字符串,直到遇到字符串结束标志''为止(注意这里可以带有空格输出)
    

    ※常用的字符串处理函数

    1.3 指针做函数返回值

    函数的返回值是一个指针(地址),这样的函数称为指针函数。
    用指针作为函数返回值时需要注意的一点是,函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回的指针尽量不要指向这些数据,不然它们在后续使用过程中可能会引发运行时错误。
    指针做函数返回值有一个好处,就是它可以返回多个值到主调函数当中去。

    1.4 动态内存分配

    一般情况下,运行中的很多存储要求在写程序时无法确定,因此需要在运行的实际需求中分配合适的存储区。

    • 1.从栈区(stack)上分配
      • 在执行函数调用时,系统在栈上为函数内的局部变量及形参分配内存,函数执行结束时自动释放这些内存。
    • 2.从堆区(heap)上分配
      • 在程序运行期间,用动态内存分配函数来申请的内存都是从堆上分配的,动态内存的生存期由程序员决定。
    /*动态存储分配函数*/
    void *malloc(unsigned size)//动态存储分配函数
    //功能:在内存的动态存储区中分配一连续空间,其长度为size。申请成功,则返回指向所分配内存空间的起始地址指针;失败,则返回NULL。
    void *calloc(unsigned n,unsigned size)//计数动态储存分配函数
    //功能:在内存的动态存储区中分配n个连续空间,每一个存储空间的长度为size,分配后把存储块全部初始化为0。申请成功,则返回指向所分配内存空间的起始地址指针;失败,则返回NULL。
    void free(void *ptr)//动态存储释放函数
    //功能:释放由动态存储分配函数申请到的整块内存空间。
    void *realloc(void *ptr.unsigned size)//分配调整函数
    //功能:更改以前的存储分配。
    
    /*动态分配n个整型大小的空间*/
    if((p=(int *)malloc(n*sizeof(int)))==NULL)
    {
        printf("NOT able to allocate memory
    ");
        exix(1);
    }
    

    1.5 指针数组及其应用

    • 1.定义

      • 一般形式类型名 *数组名[数组长度];

      • 指针数组中的每个元素都是指针类型,用于存放内存地址。

    • 2 多个字符串用二维数组表示和用指针数组表示区别

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

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

    • 举例:

    char str[5][5]={"abc","abcd","aaaa","ad","k"};
    str[0]到str[4]五个字符串的最大长度被限为(5-1)=4,注意处理字符串时不应溢出。由于每个字符串的地址已经确定,所以str[0]="news";是不允许的
    而
    char* str[5];
    str[0]="Welcome!";
    可以
    

    1.6 二级指针

    二级指针的概念

    一般形式:类型名 **变量名;

    若定义整型变量a=10,*p=&a,**pp=&p,则*p==**pp==a以此类推三级、四级……

    1.7 行指针、列指针

    使用行指针或者列指针可以指定到字符串中的某个字符。

    行指针:一般形式:类型名 (*变量名)[数组长度]

    列指针:一般形式:类型名 [数组长度](*变量名)

    使用例:

    char color[5][10] = { "red","blue","yellow","green","black" };
    char (*pc)[10];
    pc = color;
    printf("%s
    ", *(pc + 1));
    printf("%c
    ",*(*(pc+1)+2));
    

    结果:

    blue
    u
    

    2.PTA实验作业

    2.1 删除字符串中的子串(https://pintia.cn/problem-sets/1333954783207010304/problems/1333954910390890510)

    2.1.1 伪代码

    定义字符数组s[1000]、t[1000]、q[1000]
    定义字符变量x
    
    输入字符串s
    输入字符串t
    
    定义整形变量lens=s的字符串长度
    定义整形变量lent=t的字符串长度
    定义字符指针p
    
    while(p=strstr(s,t))//找到子串首字符在字符串中的地址
        do while(*(p+lent)!='')//字符前移
    	do 令字符前移至遍历自子串首字符位置之后的字符串到结束符
        end while
        删除已前移的多余字符
    end while
    输出字符串s
    

    2.1.2 代码截图

    2.1.3同学代码

    在函数的调用上,该同学利用了strcat函数,直接完成了在删除子串后的后续字符串的前移,十分的简便,运行耗时也很少。

    反观我的代码,我利用的是每次单个字符的前移,这大大的增加了计算机的运算量,而且代码可读性不高(我看自己的是这样的)

    2.2 合并2个有序数组(https://pintia.cn/problem-sets/1333954783207010304/problems/1333954910390890504)

    2.2.1 伪代码

    merge(指针a, 整型变量m, 指针b, 整型变量n)/*用于归纳并排序的函数*/
    定义整型变量i, j, k, temp均为0
    定义整型指针p = a
    while i<m&&j<n
    	if *a>=*b
    	        then a[k]=*b
    		     b++
    		     j++
    		     k++
    	        else a[k]=*p
    		     p++
    		     i++
    		     k++
    	end if
    end while
    if i<m
    	while k<=m+n-1
    		do a[k]=*p
    		   p++
    		   k++
    	end while
    end if
    if j<n
    	while k<=n+m-1
    		then a[k]=*b
    		     b++
    		     k++
    	end while
    end if
    for i=1 to m+n-1
        do for j=i+1 to m+n-1
    		do if a[i]>a[j]
    			   then temp=a[i]
    			        a[i]=a[j]
    			        a[j]=temp
    		   end if
    	   end for
    end for
    printArray(指针arr, 整型变量arr_size)/*用于输出的函数*/
    定义整型变量i
    for i=0 to arr_size-1
        if i=0
    	  then 输出arr[i](不带空格)
    	  else 输出 arr[i](带空格)
        end if
    end for
    
    

    2.2.2 代码截图

    2.2.3同学代码

    同学的代码利用另一个指针来存储两个数组,在没有归到一起之前就进行了排序,并且利用了动态内存分配,防止溢出。但二级指针在这里定义之后没有任何意义。

    我则是先将两个数组放到一起再进行排序,而且整合的过程十分复杂,导致变得十分繁琐。(脑死状态下写的果然都没法看)

    更改后

    2.3 说反话-加强版(https://pintia.cn/problem-sets/1333954783207010304/problems/1333954910390890509)

    2.3.1 伪代码

    定义字符数组a
    定义指针p、q分别用来存储字符串a的首地址和尾地址
    定义整型变量flag用来判断输出条件
    输入字符串a
    令p的指针指向a的首字符
    while(*p!='
    '&&*p!='')
        do p++
    	end while
    令q的指针指向a字符串尾字母
    while(q!=a)
        do if (*q==' ')
    		then if (* (q + 1) != ' ' && *(q + 1) != '')
    		      then if (flag==1)
    		            then 输出q+1后的字符串
    		                 令flag=0
    			  else 输出q后的字符串
    		      end if
    		*q=''
    		end if
    	end if
        q--
    end while
    if (*a!=' ')
        then if (flag == 1)
            then 输出q + 1后的字符串
            令flag = 0
        else 输出q后的字符串
    	end if
    end if
    

    2.3.2 代码截图

    2.3.3和超星视频做法区别

    超星视频里的做法用了一种没见过的输出方式,即printf("%.*s,len,p"),可以从某个地址开始子串,但具体用法还是不是很熟悉。

    视频中考虑了空格的输出,而我的方式则不用再在输出中加空格,可以直接利用句子中的空格。除了这里不大相同以外其他地方基本相同。

    我借鉴了视频中的逆向输出的方法,代码量减少了很多,这是我需要学习的地方。

    ※知识点
    逆向扫描字符:while(p!=beginPtr) {p--}
    字符串指针表示字符串:printf("%.s",len,str);
    利用不同的指针指向所需的地址效率更高。

  • 相关阅读:
    CF991D Bishwock
    CF1010D Mars rover
    NOIP 2011 计算系数
    SDOI 2008 仪仗队
    浅谈欧拉函数
    CF1249F Maximum Weight Subset
    NOIP 2011 铺地毯
    CF707D Persistent Bookcase
    C++ STL bitset 容器详解
    CF798D Mike and distribution
  • 原文地址:https://www.cnblogs.com/Rex7478/p/14197312.html
Copyright © 2011-2022 走看看