指针在C语言中是非常重要的概念。
内存的最小单元是1字节。
32位系统中的指针变量大小是始终4个字节,64位系统中的指针变量大小是始终8字节。
在C语言中,可以通过以下方式使用指针变量
#include
int main()
{
int a = 10;
int* p = &a; // 这里p就是一个指针变量,存放的是a在内存中的地址
return 0;
}
指针类型分为以下几种
char *pc;
声明一个指向字符类型的指针变量int *pi;
声明一个指向整数类型的指针变量short *ps;
声明一个指向短整数类型的指针变量long *pl;
声明一个长整数类型的指针变量float *pf;
声明一个单精度浮点数类型的指针变量double *pd;
声明一个双精度浮点数类型的指针变量以下用char*
和int*
作为例子简述指针类型之间的区别
// 区别1: char*存放char类型变量的地址,int*存放int类型变量的地址
#include
int main()
{
char str = 'a';
char* pc = &str; // 这里pc是str在内存中的地址
*pc = 'b';
printf("%c\n", str); // b
int* pi = &str; // 这里会有一个warnning,提示str是char类型,用int*不兼容
*pi = 10; // err
printf("%d\n", str);
return 0;
}
/*
区别2:
char*类型指针加1是往后移一个字节,因为对应的char是占一个字节,
int*类型指针加1是往后移4个字节,因为对应的int是占4个字节
*/
#include
int main()
{
int n = 10;
int* pi = &n;
printf("%p\n", pi); // 00B9F954
printf("%p\n", pi + 1); // 00B9F958 跟上面相差4个字节
char str = 'a';
char* pc = &str;
printf("%p\n", pc); // 012FFA53
printf("%p\n", pc + 1); // 012FFA54 跟上面相差1个字节
return 0;
}
总结:存放不同类型变量的地址,应该使用与之对应类型的指针变量。
指针+n = 原指针地址 + (指针对应的数据类型的字节长度 * n)
以下用char*
和int*
作为例子
#include
int main()
{
int n = 10;
int* pi = &n;
printf("%p\n", &n); // 0057FEB0
printf("%p\n", pi); // 0057FEB0
printf("%p\n", pi + 2); // 0057FEB8 = 0057FEB0 + (4 * 1)
char str = 'a';
char* pc = &str;
printf("%p\n", &str); // 0057FE9B
printf("%p\n", pc); // 0057FE9B
printf("%p\n", pc+3); // 0057FE9E = 0057FE9B + (1 * 3)
return 0;
}
总结:指针对应的数据类型是指针移动的距离的关键因素。
每个类型的指针操作的空间不同
以下用char*
和int*
作为例子
#include
int main()
{
int n = 0x11223344; // 这里使用的是16进制,也可以使用10进制 287454020
int* pi = &n;
char* pc = (char*)&n;
// 这里&n在内存监视中是这样的 0x001DF970 44 33 22 11
*pi = 0; // 这里操作完之后,是这样的 0x001DF970 00 00 00 00
n = 0x11223344; // 0x001DF970 44 33 22 11
*pc = 0; // 这里操作完之后,是这样的 0x001DF970 00 33 22 11
return 0;
}
总结:指针操作的空间是这个指针对应的数据类型的大小,例如char*
就是4个,int*
就是1个。
野指针是指指向未知存储位置的指针。
指针未初始化
int main()
{
int* p; // 这里创建了指针却未初始化,所以它默认是随机值
*p = 10; // error
return 0;
}
指针越界访问
/*
这个数组长度为10,但是在for循环的时候指针最后一次超出了10,造成了指针越界访问
*/
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
for (int i = 0; i < sz + 1; i++)
{
*p = i; // 在最后一次循环的时候,越界了
p++;
}
return 0;
}
指针指向的空间被释放
#include
int* test()
{
int a = 10;
return &a;
}
int main()
{
int* p = test(); // 此时p指向的是a的地址,但是a在test函数结束之后就被回收了,所以此时p就是野指针了。
return 0;
}
指针创建的时候先初始化:int* p = NULL;
避免指针越界访问
在使用指针的时候,先置空,使用时再判断一下是否是NULL
#include
int main()
{
int* p = NULL;
if (p != NULL) // 这里判断一样
{
*p = 1;
}
return 0;
}
避免函数返回变量的地址
指针±整数改变指针的地址
指针-指针算出字符串的长度
#include
int main()
{
int arr[10] = { 0 };
int* p0 = &arr[0];
int* p6 = &arr[6];
printf("%d\n", p6 - p0); // 6
return 0;
}
// 这里使用指针-指针写一个函数算出字符串的长度
int my_strlen(char* arr)
{
char* p = arr; // 记录下开始的位置
while (*arr != '\0')
{
arr++;
}
// 这个结束之后说明arr到了字符串的末尾的位置
return arr - p;
}
指针的关系运算符
#include
int main()
{
int arr[2] = { 0 };
int* p = arr;
*p++ = 1; // 这里其实就是*p=1;p++;
return 0;
}
除以下两种情况下不是,其他情况下数组名表示的是数组首元素的地址
sizeof(arr)
这里的数组是代表的整个数组&arr
这里也是整个数组
以下代码验证数组名代表的是不是数组首元素的地址
#include
int main()
{
int arr[5] = { 0 };
printf("数组名地址=%p\n", arr); // 数组名地址 = 012FFB30
printf("数组首元素地址=%p\n", &arr[0]); // 数组首元素地址 = 012FFB30
return 0;
}
验证*(arr + i) = &arr[i]
#include
int main()
{
int arr[5] = { 0 };
int* p = arr;
for (int i = 0; i < 5; i++)
{
printf("&arr[%d]=%p-------*(arr+%d)=%p\n", i, &arr[i], i, p + i);
}
return 0;
}
/*
&arr[0]=010FFAC4-------*(arr+0)=010FFAC4
&arr[1]=010FFAC8-------*(arr+1)=010FFAC8
&arr[2]=010FFACC-------*(arr+2)=010FFACC
&arr[3]=010FFAD0-------*(arr+3)=010FFAD0
&arr[4]=010FFAD4-------*(arr+4)=010FFAD4
*/
练习:使用指针对数组进行赋值
#include
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr; // 这里将p指向数组首元素地址
for (int i = 0; i < sz; i++)
{
*(p + i) = i;
}
// 最后数组为{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
return 0;
}
指向指针变量地址的指针就是二级指针
int main()
{
int a = 10;
int* pa = &a; // 一级指针
int** ppa = &pa; // 二级指针
*(*paa) = 30; // *(*ppa) = *pa = a --> a = 30
return 0
}
/*
int * p: 这里的 *代表指针,左边代表指向的是int类型,右边是指针变量名p
int* * pp: 这里的*代表指针,左边代表指向的是int*类型,右边是指针变量名pp
*/
当出现很多变量都需要创建指针的时候,可以使用指针数组来存储对应的变量。
#include
int main()
{
int a = 10;
int b = 20;
int c = 30;
int* pa = &a;
int* pb = &b;
int* pc = &c;
// 上面三行这样写的话感觉有点繁琐,所以采用下面的指针数组来书写
int* arr[3] = { &a, &b, &c };
*(arr[0]) = 1;
*(arr[1]) = 2;
*(arr[2]) = 3;
printf("%d %d %d\n", a, b, c); // 1 2 3
return 0;
}
用指针数组模拟二维数组
#include
int main()
{
int arr1[3] = { 1, 2, 3 };
int arr2[3] = { 11, 22, 33 };
int arr3[3] = { 111, 222, 333 };
int* arr[3] = { arr1, arr2, arr3 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
printf("%d\t", arr[i][j]);
}
printf("\n");
}
return 0;
}