文章目录
1.指针是什么
以上是内存的布局,每个格子代表1个字节,如果想访问这些空间就先要对这些空间进行编号,0x00ff40就表示这个空间的编号,而在计算机内我们把这个编号也叫作地址,且访问这个内存空间需要指针去访问,所以 编号 == 地址 == 指针.
注:指针在使用时也需要开辟空间,而这个空间的大小为:32位机器上是4字节,64位机器上是8字节.
2.指针和指针类型
2.1 指针变量
#include<stdio.h>
int main()
{
int* pi = NULL;//整形的指针变量
char* pc = NULL;//字符型的指针变量
short* ps = NULL;//短整形的指针变量
long* pl = NULL;//长整形的指针变量
float* pf = NULL;//单浮点型的指针变量
double* pd = NULL;//双浮点型的指针变量
//我们把以上的变量称为指针变量
return 0;
}
int * pi = NULL;
//int表示这个变量是int类型的
//*表示这个变量是int* 类型的
//pi是这个指针的变量名为pi
//NULL表示0,指这个指针变量没有指向的地址,所以把这个空间置为NULL.
2.2 指针 ± 整数
#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;
}
从上面的运行结果可以得出:指针的类型决定了指针向前或向后走多大距离.
2.3 指针的解引用
#include<stdio.h>
int main()
{
int a = 10;
int* pi = &a;
*pi = 100;
printf("%d\n", *pi);
return 0;
}
利用&(取地址)将a的地址取出放到int*类型的pi变量里,然后通过解引用符号( * ),从pi存的地址找到变量a,最后改变a的值.
3.野指针
3.1 野指针的成因
3.1.1 指针未初始化
#include<stdio.h>
int main()
{
int* pi;//局部变量的指针未初始化,默认未随机值.
*pi = 20;
return 0;
}
不给指针变量初始化就像一条野狗没人给它拴起来一样,是很危险的!
3.1.2 指针越界访问
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* pi = arr;//因为数组名是数组的首元素地址,所以不用&
int i = 0;
for (i = 0; i < 11; i++)
{
*(pi++) = 0;
}
return 0;
}
数组只有10个元素但赋值时,i小于11,超出了数组的元素个素导致数组越界访问,从而编译器报错!
3.2 然后避免野指针
1.定义指针时,要给指针初始化,如果定义了指针但暂时不知道初始化什么,就把指针初始化为NULL,NULL表示为0;
2.使用数组时,要注意指针是否越界
3.指针指向的空间不用就置为NULL.
4.指针使用之前要检查有效性,检查指针是否初始化了.
4.指针运算
4.1指针 ± 整数
#include<stdio.h>
#define va 5
int main()
{
int arr[va];
int* pa;
for (pa = &arr[0]; pa < &arr[va];)
{
*pa++ = 0;
}
return 0;
}
利用指针加加遍历整个数组,实现整个数组的初始化.
4.2指针 - 指针
指针减指针就是减去这个指针指向内容的个数,如:利用指针减指针实现strlen函数.
#include<stdio.h>
int my_strlen(char* pa)
{
int count = 0;
while (*pa != '\0')
{
count++;
pa++;
}
return count++;
}
int main()
{
char arr[] = "abcdef";
int ret = my_strlen(arr);
printf("%d\n", ret);
return 0;
}
4.3 指针的关系运算
#include<stdio.h>
#define va 5
int main()
{
int arr[va];
int* pa;
for (pa = &arr[va]; pa < &arr[0];)
{
*--pa = 0;
}
return 0;
}
上面这个代码是大部分编译器是可以顺利运行的,但我们要避免这种写法,因为标准并不保证它可行.
标准规定了:
允许指向数组元素的指针与指指向数组最后的那个内存位置的指针进行比较,但不允许与指向第一个元素前面的那个内存位置进行比较.
下面是修改后的代码:
#include<stdio.h>
#define va 5
int main()
{
int arr[va];
int* pa;
for (pa = &arr[va]-1; pa >= &arr[0]; pa--)
{
*pa = 0;
}
return 0;
}
5.指针和数组
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;//指针存放数组的首元素地址
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
因为数组的地址是首元素,所以我们可以通过使用指针获取到数组的首元素地址从而打印整个数组.
6.二级指针
#include<stdio.h>
int main()
{
int n = 10;
int* pn = &n;
int** ppn = &pn;//此时的ppn为二级指针
*ppn = 50;
return 0;
}
//第一个*表示这个指针是int*类型的变量
//第二个*表示ppn是指针变量
int * * ppn = &pn
也就是说:指针变量pn存放的是n的地址,指针变量ppn存放的是pn的地址。
7.指针数组
大家先想一想指针数组是指针还是数组呢?
答案是数组,是一个存放指针的数组
如:
#include<stdio.h>
int main()
{
int arr[3] = { 1,2,3, };
int arr1[3] = { 4,5,6 };
int arr2[3] = { 7,8,9 };
int* pa[] = { arr,arr1,arr2 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 3; j++)
{
printf("%d ", pa[i][j]);
}
printf("\n");
}
return 0;
}
上面代码通过指针打印出一个相似的二维数组,但和二维数组不一样的是:二维数组是在内存中连续存放的,而指针打印出来的二维数组不是连续存放的.