指针初阶+初试结构体

1.指针概述

内存

内存含义:

存储器:计算机的组成中,用来存储程序和数据,辅助CPU进行运算处理的重要部分。

内存:内部存储器,暂存程序/数据——掉电丢失 SRAM,DRAM,DDR,DDR2,DDR3。

外存:外部存储器。长时间保存程序/数据——掉电不丢失ROM,ERRROM,FLASH(NAND,NOR),硬盘,光盘。

内存是沟通CPU与硬盘的桥梁:

1.暂存CPU中的运算数据   2.暂存与硬盘等外部存储器交换的数据。

物理存储器和存储地址空间

有关内存的两个概念:物理存储器和存储地址空间。

物理存储器:实际存在的具体存储器芯片。

1.主板上装插的内存条  2.显示卡上显示的RAM芯片  3.各种适配卡上的RAM芯片和ROM芯片

存储地址空间:简单来说就是对存储器编码的范围。我们在软件上常说的内存就是指这一层含义

1.编码:对每个物理存储单元(一个字节)分配一个号码

2.寻址:可以根据分配的号码找到相应的存储单元,完成数据的读写

内存地址

1.将内存抽象成一个很大的一堆字符数组。

2.编码就是对内存的每一个字节分配一个32位或64位的编号(与32位或者64位处理器相关)。

3.这个内存编号我们称之为内存地址:

char:占一个字节分配一个地址   int:占4个字节分配4个地址  float,struct,函数,数组同理

指针和指针变量

内存区的每一个字节都有一个编号,这就是地址。如果在程序中定义了一个变量,在对程序进行编译或运行时,系统就会给这个变量分配内存单元,并确定它的内存地址(编号)。指针的实质就是内存地址,指针是内存单元的编号,指针变量就是存放地址的变量。

通常我们叙述时会把指针变量简称为指针。

2.指针基础知识
 指针变量的定义和使用

1.指针是一种数据类型,指针变量是一种变量

2.指针变量指向谁,就把谁的地址赋值给该指针变量

3.” * “操作符操作的是指针变量指向的内存空间

总之:指针是内存中最小单元的编号,也就是地址。平时口语中说的指针,通常指的就是指针变量,是用来存放内存地址的变量。即指针就是地址,口语中的指针通常指的就是指针变量

#include <stdio.h>
 
int main()
{
	int a = 0;
	char b = 100;
	printf("%p, %p\n", &a, &b); //打印a, b的地址
 
	//int*代表一种数据类型,int*指针类型,p才是变量名
	//定义了一个指针类型的变量,可以指向一个int类型变量的地址
	int *p;
	p = &a;//将a的地址赋值给变量p,p也是一个变量,值是一个内存地址编号
	printf("%d\n", *p);//p指向了a的地址,*p就是a的值
 
	char *p1 = &b;
	printf("%c\n", *p1);//*p1指向了b的地址,*p1就是b的值
 
	return 0;
}

注意:&可以取得一个变量在内存中的地址。但是,不能取寄存器变量,因为寄存器变量不在内存里而在CPU里,所以是没有地址的

指针类型不能混用的原因:

结论1:指针类型决定了指针在被解引用的时候访问几个字节,如果是int*的指针,解引用访问4个字节,如果是char*的指针,解引用访问1个字节。所以,指针类型的不同在存放时不会产生问题,但不同的指针类型大小决定了解引用时访问几个字节。

结论2:指针的类型决定了指针+-1操作的时候,跳过几个字节,即决定了步长。

int* 和 float* 指针不能混用的原因:

虽然二者访问的变量大小都是4个字节,但int* 访问的是整型数据,是以整型的存储方式存储在内存中的,float* 访问的是浮点型数据,是以浮点型的数据存储在内存中的。

3.野指针和空指针

野指针和空指针的定义

野指针的定义是指针变量指向一个未知的空间,指针变量也是变量,是变量就可以任意赋值,不要越界即可(32位为4字节,64位为8字节),但是任意数值赋值给指针变量没有意义,因为这样的指针就成了野指针,此指针指向的区域是不可控的(操作系统不允许操作此指针指向的内存区域)。所以,野指针不会直接引发错误,即野指针不解引用是不会造成危险的,操作野指针指向的内存区域才会出问题。但是野指针和有效指针变量保存的都是数值,为了标志此指针变量没有指向任何变量(空闲可用),C语言中可以把NULL赋值给此指针,空指针是指内存地址编号为0的空间。

如何规避野指针

1.指针初始化 2.小心指针越界 3.指针指向空间释放即时置为NULL 4.避免返回局部变量的地址 

5.指针使用之前检查有效性

#include <stdio.h>
//使用之前检查指针有效性
int main()
{
    int *p = NULL;
    int a = 10;
    p = &a;
    if(p != NULL)
   {
        *p = 20;
   }
    return 0; 
}

4.万能指针与const修饰的指针变量

万能指针

void*指针可以指向任意变量的内存空间:在内存中 void*占四个字节大小,因此万能指针可以接收任意类型变量的内存地址,但最好将其强制转换为void*,也可以通过万能指针修改变量的值,但是修改变量的值时需要找到变量对应的指针类型。

int main()
{
	int a = 10;
	void* p = NULL;
	p = (void*)&a; //指向变量时,最好转换为void *
	//使用指针变量修改变量值时,转换为int *
	*((int*)p) = 11;
	printf("a = %d\n", a);
	return 0;
}

const修饰指针变量

1.const修饰普通变量变为常量:可以通过指针间接修改常量的值

2.const修饰指针类型:可以修改指针变量的值,但不可以修改指针变量指向内存空间的值

3.const修饰指针变量:可以修改指针变量指向内存空间的值,不可以修改指针变量的值

4.const修饰指针类型修饰指针变量:只读指针 只能通过二级指针来修改

5、二级指针

一级指针用来存放变量的地址,二级指针则用来存放一级指针变量的地址

一级指针+偏移量相当于跳过了偏移量个元素,二级指针+偏移量相当于跳过了偏移量个一级指针的大小

二级指针的运算:

1.*ppa通过对ppa的地址进行解引用,这样找到的是pa,*ppa其实访问的就是pa.

2.**ppa先通过*ppa找到pa,然后对pa进行解引用操作,那找到的就是a.

结构体

1.结构体类型的声明
.结构的基础知识:

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

2.结构的声明:

typedef struct Stu
{
    char name[20];//名字
    int age;//年龄
    char sex[5];//性别
    char id[20];//学号
}Stu;//分号不能丢

3.结构成员的类型

结构成员可以是标量,数组,指针甚至是其他结构体

4.结构体变量的定义和初始化

struct Peo
{
	char name[20];
    char tele[12];
	char sex[5];
	int high;
};
 
struct St
{
	struct Peo p;//结构体嵌套结构体
	int num;
	float f;
};
int main()
{
	struct Peo p1 = {"张三", "15596668862", "男", 181};//结构体变量的创建
	struct St s = { {"李四", "15596668888", "女", 166}, 100, 3.14f};
	printf("%s %s %s %d\n", p1.name, p1.tele, p1.sex, p1.high);
	printf("%s %s %s %d %d %f\n", s.p.name, s.p.tele, s.p.sex, s.p.high, s.num, s.f);
	return 0;
}
struct Peo
{
	char name[20];
	char tele[12];
    char sex[5];
	int high;
};
struct St
{
	struct Peo p;
	int num;
	float f;
};
void print1(struct Peo p)
{
	printf("%s %s %s %d\n", p.name, p.tele, p.sex, p.high);//结构体变量.成员变量
}
void print2(struct Peo* sp)
{
	printf("%s %s %s %d\n", sp->name, sp->tele, sp->sex, sp->high);//结构体指针->成员变量
}
int main()
{
    struct Peo p1 = {"张三", "15596668862", "男", 181};//结构体变量的创建
	struct St s = { {"lisi", "15596668888", "女", 166}, 100, 3.14f};
	printf("%s %s %s %d\n", p1.name, p1.tele, p1.sex, p1.high);
	printf("%s %s %s %d %d %f\n", s.p.name, s.p.tele, s.p.sex, s.p.high, s.num, s.f);
	print1(p1);
	print2(&p1);
	return 0;
}

上面的print1和print2函数哪个好些?

答:优选print2函数.

原因:函数传参的时候参数是需要压栈的,如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销过大,会导致性能的下降.

结论:结构体传参的时候,要传结构体的地址.

  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 18
    评论
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

比特阿豪·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值