指针介绍
什么是指针?指针就是地址,地址就是指针。
指针,是C语言中的一个重要概念及其特点,也是掌握C语言比较困难的部分。指针也就是内存地址,指针变量是用来存放内存地址的变量, 不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。
变量都有地址,取出变量的地址如下:
#include <stdio.h> int main() { int a = 10; // a在内存中要分配空间 - 4个字节 &a; // 取出num的地址 printf("%p\n", &a); //%p - 以地址的形式打印 return 0; }
定义指针变量,存储地址
* 说明 pa 是指针变量
int 说明 pa 执行的对象是 int 类型的
int main() { int a = 10; int* pa = &a; // pa是用来存放地址的,在C语言中叫pa的是指针变量 char ch = 'w'; char* pc = &ch; return 0; }
解引用操作
如果把定义指针理解为包装成快递,那么“解引用操作”就可以理解为是拆包裹
拆出来的值就是那个变量的值,甚至还可以通过“解引用”来修改它的值。
解引用操作用法:
#include <stdio.h> int main() { int a = 10; int* pa = &a; *pa = 20; // * 解引用操作 *pa就是通过pa里边的地址,找到a printf("%d\n", a); //20 return 0; }
指针的大小
“指针的大小是相同的”
为什么相同?
“因为指针是用来存放地址的,指针需要多大空间,取决于地址的存储需要多大空间”
指针是一种复合数据类型,指针变量内容是一个地址,因此一个指针可以表示该系统的整个地址集合,
故按照32位编译代码,指针占4个字节,按照64位编译代码,指针占8个字节
(注意:不是64位系统一定占8个字节,关键是要按照64位方式编译)。
32位 - 32bit - 4byte
64位 - 64bit - 8byte
sizeof 计算指针的大小:
#include <stdio.h> int main() { printf("%d\n", sizeof(char*)); printf("%d\n", sizeof(short*)); printf("%d\n", sizeof(int*)); printf("%d\n", sizeof(long*)); printf("%d\n", sizeof(long long*)); printf("%d\n", sizeof(float*)); printf("%d\n", sizeof(double*)); //32位下都是 4 64位下都是 8 return 0; }
指针类型的意义
指针的类型决定了指针向前或者向后走一步有多大(距离)
#include <stdio.h> //演示实例 int main() { int n = 10; char* pc = (char*)&n; int* pi = &n; printf("%p\n", &n); printf("%p\n", pc); printf("%p\n", pc + 1); //+1个字节 printf("%p\n", pi); printf("%p\n", pi + 1); //+4个字节 return 0; }
指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
int main() { int a = 0x11223344; int* pa = &a; // 44 33 22 11 (至于为什么是倒着的,后面会讲。) *pa = 0;// 00 00 00 00 char* pc = &a; // 44 33 22 11 *pc = 0; // 00 33 22 11 // 在内存中仅仅改变了一个字节 // 解引用操作时就不一样了 // 整型指针操作了4个字节,让四个字节变为0 // 字符指针能把地址交到内存中, // 但是解引用操作时,只敢动1个字节 return 0; }
野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针的成因
① 指针未初始化;
② 指针越界访问;
③ 指针指向的空间已释放
如何规避野指针
1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放及时置NULL
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性
指针运算
指针+-整数
指针加整数:打印 1 2 3 4 5 6 7 8 9 10
#include <stdio.h> int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; int i = 0; int sz = sizeof(arr) / sizeof(arr[0]); int* p = arr; // 指向数组的首元素 - 1 for(i=0; i<sz; i++) { printf("%d ", *p); p = p + 1; //p++ 第一次循环+1之后指向2 } return 0; }
指针减整数:打印 10 8 6 4 2
#include <stdio.h> int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; int i = 0; int sz = sizeof(arr) / sizeof(arr[0]); int* p = &arr[9]; // 取出数组最后一个元素的地址 for(i=0; i<sz/2; i++) { printf("%d ", *p); p = p - 2; } return 0; }
指针-指针
(指针+指针无意义)
指针减指针得到的是元素之间元素的个数;
注意事项:当指针减指针时,他们必须指向同一空间(比如同一个数组的空间);
指针减指针:
int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; printf("%d\n", &arr[9] - &arr[0]); // 得到指针和指针之间元素的个数 return 0; }
int my_strlen(char* s) { char* p = s; while (*p != '\0') { p++; } return p - s; }
指针的关系运算(比较大小)
指针减减指针:
#define N_VALUES 5 int main() { float values[N_VALUES]; float *vp; for(vp=&values[N_VALUES]; vp> &values[0]; ) { *--vp = 0; //前置-- } return 0; }
简化(这么写更容易理解,上面代码 *--vp在最大索引后的位置开始访问的):
int main() { float values[5]; float *vp; for(vp=&values[N_VALUES]; vp> &values[0]; vp--) { *vp = 0; } return 0; }
实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的
那个内存位置的指针进行比较。
C语言初阶⑦(指针初阶)知识点+笔试题(下):https://developer.aliyun.com/article/1513015