C语言集训-温宇骏-第五次作业

169 阅读7分钟

指针

1.指针用法的初步介绍

指针的简单使用

#include<stdio.h>
int main(void)
{
    int* p;//p是变量的名字 int* 表示p变量存放的是int类型变量的地址
    int i=3;
    p=&i;
    /* 
    1.p保存了i的地址,因此p指向i
    2.p不是i,i也不是p,修改p的值不影响i 修改i的值不影响p
    *指针变量 就完全等同于 普通变量 
    例子:
    如果p是一个指针变量 并且p存放了普通变量i的地址
    则p指向了普通变量i
    *p 就完全等同于i
    亦或者:
    在所有出现*p的地方都可以替换为i
    在所有出现i的地方都可以替换为*p

}

p是用来存放要读取数据的地址。*p是让编译器从指定的地址中读取出数据。p输出一个指针的地址,通常是输出一个16进制的数。*p常用在一个和指针类型一致的变量或者常量。

p是一个指针变量的名字,表示此指针变量指向的内存地址 *p表示此指针指向的内存地址中存放的内容

  1. 指针就是地址,地址就是指针
  2. 地址是内存单元的编号
  3. 指针变量是存放地址的变量
  4. 指针指针变量是两个不同的概念

指针的重要性

  • 表示一复杂的数据结构
  • 快速的传递数据
  • 使函数返回一个以上的值
  • 能直接访问硬件
  • 能方便的处理字符串

2.指针的定义

地址

  • 内存单元的编号
  • 从零开始的非负整数

指针

  • 指针的本质就是一个操作受限的非负整数(只能进行相减)
  • 指针就是地址,地址就是指针

星号的三种含义

  • 乘法
  • 定义指针变量

​ int * p *定义了一个名字叫p的变量,int *表示p只能存放 int 变量的地址

  • 指针运算符

    ​ 改运算符放在已经定义好的变量的前面

    ​ 如果p是一个已经定义好的指针变量 则*p表示以p的内容为地址的变量

3.基本类型指针

  • int *p;p是指针变量 p=&i;p保存i的地址 所以p指向i

  • p指向i 所以*p就是i

  • *p=i是错误写法 要先让p指向 即取p的地址 p=&i

  • 没有指向的指针变量是垃圾值 如果没有初始化 则没有使用权限

#include<stdio.h>
int main(void)
{
 int*q;
 int*p;
 int i=5;
 p=&i;//p取i的地址
 q=p;//q取p的地址
 printf("%d\n",*q);//输出*q 就是输出 i=5
 return 0;
}

image.png

经典指针程序 互换两个数字

#include<stdio.h>
void huhuan(int *q,int *p)
{
    int t;//如果要互换*q *p的值 那t必须定义成int 不能定义成int *
    t=*q;//p是int *   *p是int
    *q=*p;
    *p=t;
}
int main(void)
{
    int a=3;
    int b=5;
    huhuan(&a,&b);
    printf("a=%d b=%d\n",a,b);
    return 0;
}

image.png

huhuan(&a,&b);中不能写成(* a,* b)也不能写成(a,b)

被调函数不能改变主函数的值 如果想改变主函数的值 必须接入主函数的变量地址

p是变量名,&a发送的地址是给p,使得*p为a的值

输入多组数据 使两个数值互换

#include<stdio.h>
void huhuan(int *q,int *p)
{
    int t;//如果要互换*q *p的值 那t必须定义成int 不能定义成int *
    t=*q;//p是int *   *p是int
    *q=*p;
    *p=t;
}
int main(void)
{
    int a,b;
    while(~scanf("%d%d",&a,&b))
    {
    huhuan(&a,&b);
    printf("a=%d b=%d\n",a,b);
    }
    return 0;
}

image.png

如何通过被调函数修改主调函数普通变量的值

  1. 实参(主调函数)必须为该普通变量的地址(&a,&b)
  2. 形参(被调函数)必须为指针变量 (int * p) p为形参
  3. 在被调函数中通过

​ *形参名 =...... 的方式修改主调函数的相关变量的值

4.指针和数组

指针和一维数组

1.一维数组名

一维数组名是个指针常量

它存放的是一维数组的第一个元素的地址

2.下标和指针的关系

如果p是个指针变量 则

p[i]永远等价于*(p+i) p是int * 类型变量

3.一维数组需要两个参数的原因

#include<stdio.h>
void f(int *p,int len)
{
  int i;
  for(i=0;i<len;++i)
  printf("%d ",*(p+i));
  printf("\n");
}
int main(void)
{
    int a[5]={1,2,3,4,5};
    int b[6]={-1,-2,-3,4,5,-6};
    int c[100]={1,99,22,33};
    f(a,5);
    f(b,6);
    f(c,100);
    return 0;
}

image.png

int *p 定义指针变量p,a为数组名 是指针变量。int len 表示数组的长度

*(p+i)等价于p[i] 等价于b[i]也等价于*(b+i)

4.指针变量的运算

  • 指针变量不能相加 相乘 相除
  • 如果两个指针变量指向的是同一块连续空间中的不同存储单元,则这两个指针变量才可以相减
#include<stdio.h>
int main(void)
{
    int *p;
    int *q;
    int a[5];
    p=&a[1];
    q=&a[4];
    printf("p和q所指向的单元相隔%d个单元\n",q-p);
    return 0;
}

image.png

5.指针变量所占字节

sizeof(数据类型) 功能:返回值为该数据类型所占的字节数

  • 假设p指向char类型变量(一个字节)
  • 假设q指向int类型变量(四个字节)
  • 假设r指向double类型变量(八个字节)
  • p q r 本身所占的字节数一样

一个指针变量,无论它指向的变量占几个字节 该指针变量本身只占四个字节

动态内存分配

1.传统数组的缺点

  • 数组长度必须实现事先制定,且只能是常整数,不能是变量
  • 系统为该数组分配的存储空间会一直存在,直到该函数运行完毕时,数组的空间才会被系统释放
  • 数组的长度不能在函数运行的过程中动态的扩充或缩小
  • A函数定义的数组,在A函数运行期间可以被其他函数使用,但A函数运行完毕后,A函数中的数组将无法在被其他函数使用
  • 传统方式定义的数组不能跨函数使用

2.动态内存分配举例——动态数组的构造,malloc函数的运用

  • 要使用malloc函数 必须调价malloc.h文件
  • malloc函数只有一个形参 并且形参是整型
  • malloc(4) 4 表示请求系统为本程序分配4个字节
  • malloc函数只能返回第一个字节的地址
  • p本身所占的内存是静态分配的 p所指向的内存是动态分配的
#include<stdio.h>
#include<malloc.h>//使用malloc函数
void f(int *q)
{
    *q=200;
    
}
int main(void)
{
  int*p=(int *)malloc(sizeof(int));//sizof(int)返回值是int所占的字节数
  *p=10;
  printf("%d\n",*p);
  f(p);//p是int *类型
  printf("%d\n",*p);
  return 0;
}

image.png

如果void f()函数中加 free (q) 则输出的第二个*p为垃圾值

3.动态一维数组的构造

#include<stdio.h>
#include<malloc.h>//使用malloc函数
int main(void)
{
  int a[5];//int 占4个字节 本数组总共包含有20个字节
  int len;
  int *p;
  int i;
  //动态的构造一维数组
  printf("请输入你要存放的元素的个数:");
  scanf("%d",&len);
  p=(int*)malloc(4*len);
  //对一维数组进行操作
  for(i=0;i<len;++i)
  scanf("%d",&p[i]);
  printf("一维数组的内容是:");
  for(i=0;i<len;++i)
  printf("%d\n",p[i]);

  return 0;
}

image.png

p[i]表示的是*(p+i) 理解为地址的值 scanf函数需要取& 取地址符

若printf函数中加&取地址符 则输出结果是该地址 而不是该值

多级指针

  • 若p是int *类型 则&p是int ** 类型

    #include<stdio.h>
    int main(void)
    {
      int i=10;
      int *p=&i;
      int **q=&p;
      int ***r=&q;
      printf("%d",***r);
      return 0;
    }
    

( * * r= * q=p=i=10)*

跨函数使用内存

静态变量不能跨函数使用

#include<stdio.h>
void f(int **q)//q是指针变量,无论q是什么类型的指针变量 都占四个字节
{
  int i=5;
  *q=&i;
}
int main(void)
{
  int *p;
  f(&p);
  printf("%d\n",*p);
  return 0;

}

image.png

执行完一次后,i被销毁 可p变量依然指向i

动态内存跨函数使用

#include<stdio.h>
#include<malloc.h>
void f(int **q)//q是指针变量,无论q是什么类型的指针变量 都占四个字节
{
  *q=(int*)malloc(sizeof(int));
  **q=5;//q存放p的地址 *q是p **q是*q

}
int main(void)
{
  int *p;
  f(&p);
 printf("%d\n",*p);
  return 0;

}

image.png


课后习题

10.12

T1

8 8 4 4 0 0 2 2

T2

ref 有四个元素

T3

ref的地址是指针变量ptr所指的地址

ref+1 表示ref[1]的地址

++ref 无意义 指针变量不能相加

T4

a. * ptr的值是12 ,*(ptr+2)的值是16

b. * ptr的值是12, * (ptr + 2)的值14

T5

a. * ptr的值是12 ,*(ptr+1)的值是16

b. * ptr的值是12, * (ptr + 1)的值14