1.内存
内存单元编号==地址==指针
2.指针变量和地址
2.1.取地址操作符(&)
&——用来获取变量地址
#include <stdio.h>
int main()
{
int a = 10;
printf("%p\n", &a);//%p打印出&a取出的地址,即a的地址
return 0;
}
2.2.指针变量
通过取地址操作符(&)获取的地址也是一个数值,而指针变量是一种变量,用来存放地址
2.3.解引用操作符—(*)
#include<stido.h>
int main ()
{
int a=100;
int *p=&a;
*pa=0;
return 0;
}
2.4.指针变量大小
2.5.指针变量类型意义
2.5.1类型:
①整形指针 int *p
②字符指针 char *p
③数组指针变量 (例如形式 int (*p)[10]; )(p要先和*结合,说明p是一个指针变量)
数组名就是数组⾸元素(第⼀个元素)的地址,本质上p[i] 是等价于 *(p+i)。
⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。
void test(int* arr)//参数写成指针形式
void test(int arr[])//参数写成数组形式
④函数指针
2.5.2 函数指针类型解析
int (*pf3) (int x, int y)
| | ------------
| | |
| | pf3指向函数的参数类型和个数的交代
| 函数指针变量名
pf3指向函数的返回类型
int (*) (int x, int y) //pf3函数指针变量的类型
2.5.2.1函数指针的应用
#include <stdio.h>
int Add(int x, int y)
{
return x+y;
}
int main()
{
int(*pf)(int, int) = Add;
printf("%d\n", (*pf)(2, 3));
printf("%d\n", pf(3, 5));
return 0;
}
2.5.3指针的解引用
//代码1
#include <stdio.h>
int main()
{
int n = 0x11223344;
int *pi = &n;
*pi = 0;
return 0;
}
//代码2
#include <stdio.h>
int main()
{
int n = 0x11223344;
char *pc = (char *)&n;
*pc = 0;
return 0;
}
2.5.4 void*指针
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;
char* pc = &a;
return 0;
}
上述代码,将⼀个int类型的变量的地址赋值给⼀个char*类型的指针变量,编译器会因为类型不兼容而发出警告,而使用void*类型就不会有这样的问题。
2.5.5二维数组传参
如数组 int a[3][5],形参可以写成数组,也可以写成指针形式int (*p)[5],即意味着⼆维数组传参本质上也是传递了地址,传递的是第⼀ ⾏这个⼀维数组的地址。而运用时可以写作 *(*(p+i)+j )
3.const修饰指针
3.1const修饰变量
#include <stdio.h>
int main()
{
int m = 0;
m = 20;//m是可以修改的
const int n = 0;
n = 20;//n是不能被修改的
return 0;
}
上述代码中,n就是不可以被直接修改的,但如果绕开n,使用n的地址,去修改n就可以做到。
#include <stdio.h>
int main()
{
const int n = 0;
printf("n = %d\n", n);
int*p = &n;
*p = 20;
printf("n = %d\n", n);
return 0;
}
两次结果:n=0和n=20;这里看到即使变量n被const修饰,通过拿到n的地址也可以改变n的值,那要怎么做即使拿到n的地址,也不可以改变n的值呢?就到下一个知识点
3.2const修饰指针变量
4.指针的运算
4.1指针+-整数
案例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;
}
#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);
printf("%p\n", pi);
printf("%p\n", pi+1);
return 0;
}
4.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;
}
4.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;
}
5.野指针
5.1成因:指针未初始化,指针越界访问,以及指针指向的空间的释放
//指针指向的空间释放
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}//脱离函数text,n的空间就被释放了
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}
5.2规避办法
5.2.1指针初始化
明确知道指针指向哪里就直接复制地址,不知道就给指针赋值NULL。(不可以赋值为0,0也是地址)
5.2.2小心指针越界
5.2.3及时置空
5.2.4避免返回局部变量的地址
6.assert断言
assert(p != NULL);
7.传址调用
#include <stdio.h>
void Swap2(int*px, int*py)
{
int tmp = 0;
tmp = *px;
*px = *py;
*py = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d b=%d\n", a, b);
Swap1(&a, &b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
8.二级指针
![](https://i-blog.csdnimg.cn/direct/bf8b64ed9c1c46cd8ddfdaf1bb6bf22d.png)
![](https://i-blog.csdnimg.cn/direct/07842a2a6d0c4090ac8e694ba0f0ecd7.png)
9.指针数组
9.1指针数组运用:模拟二维数组
#include <stdio.h>
int main()
{
int arr1[] = {1,2,3,4,5};
int arr2[] = {2,3,4,5,6};
int arr3[] = {3,4,5,6,7};
//数组名是数组⾸元素的地址,类型是int*的,就可以存放在parr数组中
int* parr[3] = {arr1, arr2, arr3};
int i = 0;
int j = 0;
for(i=0; i<3; i++)
{
for(j=0; j<5; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
}
11函数指针数组
即把函数的地址存到一个数组中,
int (*parr1[3])();//函数指针数组定义
parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢? 是 int (*)() 类型的函数指针。
11.1应用
应用:转移表
int(*p[5])(int x, int y) = { 0, add, sub, mul, div };
5种情况存放在数组中