目录
1. 指针是什么?
我们通过使用&(取地址操作符)取出变量内存的起始地址,把地址存放到一个变量里面去,这个变量就叫做指针变量。
#include <stdio.h>
int main()
{
int a = 10;//在内存中开辟一块空间
int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
//a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量
中,p就是一个之指针变量。
return 0; }
那这里的问题是:一个小的单元到底是多大?(1个字节)如何编址?经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。对于 32 位的机器,假设有 32 根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电 平(低电压)就是(1或者 0 );那么32根地址线产生的地址就会是:
00000000 00000000 00000000 0000000000000000 00000000 00000000 00000001...11111111 11111111 11111111 11111111这里就有 2 的 32次方个地址。 //1bit=8byte,1byte=1024KB,1KB=1024MB,1GB=1024MB每个地址标识一个字节,那我们就可以给 ( 2^32Byte == 2^32/1024KB ==2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB ) 4G 的空闲进行编址。同样的方法,那 64 位机器,如果给 64 根地址线,那能编址多大空间,自己计算。这里我们就明白:在 32 位的机器上,地址是 32 个 0 或者 1 组成二进制序列,那地址就得用 4 个字节的空间来存储,所以 一个指针变量的大小就应该是4 个字节。那如果在 64 位机器上,如果有 64 个地址线,那一个指针变量的大小是 8 个字节,才能存放一个地址。总结:指针是用来存放地址的,地址是唯一标示一块地址空间的。指针的大小在 32 位平台是 4 个字节,在64位平台是8个字节。
2. 指针和指针类型
int num = 10;
p = #
在这里说明指针不管是整形指针还是字符指针他的大小都是一样的(4/8),那还定义不同的指针类型是为什么呢?
其实指针类型大有用处
#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; }
结果为
这说明指针类型决定了指针向前或向后走一步走多大的距离。
int *+1-->+1*sizeof(int)==+4
char *+1-->+1*sizeof(char)==+1
看下面这段代码
#include<stdio.h>
int main()
{
int a = 0x11223344;
int* pa = &a;
*pa = 0;
return 0;
}
调试看&a的情况
再下一步将*pa=0呢
再让我们看看若为字符指针操作的情况
#include<stdio.h>
int main()
{
int a = 0x11223344;
char* pc = &a;
*pc = 0;
return 0;
}
发现他只是改变了第一个字节的数值
这就说明了指针类型决定了,指针在被解引用的时候,访问的权限。
整形指针解引用访问4个字节。
字符指针解引用访问1个字节。
3. 野指针
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0; }
//若不知道该将指针初始化为什么可以给他一个空指针
//int *p= NULL;
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; }
运行后是这样的
3. 指针指向的空间释放
例如:
3.2 如何规避野指针
4. 指针运算
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0; }
vp<&values[N_VALUES]就是指针的关系运算
*vp++就是指针+
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%d\n", &arr[9] - &arr[0]);
return 0;
}
运行结果为
从而我们可以得知指针-指针得到的是中间元素的个数,所以我们就可以用它来试试实现一个strlen函数,代码如下:
#include<stdio.h>
my_strlen(char* str)
{
char* start = str;
while (*str)
{
str++;
}
return str - start;
}
int main()
{
char arr[] = "abcdef";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0; }
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--) {
*vp = 0; }
这样就比较好理解的,但是这段代码在部分编译器上是不能运行的,因为
标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
5. 指针和数组
先看一个例子
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
printf("%p\n", arr);
printf("%p\n", &arr[0]);
return 0; }
#include <stdio.h>
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,0};
int *p = arr; //指针存放数组首元素的地址
int sz = sizeof(arr)/sizeof(arr[0]);
for(i=0; i<sz; i++)
{
printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p+i);
}
return 0; }
结果为
所以 p+i 其实计算的是数组 arr 下标为i的地址。
6. 二级指针
7. 指针数组
#include<stdio.h>
int main()
{
int data1[5] = { 1,2,3,4,5 };
int data2[5] = { 2,3,4,5,6 };
int data3[5] = { 3,4,5,6,7 };
int* arr[3] = { data1,data2,data3 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
结果为
arr[i][j]也可以写成*(arr[i]+j)。
好了这就是C语言指针的初阶内容
下周我会努力将进阶也写一篇出来
如果对你有帮助麻烦点个赞
谢谢!