目录
1. 指针
int *p = &a ; 对变量 a ,取出它的地址,可以使用 & 操作符p就是一个之指针变量,将 a 的 4 个字节的第一个字节的地址存放在 p 变量中
地址在32位平台是4个字节,在64位平台是8个字节
指针的大小在32位平台是4个字节,在64位平台是8个字节
2.指针类型
先了解一下指针的定义方式是: type + * 指针名,,所以去掉指针名就是指针类型
例子 | 指针类型 | ||
int*ptr | int * |
是为了存放 int
类型变量的地址。
| 解引用可以访问4个字节 |
char*ptr | char* |
是为了存放
char
类型变量的地址。
| 解引用可以访问1个字节 |
double*p | double* | 是为了存放 char 类型变量的地址 | 解引用可以访问8个字节 |
int(*ptr)[3] | int()[3] | ||
int**ptr | int ** |
注意:"指针的类型"和"指针所指向的类型"是两个概念
指针类型决定指针进行解引用的时候,能够访问空间大小。
#include <stdio.h>int main(){int a=0X11223344;int * pa =&a; 存放 int 类型变量char * pc =&a; 存放 char 类型变量printf("%p\n",pa);printf("%p\n",pa+1);printf("%p\n",pc);printf("%p\n",pc+1);return 0;}
代码执行结果:
0095FB58
0094FB5C
0095FB58
0095FB59
观察代码运行结果,我们发现int类型增加了4,char类型增加了1
同理我们可以推出:double类型指针跳过8个字节
指针类型决定指针+1跳过几个字节(指针的步长)
解引用
这是对指针的一种应用。解引用是指:通过指针,找到对应的内存和内存中的数据。我们可以通过解引用访问或修改指针指向的内存内容。
解引用后等价于指针指向的变量。
int main()
{
int arr[10] ={0};char*p=arr; 每次只能访问一个字节
// int *p=arr; 每次可以访问四个字节
int i=0;
for(i=0;i<10;i++}
{
*(p+i)=1; 将内存内容改为10个1
}
return 0;}
3.野指针:指向位置不可知的指针
- 避免方法:
- 1. 指针初始化{不知道初始化什么值时用NULL(空指针)
- 2. 小心指针越界
- 4. 避免返回局部变量的地址
- 5. 指针使用之前检查有效性
4.指针运算
- 指针+- 整数
- 指针-指针
- 指针的关系运算(比大小)
练习:利用指针打印一组数字
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
int* p = &arr[0];
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
return 0;
int*p=arr 与int *p=arr[0] 意义相同,访问数组名,即数组首位的地址,即arr[0]
或者也可以写为
#include<stdio.h>
int main()
{
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int i=0;
int sz=siseof(arr)/sizeof(arr[0]);
int *p=arr;
for(i=0;i<sz;i++}
{
printf("%d",*p);
p=p+1;
}
return 0;
}
同理,我们也可以跳着打印,只需要将p++(p=p+1)换为p+=2
我们也可以倒着打印,只需要将for循环改为for(i=sz;i>0;i--)
所以,指针+- 整数,只是将访问的位置向前或向后跳跃
指针-指针,指针是一个地址,那么地址-地址是个什么东西呢?我们可以验证一下
#include<stdio.h>
int main()
{
int arr[10]={1,2,3,4,5,6,7,8,9,10};printf("%d\n",&arr[9]-&arr[0]); //把它打印出来看看
return 0;
}
我们发现结果为9,恰好为9-0的值,这是不是巧合呢? 当然不是,指针-指针 得到的值就是两指针间元素个数(大地址-小地址,且在同一空间中)
说到元素个数,我们会想到strlen可以求字符串长度,那么我们可不可以利用指针-指针来实现这一目的?但愤然可以。
//用指针-指针实现strlen的作用(求字符串长度,即元素个数)
#include<stdio.h>
int my_strlen(char* str)
{
char* start = str;
char* end = str;
while(*end != '\0') //‘\0’是字符串结束的标志
{
end++;
}
return end - start;
}
int main()
{
char arr[] = "hello world";
int len = my_strlen(arr);
printf("%d\n", len);
printf("%d\n", strlen("hello world"));
return 0;
}
结果果然相同
指针的关系运算
for ( vp = & values [ N_VALUES ]; vp > & values [ 0 ];){*-- vp = 0 ;}
5. 指针和数组
printf("%p\n",数组名); 这里的地址是首元素的地址,地址可以放在指针变量中
printf("%p",&数组名[0]); 与上边两者相同
&数组名,不是首元素的地址,数组名表示整个数组,&数组名 取出的是整个数组的地址
同理,sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小
#include<stdio.h>
int main()
{
int arr[10]={0};
int *p=arr;
int i=0;
int sz=sizeof(arr)/sizeof(arr[0]);
for(i=0;i<sz;i++)
{
*p++=i+1;
}
p=arr;
for(i=0;i<sz;i++)
{
printf("%d",*p);
p++;
}
return 0;
}
当然也可以写为:
for ( i=0 ; i<sz ; i++)
{
*(p+1) = i +1;
}
指针非常灵活,只要找到一个地址,它后面的地址都可以访问
arr [ i ] 与 *(i+arr)等价,也可以写为*(i+arr), i [arr],但一般不用
6. 二级指针
例如:int * pa = &a; 一级指针
那么,int * *p pa = &pa; 二级指针
第一个*表示类型 ,第二个*表示指针,ppa就是一个二级指针变量 ( 用来存放一级指针变量地址)
同理, int * * * p p paa =& p pa; 三级指针……
与一级指针相同,我们可以将二级指针解引用,可以用代码进行演示
//二级指针解引用
#include<stdio.h>
int main()
{
int a = 5;
int* pa = &a;
int** ppa = &pa;
**ppa = 27;
printf("%d\n", **ppa);
printf("%d\n", a);
return 0;
}
证明二级指针可以解引用
7. 指针数组(本质是数组)与 数组指针(本质是指针)
指针数组:存放指针的数组
#include<stdio.h>
int main()
{
int a=10;
int b=20;
int c=30;
int d=40;
// int *pa=&a;
//int *pb=&b;
//int *pc=&c;
// int *pd=&d
//一个一个罗列很麻烦,它们都是指针,可以用一个数组来存放指针
//有整型数组(存放整形),字符数组(存放字符)也有指针数组(存放指针)
int *arr[4]={&a,&b,&c,&d}; //指针数组:存放指针的数组
int i=0;
for(i=0;i<4;i++)
{
printf("%d ", *( arr[i] ) );
}
return 0;
}
练习:利用指针使用一维数组模拟二维数组
如果每个元素都是指针,数组名表示首元素地址
int a[]={1,2,3,4};
int b[]={2,3,4,5};
int c[]={3,4,5,6};
int *arr[3] = {a,b,c};
#include<stdio.h>
int main()
{
int a[] = { 1,2,3,4 };
int b[] = { 2,3,4,5 };
int c[] = { 3,4,5,6 };
int* arr[3] = { a,b,c };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 4; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}