【1】指针
优点:
1)使程序更加简洁、紧凑、高效
2)有效的表示比较复杂的数据结构
3)动态内存分配
4)可以得到多余一个的返回值
- 概念:
地址:内存中每个字节单位都有一个编号
指针:指针就是地址
指针变量:用于存放地址的变量
- 定义格式: 存储类型 数据类型 *变量名;
int * p;
int a=5;
int * p = &a;
char ch = 'a';
char * q = &ch;
- 指针操作符
& : 取地址,取变量的地址
*: 取内容,取地址里面的内容
*和&互为逆运算,可以相互抵消
int a = 5;
int *p = &a;
*&a == a
&*a //错误
- 指针赋值
使用指针变量前不仅要定义还要初始化,未经初始化的指针变量不能随便使用,会产生野指针
1)将普通变量的地址赋值给指针变量
int a=5;
a) int * p = &a;
b) int *p = NULL;
p = &a;
printf("%d %d\n",*p,a);//5 5
printf("%p %p\n",p,&a);
*p = 10;//可以通过修改*p改变指针指向内容
printf("%d %d\n",*p,a);//10 10
- 将数组的首地址赋值给指针变量
char buf[32]="hello";
char * p = buf; // 指针p指向的是字符数组的首地址,也就是'h'的地址
printf("%s %s\n",buf,p);//hello hello
3)将指针变量赋值给指针变量
int a=5;
int *p = &a;
int *q= p;
- 指针运算
int a[5]={1,2,3,4,5};
int *p = a;
1)算术运算
p++:指针向高地址方向移动了一个数据单位的大小,指针的指向发生变化
p--:指针向低地址方向移动了一个数据单位的大小,指针的指向发生变化
p+n:访问了高地址方向第n个数据的地址,指针的指向没有变化
p-n:访问了低地址方向第n个数据的地址,指针的指向没有变化
q-p:两个地址之间的差=两个地址之间相隔元素的个数
计算字节数:
1)(int)q-(int)p :将地址强转成int类型,相减得到的结果就是字节数
- sizeof(数据类型)*(q-p)
2)关系运算
> < == !=
指针之间的关系运算比较的是指向地址的高低,指向高地址的指针>指向低地址的指针
只有在同一个数组中比较才有意义
- 指针的大小
sizeof(指针名)
32位操作系统:4字节
64位操作系统:8字节
总结:
1)32位操作系统:4字节; 64位操作系统:8字节
2)内存的地址是固定的,但是变量的地址是不固定的,栈区变量是由系统随机分配的
3)指针的类型是根据指向空间的数据的类型决定的
- 段错误
Segmentation fault (core dumped)
1)野指针,没有规定指向的指针会在内存中乱指。
产生原因:
1)指针变量没有初始化
2)free之后没有重新赋值为NULL
解决:int *p = NULL;
2)对非法空间进行赋值
练习1:将字符串转换成整型数字输出。
要求:字符串为0-9组成,输出数据为一个整形数
Eg:char s[10]=”123”;printf(“%d\n”,num);//num=123;
练习2:字符串倒置,指针实现
【2】指针修饰
- const
- const int a = 10; // int const a = 10;
a = 20;//错误,a被const修饰为只读,不能赋值
但是可以通过指针修改*p,间接修改a的内容
- const int *p;// const修饰*p,指针指向的内容不能更改,指针的指向可以更改
int const *p;
int a = 10,b=20;
const int *p = &a;
a) *p = 30;//错误
b) p = &b;//正确
3) int * const p;//const修饰p,指针指向的内容可以修改,指针的指向不能更改
int a = 10,b=20;
int * const p = &a;
a) *p = 30;//正确
b) p = &b;//错误
4)const int * const p;//都不能更改
- void
void 不能修饰普通变量;
void * p;//任意类型的指针
使用场景:一般用于函数传参或函数返回值
注意:通过void类型指针取数据时,需要对地址强转
void *p = buf;
printf("%d \n",*(int *)p);
取地址:(int *)p 取内容:*(int *)p