数据结构-指针和结构体

2023-05-16

1 指针

首先看变量在内存中的存储

image-20220403105351475

有时候需要获取并使用程序运行中某个变量的内存地址,如何获取这个地址、如何存储这个地址?

&取地址符,&a就是获得了a的地址

image-20220403105325699

可以存储地址的变量称为指针变量

指针看起来就是地址,但不仅仅是地址信息,还包含类型信息

2 指针变量的声明和初始化

声明指针变量

image-20220403110132208

int  *pn;    //pn 是整型指针,指向整型变量

指针变量名是pn,不是*pn   * 是指针声明符

float *pa;   //pa 是浮点型指针,指向浮点型变量
char *pc;    //pc 是字符型指针,指向字符型变量

声明并初始化指针变量

int  i, *p = &i;

等价于

int  i;
int  *p = &i;

等价于

int  i, *p;
p = &i;

下面看指针和变量在内存中的形式

image-20220403105820456

最好在声明指针时就对它进行初始化。一个指向不确定的指针是很危险的,因为通过指针可以访问其指向的内存区域,会造成意外覆盖内存

如果声明指针变量时不能确定它的指向,可以初始化为NULL

int  *p = NULL; 

值为NULL的指针称为空指针,这意味着,指针并不指向任何地址。在头文件 stdio.h 中,NULL 定义为常量0。

3 指针与数据类型

前面关于指针的讨论忽略了一个重要事实,即不同类型的数据在内存中占用的字节数量是不同的。 而指针仅指示了第一个字节的地址。如何通过指针访问该数据:读取多少个字节? 如何理解这些字节内容?

所以编译器必须知道它所指向的数据类型,每个指针都和某个数据类型相关联。

int  *p;          //p指向一个int类型的对象 ,读4个字节
double *p;    //p指向double类型的对象,读8个字节

(1)指针变量也是一个变量,只不过该变量中存储的是另一个对象的内存地址

(2)如果一个变量存储另一个对象的地址,则称该变量指向这个对象

(3)由于指针值是数据,指针变量可以赋值,所以一个指针的指向在程序执行中可以改变。指针p 在执行中某时刻指向变量a,在另一时刻也可以指向变量b

 int a;
 int *p = &a; // p中存的是a的地址
image-20220403111548331

4 间接访问运算符

image-20220403111807593

直接访问n:通过变量名访问,如 n = 15;

间接访问n:通过另一个变量来访问,pn中存储了n的地址,通过pn可以间接访问n,*pn就是对n的引用

   int  a = 3, *p = NULL; 	   
   printf("a的地址:%p\n", &a);    //用格式符%p以十六进制格式输出内存地址。
   printf("a的值:%d\n", a);

   p = &a;  //让指针变量p指向a  
   printf("\np的地址:%p\n", &p);   //变量p也需系统分配内存地址,也有内存地址 
   printf("p的值:%p\n", p);
   printf("*p的值:%d\n", *p);

   *p=10;   //等价于a=10;
   printf("\na=%d, *p=%d\n", a, *p);   //指针变量p指向a, *p是通过a的地址间接访问a
image-20220403112411917
#include<stdio.h>
int main()
{
    int a = 1, x, y, *p;
    p = &a;
    x = (*p)++;   //即x=a++; 先使用再增1,即x=a; a++;
    y=*p;
    printf("x=%d,y=%d\n",x,y);  //x=1,y=2
}

x = (* p)++;
++运算的对象是* p,而不是p
++后置,先使用再增值,
等价于:
x=* p; (*p)++;
将 p 所指向的变量值加1

5 指针作为函数的参数

有了指针的概念,重新思考以下问题:如何在被调函数中改变主调函数的局部变量?

C语言中,参数传递是“传值”方式,形参是实参的副本,因而在被调函数中,对形参的修改不影响实参变量的值。

为达到改变变量的目的,需用指针变量作为函数的形参,将实参变量的指针传给形参。此时,在函数执行过程中,通过形参指针,间接访问被调函数中的变量。

//以下程序不能实现a、b值的互换
void swap(int x, int y)
{
	int t;
	t = x;	x = y;	y = t;
}
int main()
{
	int a = 3, b = 5;
	swap( a, b );
	printf("%d %d\n",a,b);     // 3 ,5 
    return 0;
}
image-20220403113659384

形参变量值的改变不会影响实参可以打个比方,main用传真方式把a和b的内容传给了swap,swap接到传真,打印到x和y上,x和y是a和b的副本,x和y的改变不会影响a和b的内容。

试试看:用指针解决问题

void swap( int *pa, int *pb)
{
    int t;
    t = *pa; *pa = *pb;  *pb=t;  //*pa是对a的引用,*pb是对b的引用
}
int main()
{
	int a = 3, b = 5;
	swap(&a, &b);      // 传递a,b的地址
	printf("%d %d\n",a,b);    // 5 3
    return 0;
}

image-20220403114101264

通过形参指针变量pa和pb间接访问并改变了a和b的值打个比方,是main给swap打电话,告诉他a和b这两个原件就在档案柜的第315(存入pa)、316号抽屉(存入pb) ,swap直接去315和316号抽屉,直接访问和修改的是原件a和b

试试看:为何不能实现互换

#include<stdio.h>
void swap( int *pa, int *pb)
{
    int *t;
    t = pa;  pa = pb; pb = t;
}
int main()
{
    int a = 3, b = 5;
    swap(&a, &b);
    printf("%d %d\n", a, b);   // 3 5
    return 0;
}

image-20220403114311427

函数调用结束时,pa,pb被释放, 形参指针变量值的改变不会影响实参

如何访问主调函数中的变量,要通过函数调用来改变主调函数中某个变量的值:
(1) 在主调函数中,将该变量的地址或者指向该变量的指针作为实参
(2) 在被调函数中,用指针类型形参接受该变量的指针
(3) 在被调函数中,用形参指针间接访问主调函数中的变量。

6 结构类型的定义方法

struct 结构体名
{
    数据类型  成员1;
    数据类型  成员2;.
    数据类型  成员n名;
};   //;不要漏掉
image-20220403114632685

结构类型变量的定义

常用两种方法:
1 先定义结构体模板,再定义变量名

struct student
{
    long  stuID;
    char  stuName[10];
    char  stuSex;
    char  birthYear;
    int     mathScore;
}struct student stu1;       // stu1是变量

2 在定义类型的同时定义变量

struct student
{
    long  stuID;
    char  stuName[10];
    char  stuSex;
    char  birthYear;
    int     mathScore;
}stu1;

7 嵌套的结构类型

在一个结构体内其成员可以是另一个结构体

image-20220403114946984

struct date
{
    int  year;
    int  month;
    int  day;
};
struct student
{
    long  studentID;
    char  stuName[10];
    char  stuSex;
    struct date  birthday;
    int     mathScore;
};

struct student stu1;

注意:不可以嵌套同类型的结构体

8 使用typedef定义数据类型别名

可以这样写

struct student
{
    long  stuID;
    char  stuName[10];
    char  stuSex;
    int     mathScore;  
};
typedef struct student STUDENT;

也可以

typedef struct student
{
    long  stuID;
    char  stuName[10];
    char  stuSex;
    int     mathScore;  
} STUDENT;

定义后别名和原名通用

struct student stu1;
STUDENT stu2;

9 结构变量的初始化

不嵌套情况

STUDENT  stu2={100010,"King哥",'M',95};
struct student stu1={100010,"King哥",'M',  95};

嵌套情形

STUDENT stu1={100010,"King哥",'M', {2001,7,9}, 95};

10 结构变量成员的引用

typedef struct date
{
    int  year;
    int  month;
    int  day;
}DATE;
struct student
{
    long  stuID;
    char  stuName[10];
    char  stuSex;
    DATE  birthday;
    int     mathScore;
} stu1;

圆点运算符
结构体变量名.成员名

stu1.stuID = 100012;

嵌套情形
以级联方式逐级访问

stu1.birthday.year = 1990; 

11 结构数组

结构数组定义

元素为结构类型的数组称为结构数组。
在实际应用中,经常用结构数组来表示具有相同数据结构的一个群体。例如一个班的学员档案,一个公司的职工档案等

struct student
{
    int num;
    char name[20];
    char sex;
    float score;
}stu[30];

定义了一个结构数组stu,共有30个元素,stu[0]~stu[29]。每个数组元素都具有struct student的结构形式

结构数组元素的成员引用
结构体数组名[下标] . 结构体成员名
使用方法与同类型的变量完全相同

	stu[5].num = 26;
	strcpy(stu[5].name, "Zhang San");
	stu[4] =stu[1];

12 结构指针

结构指针:指向结构类型变量的指针
结构类型名 *指针变量名

	struct student stu1 = { 1,"СÃ÷",'F',45};
	struct student *p;
	p = &stu1;

image-20220403121828035

12 通过结构指针访问结构成员

(1) 用* p访问结构成员

(*p).num = 36;

(2) 用指向运算符“->”访问结构成员。

p->num = 36;

当p = &stu1时,以下三条语句相同:

	p = &stu1;
	stu1.num = 36;
	(*p).num = 36;
	p->num = 36;

结构成员的多种访问方法

#include<iostream>
#include<bits/stdc++.h>
using namespace std;


struct employee
{
    char  name[20];
    int   age;
    int   tel;
};
int main( )
{
   struct  employee emp1 = {"jk", 22,  67896544};
   struct  employee  *p;
   p = &emp1; 
   printf("%s,%d,%d\n", emp1.name,  emp1.age,  emp1.tel);
   printf("%s,%d,%d\n", (*p).name,  (*p).age, (*p).tel);
   printf("%s,%d,%d\n",  p->name,  p->age,  p->tel);
   return 0;
}

image-20220403122511889

13 内存分配函数

malloc()——分配内存块,但不对内存块初始化。
calloc()——分配内存块,且对内存块清零。
realloc()——调整先前分配的内存块的大小。

函数返回void *类型的值。void *类型的指针是”通用”指针,本质上只是内存地址。(由于无法知道计划存储在内存块中的数据是什么类型,所以它们不能返回int类型、char类型等普通类型的指针。)

如果内存分配失败返回“空指针”,用宏名”NULL”表示。空指针是“不指向任何地方的指针”

1 动态存储分配函数malloc()

void *malloc(unsigned size)
在内存的动态存储区中分配一连续空间,其长度为size
若申请成功,则返回一个指向所分配内存空间的起始地址的指针
若申请内存空间不成功,则返回NULL(值为0)
返回值类型:(void *)
通用指针的一个重要用途 :将malloc的返回值转换到特定指针类型,赋给一个指针

2 动态存储释放函数free

void free(void *ptr)
释放由动态存储分配函数申请到的整块内存空间,ptr为指向要释放空间的首地址。
当某个动态分配的存储块不再用时,要及时将它释放

3 其它动态存储分配函数

void calloc(unsigned n, unsigned size)
calloc函数为n个元素的数组分配内存,其中,每个元素长度都是size个字节。(calloc与malloc类似,但是主要的区别是存储在已分配的内存空间中的值默认为0,使用malloc时,已分配的内存中可以是任意的值。)
void * realloc(void
ptr, unsigned size)
调整先前分配的内存块的大小, ptr指向原先通过malloc、calloc或realloc获得的内存块,size是内存块的新尺寸

	p = (int *) malloc (n* sizeof(int)); 
	p = (int *) calloc (n, sizeof(int)); 
	p = (int *) realloc (p, n*sizeof(int)); 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构-指针和结构体 的相关文章

随机推荐

  • JS入门笔记:获取文档对象

    DOM获取元素的方法 1 getElementById 参数 a 参数为元素的id xff0c 并且是字符串形式 b 返回的是一个元素对象 c 使用console dir 打印获取的元素可以更好的察看其相关属性和方法 2 根据标签名来获取元
  • VCC、VDD、VSS、GND区别

    一 具体分析 xff1a 1 在电子电路中 xff0c VCC是电路的供电电压 VDD是芯片的工作电压 2 在普通的电子电路中 xff0c 一般VCC gt VDD 3 在COMS器件中 xff0c VDD是CMOS的漏极引脚 xff0c
  • 立创EDA入门

    如有错误 xff0c 感谢指正 如有错误 xff0c 感谢指正 xff0c 请私信博主 xff0c 有辛苦红包 xff0c 拜 一字之师 请根据目录寻找自己需要的段落 导语 xff1a 本博客为个人整理EDA学习记录帖 xff0c 如有错误
  • DockerFile构建过程

    DockerFile构建过程 了解镜像加载原理 Docker镜像加载原理 UnionFS 联合文件系统 xff09 UnionFS 联合文件系统 xff09 Union文件系统 UnionFS 是一种分层 轻量级并且高性能的文件系统 xff
  • 杂记——1.Navicat连接远程数据库时出现的2003错误

    1 问题描述 当我们用Navicat连接自己的远程数据库时 xff0c 在IP地址与密码都输入正确的情况下 xff0c 点击测试连接时有时会出现以下情况 导致连接失败 xff0c 这就会困扰许多新手小伙伴 xff0c 为什么我的IP与密码都
  • 杂记——9.eclipse启动Tomcat

    这篇文章 xff0c 我们简单的来说一下如何用eclipse启动Tomcat 具体步骤如下所述 第一步 xff1a 打开eclipse xff1a 第二步 xff1a 点击上方的 Window 第三步 xff1a 点击Preferences
  • 杂记——12.腾讯会议使用OBS虚拟摄像头实现多屏幕共享的解决方法

    这篇文章将来讲述一下腾讯会议如何使用OBS虚拟摄像头来实现多屏幕共享 目录 1 下载地址 2 下载与安装 2 1 OBS Studio的下载与安装 2 2 OBS VirtualCam 虚拟摄像头插件的下载与安装 3 运行与操作 4 小问题
  • 一种基于OpenCV的陪护机器人

    近年来人工智能不断发展 xff0c 从工业领域扩散到多个领域 xff0c 功能逐渐变多 xff0c 从以前的工业机器人到现如今的服务类机器人 xff0c 人工智能在不断提升与完善 本文针对老年人 xff0c 儿童 xff0c 病人等实际的应
  • 操作系统——13.处理机调度的时机、切换与过程、方式

    这篇文章我们继续来学习进程调度的相关知识 目录 1 概述2 2 进程调度的时机 3 进程调度的方式 4 进程的切换与过程 5 小结 1 概述2 首先 xff0c 我们来看一下本节类容的大体框架 xff1a 2 进程调度的时机 进程调度 xf
  • 开发手册——一、编程规约_1.命名风格

    这篇文章主要梳理了在java的实际开发过程中的编程规范问题 本篇文章主要借鉴于 阿里巴巴java开发手册终极版 下面我们一起来看一下吧 1 强制 代码中的命名均不能以下划线或美元符号开始 xff0c 也不能以下划线或美元符号结束 反例 xf
  • 杂记——16.idea中导入maven项目

    这篇文章我们来讲一下如何从Gitee上拉取项目 xff0c 并将该项目导入到idea中 目录 1 拉取项目 2 idea导入项目 3 更改相关的配置 3 1更改maven仓库 3 2更改数据库的连接池 1 拉取项目 第一步 xff1a 找到
  • 数据结构与算法——7.线性表——1.顺序表

    这篇文章我们来讲一下线性表 1 线性表概述 线性表是最基本 最简单 xff0c 也是最常用的一种数据结构 一个线性表是n个具有相同特性的数据元素的有限序列 下面介绍两个术语 xff1a 前驱元素 xff1a 若A元素在B元素前面 xff0c
  • java基础知识——16.多态

    这篇文章 xff0c 我们来介绍一下java的多态 目录 1 多态概述 2 多态调用成员的特点 3 多态的优势与弊端 4 instanceof 关键字 5 小结 1 多态概述 我们先来回顾一下前面的知识 利用封装 xff0c 我们可以将一些
  • java基础知识——23.正则表达式

    这篇文章我们简略的讲一下java的正则表达式 目录 1 正则表达式概述 2 正则表达式的简单匹配规则 3 正则表达式的复杂匹配规则 4 正则表达式的分组匹配规则 5 正则表达式的非贪婪匹配 6 使用正则表达式进行搜索和替换 1 正则表达式概
  • java基础知识——26.反射

    这篇文章我们来讲一下java的代理与反射 xff0c 这是很重要的一部分内容 目录 1 什么是反射 2 获取class对象的三种方式 3 反射获取构造方法 4 利用反射来获取成员变量 5 利用反射来获取成员方法 6 反射的作用 7 反射小结
  • java基础知识——27.动态代理

    这篇文章 xff0c 我们来学一下java的动态代理 目录 1 动态代理的介绍 2 具体的代码实现 1 动态代理的介绍 动态代理 xff1a 无侵入式的额外给代码增加功能 很不好理解 xff0c 下面 xff0c 我们通过两个例子来说明一下
  • JavaWeb——1.JavaWeb概述

    这是我们javaweb的第一篇文章 xff0c 首先我们来介绍一下什么是Javaweb JavaWeb xff1a 使用java语言完成服务器端程序开发 如下面这张图所示 xff1a 可能不太好理解 xff0c 那么就用通俗的语言来解释一下
  • 关于创建spring boot项目时的报错(java: 无法访问org.springframework.boot.SpringApplication 错误的类文件:xxx类文件具有错误的版本 61)

    最近使用idea创建一个新的spring boot项目时出现了一个错误 xff0c 完成项目创建后直接运行项目出现了以下错误 当然在出现了这个问题前 xff0c 项目还出现明明已经选了Java8的版本 xff0c 项目构建完成后Java版本
  • 【公网映射——让私人电脑成为一台公网服务器】

    一 前言 自己有台电脑一直空着 xff0c 想着如果能把这台电脑布成一个服务器 xff0c 做测试用 xff0c 就能发挥它的余热了 二 步骤概述 2 1 把电脑的8080端口映射到公网 xff0c 使得外网可以访问到这台电脑的web工程
  • 数据结构-指针和结构体

    1 指针 首先看变量在内存中的存储 有时候需要获取并使用程序运行中某个变量的内存地址 xff0c 如何获取这个地址 如何存储这个地址 xff1f amp 取地址符 xff0c amp a就是获得了a的地址 可以存储地址的变量称为指针变量 指