指针

指针可以直接操作内存和硬件

1. 内存

1.1 内存含义

内存含义:

  • 存储器:计算机的组成中,用来存储程序和数据,辅助CPU进行运算处理的重要部分。
  • 内存:内部存贮器,暂存程序/数据——掉电丢失 SRAM、DRAM、DDR、DDR2、DDR3。RAM掉电丢失,DDR2,DDR3速率不同
  • 外存:外部存储器,长时间保存程序/数据—掉电不丢ROM、ERRROM、FLASH(NAND、NOR)、硬盘、光盘。
    内存是沟通CPU与硬盘的桥梁:
  • 暂存放CPU中的运算数据
  • 暂存与硬盘等外部存储器交换的数据

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

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

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

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

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

  • 编码:对每个物理存储单元(一个字节)分配一个号码
  • 寻址:可以根据分配的号码找到相应的存储单元,完成数据的读写

1.3 内存地址

启动一个程序,系统在内存上给程序分配一块内存空间,32位操作系统--210*2102^104 -- 4G的内存空间,内存的是由字节组成的,每个字节都会有地址编号,编号从0x00000 0000 - 0xffff ffff

  • 将内存抽象成一个很大的一维字符数组。
  • 编码就是对内存的每一个字节分配一个32位或64位的编号(与32位或者64位处理器相关)。
  • 这个内存编号我们称之为内存地址。
    内存中的每一个数据都会分配相应的地址:
  • char:占一个字节分配一个地址
  • int: 占四个字节分配四个地址
  • float、struct、函数、数组等

地址:内存编号

2.指针基础知识

2.1 指针与指针变量

地址:内存的编号,我们把这个编号(地址)也叫做指针

  • 内存区的每一个字节都有一个编号,这就是“地址”。
  • 如果在程序中定义了一个变量,在对程序进行编译或运行时,系统就会给这个变量分配内存单元,并确定它的内存地址(编号)
  • 指针的实质就是内存“地址”。指针就是地址,地址就是指针。
  • 指针是内存单元的编号,指针变量是存放地址的变量。
  • 通常我们叙述时会把指针变量简称为指针,实际他们含义并不一样
    11-指针-小白菜博客
    指针变量
    指针变量存放的是指针地址,所以32位操作系统指针变量大小占4个字节,64位操作系统指针变量占8个字节

2.2 指针变量的定义和初始化

#include <stdio.h>
int main()
{
        int a = 10;  // a在内存中占4个字节,a在内存中有地址
        // 1. *与符号结合表示一个指针变量
        // 2. 要保存谁的地址,将他的定义形式放在此处
        // 3. 用*p替换掉定义的变量
        
        // 指针变量p的类型,将p涂黑剩下的类型
        // 指针变量p保存什么类型的地址,将指针变量p及最近的*一起涂黑,剩下的类型就是指针变量类型
        int *p; // *p -- p是一个指针变量,p的类型是int *类型,保存int类型的地址

        p = &a;

        // 定义指针存储数组地址
        int a[10];
        int (*p)[10];

        int **p; // p的类型是 int \*\* ,保存的是int *类型的地址
        
}

#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;
}

2.3 指针变量的使用

指针变量保存谁的地址就指向谁,指针指向谁就可以操作谁

int main()
{      
        int a = 10;
        int *p;
        p = &a;
        // 在使用的时候,*与p结合,表示取p指针所指向的那块内存空间的内容
        *p = 100;
        printf("a = %d\n",a); //a = 100

        p = 200;  // 这个操作是可以的
}

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

2.4 *与取地址

  • = 两边的变量要匹配
  • 在使用的时候,在一个表达式之前取*,表达式类型减一级*
  • 在使用的时候,在一个表达式之前&,表达式类型加一级*
int main()
{      
        int a = 10;
        int *p;
        p = &a;   // p的类型是 int * -- &a之后a的类型也是int *
        // 在使用的时候,*与p结合,表示取p指针所指向的那块内存空间的内容
        *p = 100;
        printf("a = %d\n",a); //a = 100

        p = 200;  // 这个操作是可以的,p是变量可以改变

        int *p;
        int **q;
        *q = p; // q -- int *   p -- int *
        q = &p; // q -- int **  p -- int **
}

2.5 指针变量的大小

  • 32位编译器 -- 指针变量大小都是4字节
  • 64位编译器 -- 指针变量大小都是8字节
int main()
{
        short *p1;
        char *p2;
        int *p3;   // p1 p2 p3 都是4字节
        int **p4   // p4 也是4字节
}

2.6 指针的宽度与步长

不同类型的指针变量取指针指向的空间的内容的宽度

  • char *p -- 取一个字节
  • short *p -- 取二个字节
  • int *p -- 取四个字节
  • 指针的宽度 = sizeof(将指针变量与指针变量最近的*拖黑剩下的类型)
  • int **p -- 指针宽度sizeof(int *) -- 4个字节
  • 指针的宽度也叫做步长,就是指针加1跨过的字节
#include <stdio.h>
int main()
{
        int num = 0x01020304;
        // short *p1 = &num   错误, 左边类型是 short * 右边类型是 int * 所以要进行强转
        short *p1 = (short *)&num;
        char *p2 = (char *)&num; 
        int *p3 = &num;  
        
        // 通过*取指针变量所指向的空间的内容时,取的内存的宽度和指针变量本身的类型有关
        printf("%x\n",*p1);  // 304   
        printf("%x\n",*p2);  // 4
        printf("%x\n",*p3);  // 1020304
        return 0;
}
#include <stdio.h>
int main()
{
        int num = 0x01020304;
        // short *p1 = &num   错误, 左边类型是 short * 右边类型是 int * 所以要进行强转
        short *p1 = (short *)&num;
        char *p2 = (char *)&num; 
        int *p3 = &num;  
        
        // 通过*取指针变量所指向的空间的内容时,取的内存的宽度和指针变量本身的类型有关
        printf("%x\n",*p1);  // 304   
        printf("%x\n",*p2);  // 4
        printf("%x\n",*p3);  // 1020304
        return 0;
}