一,什么是指针
内存中每个用于 数据 存取的 基本单位 ,都被赋予一个唯一的 序号 ,称为地址。
C语⾔中给地址起 了新的名字叫:指针。 我们可以理解为: 内存单元的编号 == 地址 == 指针。
二,取地址符号(&),解引⽤操作符(*)和指针变量
#include <stdio.h>
int main()
{
int a = 10;
int* pa=&a;//取出a的地址
printf("%p\n", pa);
return 0;
}
上述的代码就是创建了整型变量a,内存中 申请4个字节,⽤于存放整数10,要想得到a的地址,就需要用取地址符来得到地址,然后用指针变量(类型 * 变量名)来储存地址,再用%p打印。
把地址保存起来,未来是要使⽤的,那怎么使⽤呢? 在现实⽣活中,我们使⽤地址要找到⼀个房间,在房间⾥可以拿去或者存放物品。 C语⾔中其实也是⼀样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针) 指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符(*)。
#include <stdio.h>
int main()
{
int a = 100;
int* pa = &a;
*pa = 0;
return 0;
}
*pa 的意思就是通过pa中存放的地址,找到指向的空间, *pa其实就是a变量了;所以*pa = 0,这个操作符是把a改成了0.
三,指针大小
#include <stdio.h>
//指针变量的⼤⼩取决于地址的⼤⼩
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{
printf("%zd\n", sizeof(char *));
printf("%zd\n", sizeof(short *));
printf("%zd\n", sizeof(int *));
printf("%zd\n", sizeof(double *));
return 0;
}
四,指针加减整数
#include <stdio.h>
int main()
{
int n = 10;
char* pc = (char*)&n;
int* pi = &n;
printf("&n=%p\n", &n);
printf("pc=%p\n", pc);
printf("pc+1=%p\n", pc + 1);
printf("pi=%p\n", pi);
printf("pi+1=%p\n", pi + 1);
return 0;
}
运行结果
五,指针运算
5.1指针加减整数
#include <stdio.h>
//指针+- 整数
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
for(i=0; i<sz; i++)
{
printf("%d ", *(p+i));//p+i 这⾥就是指针+整数
}
return 0;
}
运行结果:
p+0得到1
p+1得到2
p+2得到3
............
5.2指针减指针
//指针-指针
#include <stdio.h>
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;
}
运行结果:
指针减指针得到的是指针之间元素的个数,因此可以用来模拟库函数strlen()。
5.3指针的关系运算
//指针的关系运算
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
while(p<arr+sz) //指针的⼤⼩⽐较
{
printf("%d ", *p);
p++;
}
return 0; }
运行结果:
arr是第一个元素地址,sz是数组大小为10,arr+10就是元素10后面一个元素
同时p也会不断加1,这时候p<arr+sz不成立了,就不会打印了
六,野指针
6.1野指针成因
1.指针未初始化
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
2.指针越界访问
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = &arr[0];
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
3.指针指向的空间释放
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}
6.2如何规避野指针
1.指针初始化
#include <stdio.h>
int main()
{
int num = 10;
int*p1 = #
int*p2 = NULL;
return 0;
}
2.小心指针越界
3.及时置NULL
指针不用时及时置NULL,使用前检查有效性【if(p!=NULL){}】
4.避免返回局部变量的地址
例子看6.1.3test()函数
七,关于数组名的理解
#include <stdio.h> int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; printf("&arr[0] = %p\n", &arr[0]); printf("arr = %p\n", arr); return 0; }
可以发现数组名就是数组⾸元素(第⼀个元素)的地址。
---------------------------------------------------------------------------------------------------------------------------------
接下来看这段代码:
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", sizeof(arr));
return 0;
}
输出的结果是:40,如果arr是数组⾸元素的地址,那输出应该的应该是4/8才对。
其实数组名就是数组⾸元素(第⼀个元素)的地址是对的,但是有两个例外:
• sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩,单位是字节。
• &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素的地址是有区别的)。
#include <stdio.h> int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; printf("&arr[0] = %p\n", &arr[0]); printf("arr = %p\n", arr); printf("&arr = %p\n", &arr); return 0; }
你会发现结果一样
那arr和&arr有什么区别?
#include <stdio.h> int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; printf("&arr[0] = %p\n", &arr[0]); printf("&arr[0]+1 = %p\n", &arr[0]+1); printf("arr = %p\n", arr); printf("arr+1 = %p\n", arr+1); printf("&arr = %p\n", &arr); printf("&arr+1 = %p\n", &arr+1); return 0; }
八,二级指针
#include <stdio.h>
int main()
{
int a=10;
int* p=&a;//一级指针
int** pa=&pa;//二级指针
}
int b = 20 ;*pa = &b; // 等价于 pa = &b;
**pa = 30 ;// 等价于 *pa = 30;// 等价于 a = 30;
八,指针数组
九,字符指针变量
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
int main()
{
const char* pstr = "hello world";//这⾥是把⼀个字符串放到pstr指针变量⾥了吗?
printf("%s\n", pstr);
return 0;
}
const char* pstr = "hello bit."; 特别容易以为是把字符串 hello bit 放 到字符指针 pstr ⾥了,但是本质是把字符串 hello bit. ⾸字符的地址放到了pstr中。
十,数组指针变量
• 整形指针变量: int * pint; 存放的是整形变量的地址,能够指向整形数据的指针。
• 浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。
那数组指针变量应该是:存放的应该是数组的地址,能够指向数组的指针变量。
int arr[10] = {0};
int(*p)[10] = &arr;//数组指针
注意区别数组指针和指针数组。
十一,函数指针变量
#include <stdio.h> int Add(int x, int y) { return x+y; } int main() { int(*pf3)(int, int) = Add; printf("%d\n", (*pf3)(2, 3)); printf("%d\n", pf3(3, 5)); return 0; }
结果是 5 8。
十二,函数指针数组
int (*par[3])();
par 先和 [] 结合,说明 par是数组,数组的内容是什么呢? 是 int (*)() 类型的函数指针。
基础知识就到结束了,还有的期待我下次分享吧。