一、指针
指针理解的2个要点:
1. 指针是内存中一个最小单元的编号,也就是地址
2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
总结:指针就是地址,口语中说的指针通常指的是指针变量。
内存---电脑上的存储设备
&a---对a取地址,取出a的地址
a是一个整型变量,整型变量占用四个字节,每个字节都有地址
&a取出的是第一个字节的地址(较小的地址)
指针变量 我们可以通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个变量就是指针变量
32位机器,32根地址线,32比特,32/8=4字节,指针变量大小是4个字节
每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G的空间进行编址。
64位机器,64根地址线,64比特,64/8=8字节,指针变量大小是8个字节
在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
总结:
指针变量是用来存放地址的,地址是唯一标示一个内存单元的。
指针的大小在32位平台是4个字节,在64位平台是8个字节。
二、指针的类型
char* 类型的指针是为了存放 char 类型变量的地址。
short* 类型的指针是为了存放 short 类型变量的地址。
int* 类型的指针是为了存放 int 类型变量的地址。
(1)首地址都相同
(2)跳过的字节不同
1.char类型
2.int类型
跳过4
指针类型其实是有意义的
1. 指针类型决定了,指针进行解引用操作的时候,一次性访问几个字节,访问权限的大小
如果是char*的指针,解引用访问1个字节
如果是int*的指针,解引用访问4个字节
float* ----------------- 4个字节
2. 指针类型决定指针的步长(指针+1到底跳过几个字节)
字符指针+1,跳过1个字节
整型指针+1,跳过4个字节
三、指针的计算
1.指针+-整数
//首地址相同
//char类型+1
//int类型+4
2.指针-指针
前提:两个指针要指向同一块空间
指针-指针的绝对值得到的是两个指针之间的元素个数
EG:求字符串的长度
//写一个函数求一个字符串的长度
int my_strlen(char* str)
{
int count = 0;
while (*str != '\0')
{
count++;
str++;
}
return count;
}
int my_strlen(char* str)
{
char* start = str;
while (*str != '\0')
{
str++;
}
return str - start;
}
int my_strlen(char* str)
{
char* start = str;
while (*str++ != '\0')
;
return str - start - 1;
}
int my_strlen(char* str)
{
char* start = str;
while (*str != '\0')
{
str++;
}
return str - start;
}
int main()
{
char arr[] = "abcdef";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
3.指针的关系运算
for(vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;
}
代码简化, 这将代码修改如下:
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
*vp = 0;
}
实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
四、野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
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;
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
2.规避野指针的方法
1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放,及时置NULL
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性
int main()
{
int a = 10;
int* pa = &a;//指针的初始化
int* p = NULL;//NULL - 空指针,专门用来初始化指针
if (p != NULL)//指针的运用
{
}
return 0;
}
五、指针和数组
可见数组名和数组首元素的地址是一样的。
结论:数组名表示的是数组首元素的地址。
六、二级指针
1.指针变量也是变量,是变量就有地址,那指针变量的地址存放的地方----这就是二级指针 。
int main()
{
int a = 10;
int * pa = &a;//pa是一级指针变量
int* * ppa = &pa;//ppa就是一个二级指针变量
**ppa = 50;
printf("%d\n", **ppa);
printf("%d\n", a);
//int** * pppa = &ppa;//pppa是一个三级指针变量
return 0;
}
对于二级指针的运算有:
*ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是 pa .
int b = 20; *ppa = &b;//等价于 pa = &b;
**ppa = 30; //等价于*pa = 30; //等价于a = 30;
int main()
{
int a = 10;
int b = 20;
int c = 30;
int d = 40;
int e = 50;
int* arr[5] = {&a, &b, &c, &d, &e};
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d\n", *(arr[i]));
}
for (i = 0; i < 5; i++)
{
printf("%p\n", arr[i]);
}
return 0;
}
2.使用一维数组,模拟一个二维数组
#include <stdio.h>
int main()
{
//假设我想模拟出一个3行4列的数组
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");
}
}
七、指针数组
指针数组是数组。
是存放指针的数组。
数组我们已经知道整形数组,字符数组。
int arr1[5]; char arr2[6];
int* arr3[5];//指针数组
arr3是一个数组,有五个元素,每个元素是一个整形指针。