int* pa = &a
int是在说明pa指向的对象是int类型;*说明pa是指针变量
地址的存放需要多大的空间,指针变量的大小就是多大
32位机器上:地址是32个0/1的二进制序列,存储起来需要32个bit位,也就是4个字节,指针变量的大小就是4个字节
64位机器上:地址是64个0/1的二进制序列,存储起来需要64个bit位,也就是8个字节,指针变量的大小就是8个字节
3..指针类型和意义
3.1指针的解引用
指针类型决定了执行解引用操作时访问多大的空间
int*的指针解引用访问4个字节;char*访问1个字节
3.2指针+-整数
指针类型决定了指针的步长,就是向前/向后一步走多大距离
type* p:
p+i
是跳过了i个type类型的数据,相当于跳过了i*sizeof(type)个字节
根据实际的需要,选择适当的指针类型才能达到效果
3.3void*指针
可以理解为无具体类型的指针(泛型指针),这种类型的指针可以用来接受任意类型的地址。但是也有局限性,void*类型的指针不能直接进行指针的解引用和+-
4.const修饰指针
4.1const修饰变量
int n = 10; → const int n =10;
常属性——不能被修改了
const修饰了n之后,n不能被修改了,但是n还是变量(常变量)
4.2const修饰指针变量
const如果放在*的左边,修饰(限制)的是指针指向的内容不能通过指针来改变。但是指针变量本身的内容可变。
不能通过p改变p指向对象的内容,但是可以改变p指向的对象。p本身可以改变,*p不可以改变
const如果放在*的右边,修饰(限制)的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。
不能修改p本身的值,但是p指向的内容(*p)是可以通过p改变的
5.指针运算
5.1指针+-整数
因为数组在内存中是连续存放的,只要知道第一个元素的地址,就能知道后米娜的所有元素
5.2指针-指针
int my_strlen(char *s)
{ char *p = s;
while (*p != ‘\0' ) ;
p++;
return p-s; }
//指针-指针的绝对值是指针和指针之间的元素个数
int main()
{ printf("%d\n", my_strlen("abc"));
return 0; }
5.3指针关系运算
(两个指针的比较大小)
6.野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
6.1野指针成因
6.1.1变量未初始化
局部变量不初始化的时候,它的值是随机值
6.1.2指针的越界访问
6.1.3指针指向的空间释放
6.2 如何规避野指针
6.2.1指针初始化
//int *p;//不初始化,就是随机值,野指针
int *p = NULL; //在使用指针前一定要检查是否是空指针
6.2.2小心指针越界
一个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问
6.2.3指针变量不在使用时,及时置NULL,指针使用前检查有效性
6.2.4避免返回局部变量的地址
7.assert断言
assert.h头文件定义了宏assert(),用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行。这个宏被称为断言
例:assert(p != NULL);//验证p是否是空指针
#define NDEBUG
#include <assert.h>
加入上面两行代码,重新编译程序,程序会禁用文件中所有的assert语句
assert的缺点是,因为引入了额外的检查,增加了程序的运行时间
8.指针的使用和传址调用
8.1strlen的模拟实现
//strlen()——求字符串长度——统计的是字符串\0之前的字符个数
参数str接收一个字符串的起始地址,然后开始统计字符串中\6之前的字符个数,最终返回长度。如果要模拟实现只要从起始地址开始向后逐个字符的遗历,只要不是\0字符,计数器就+1,这样直到\0就停止。
8.2传值调用和传址调用(?)
传值调用:将实参的数据(而不是地址)传给了形参,在被调用的函数中,形参值改变了,但是不影响主调函数的实参值。参数传递方向是由实参向形参单向传递。
当实参传递给形参的时候,形参有自己独立的空间(地址)。形参是实参的一份临时拷贝,对形参的修改不会影响实参。
传址调用:将实参变量的地址传给形参,(此时形参是指针,即让形参的指针指向实参地址)改变形参的同时也把实参改变了。