0.前言
本章开始主要讲解C语言内存空间的使用。
这里的内存泛指RAM资源、内存条资源、显卡资源、IIC资源等等。
本文主要整理指针的使用方法。
内存属性:
1.内存操作的大小
2.内存的变化性,可写可读的问题
本文目录:
1.指针
1.1指针概述
- 指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。是内存的代名词。
- 要搞清一个指针需要搞清指针的四方面的内容:
- 指针的类型、指针所指向的类型、指针的值或者叫指针所指向的内存区、指针本身所占据的内存区。
- 指针指向内存空间,一定要保证合法性。
C语言编译器对指针这个特殊的概念,有两个疑问?编译器能解答这两个问题,才能正常编译。
- 1.分配一个盒子,盒子要多大?所占内存字节大小?
在32bit系统中,指针就4个字节。
- 2.盒子里存放的地址所指向内存的读取方法是什么?
指针指向的是首地址(地址是连续的),所指向的内存读取(使用)大小,取决与指针变量的类型,即(char* p;//占1个字节。int* p;//占4个字节。char和int修饰的是指向内存的大小,而不是p本身。p本身大小就4个字节。)
观察浮点数在内存中的存储形式
- ①
- ②
%x是当作有符号数读取的,而这里使用char读,会将首位当作符号位,因此出错。改为下面方式: - ③
1.2概述总结
1.2.1指针的类型
- 从语法的角度看,只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。
- 这是指针本身所具有的类型。让我们看看例一中各个指针的类型:
(1)int*ptr;//指针的类型是int*
(2)char*ptr;//指针的类型是char*
(3)int**ptr;//指针的类型是int**
(4)int(*ptr)[3];//指针的类型是int(*)[3]
(5)int*(*ptr)[4];//指针的类型是int*(*)[4]
1.2.2指针所指向的类型
- 当通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。
- 从语法上看,只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如:
(1)int*ptr; //指针所指向的类型是int
(2)char*ptr; //指针所指向的的类型是char
(3)int**ptr; //指针所指向的的类型是int*
(4)int(*ptr)[3]; //指针所指向的的类型是int()[3]
(5)int*(*ptr)[4]; //指针所指向的的类型是int*()[4]
- 在指针的算术运算中,指针所指向的类型有很大的作用。
- 指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。
- 当你对C 越来越熟悉时,你会发现,把与指针搅和在一起的"类型"这个概念分成"指针的类型"和"指针所指向的类型"两个概念,是精通指针的关键点之一。
- 部分资料中,就把指针的这两个概念搅在一起了,所以前后矛盾,越看越糊涂。
1.2.3指针的值----或者叫指针所指向的内存区或地址
- 指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。
- 在32 位程序里,所有类型的指针的值都是一个32 位整数,因为32 位程序里内存地址全都是32 位长。
- 指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。
- 说一个指针的值是XX,就相当于说该指针指向了以XX 为首地址的一片内存区域;说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。
- 指针所指向的内存区和指针所指向的类型是两个完全不同的概念。 在【int*ptr;】中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。
- 以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?(重点注意)
1.2.4指针本身所占据的内存区
- 指针本身占了多大的内存?
- 你只要用函数sizeof(指针的类型)测一下就知道了。
- 在32 位平台里,指针本身占据了4 个字节的长度。
- 指针本身占据的内存这个概念在判断一个指针表达式是否是左值时很有用。
- (简单地说:以赋值符号 = 为界,= 左边的就是左值,= 右边就是右值。 比如:(1) int b = 3;(2) int a = b;第(2)行代码,a为左值,b为右值。)
此部分来源链接: C语言指针详解(经典,非常详细)
2.指针+修饰符(限制符)
2.1常量、只读关键字
const
//①以下两种等价,指针指向位置可改变,指向的内存空间是只读的
//大多情况下是指向字符串" "的首地址,字符串内容不可修改
const char* p;
char const *p;
//②以下两种等价,指向指向位置不可改变,指向的内存空间是可变的
//大多数情况下用于硬件资源的定义,寄存器地址不可改变,内容可更改
char * const p;
char *p const;
//③以下情况,指针指向位置不可可改变,指向的内存空间也是只读的
//大多数情况下用于ROM空间定义,既是硬件且内容不可改变
const char *p const;
- " "双引号是整形常量,是不可变的,不可被写入的。默认是const。
//Segmentation fault,段错误,是内存被非法访问导致的,因为写的是只读属性内存
char *p="hello world!";//因为是双引号,使用指针默认是const 定义
//等价于下面
const char *p="hello world!";
buf[ ]则可以更改,可写。如下:
man手册:帮助手册
终端用法举例:
man printf //查看printf手册说明
因为是const,所以printf( “”);里需要加引号。
2.2防止优化
volatile
//①对于指向内容的修饰,防止软件将用于硬件修改的变量优化掉
volatile char* p;//硬件相关变量
*p=0x10;
while(*p == 0x10);
xxxx;
2.3声明替换
typedef
//声明替换
char *name_t; //name_t是一个指针,指向了一个char类型的内存
//更改
typedef char *name_t; //name_t是一个指针类型的名称,指向了一个char类型的内存
name_t abc;
3.指针+运算符
++、+、--、-//加法、减法运算
int *p=xxx; //[0x12]
p++; //p=[0x12+1*(sizeof(*p))]
//指针的加法运算,实际上加的是一个单位,单位的大小可以使用sizeof(p[0])
//其他符号同理
-----------------------------------------------------------
变量名[n]
//n:ID标签
//地址内容的标签访问方式
int *p;
p[2];//非线性访问,求往后两个单位的内容;p[0]是原本指向地址的内容
//p+n,结果是地址;p[n],结果是内容
- 内存里是小端模式,即数据从低到高存,存一个变量时,先存低位,再存高位(变量分配地址,是从高往低分配的)
- 强制类型转换,是为了告诉编译器是人为操作,防止报错
指针越界(内存泄漏)
int *p;
p[10000];
//使用指针运算时要考虑范围大小,否则太大可能会指针越界(内存泄漏)
- const是建议字符(在C++中是强制字符),只能保证在编译时不改变,不能保证执行时不改变。
>=、<=、==、!=
//逻辑操作符
//常用:==、!=
//1.跟一个特殊值进行比较:0x0,地址默认有的无效值/结束标志,即NULL(任意类型)
if(p==0x0);
或
if(p==NULL);
//2.指针必须是同类型的比较才有意义
char*
int*
4.多级指针
多级指针:存放地址的地址空间。
int **p;//int是修饰最终的内存变量,多级指针描述的是内存与内存之间的线性关系
char **p;
//多级指针常用以下读取方法:
p[0]、p[1]、p[2]
//多级指针结束标志(限制):
if(p[m]==NULL)//结束了
改写:
- 二维指针本质上相当于一张地址表,地址表有无穷大,但是人为定义好了指针结束标志。
参考资料:
链接1: C语言指针详解(经典,非常详细)
链接2: 嵌入式C语言