目录
C语言初阶指针一共分为七个模块
1.指针是什么 2.指针和指针类型 3.野指针 4.指针运算 5.指针和数组 6.二级指针 7.指针数组
一、指针是什么
指针理解的两个要点:
1.指针是内存中一个最小单元的编号,也就是地址
2.平时口语中所说的指针,通常是指指针变量,用来存放内存地址的变量
指针变量:
我们可以通过&取地址操作符取出变量的内存地址,把地址存放到一个变量当中,这个变量就是指针变量。
下面通过代码来演示
int a = 10; //在内存中开辟一块空间。
int* pa = &a; //这里我们对变量a,取出他的地址,可以使用&操作符。
a变量占用四个字节的空间,这里是将a的4个字节的第一个字节的地址存放在pa变量中,pa就是一个指针变量
二、指针和指针类型
指针和指针类型通过一些具体的代码来介绍
//当有这样的代码:
int num = 10;
int* p = #
//指针变量相应的类型
char c = 'w';
char* pc = &c;
printf("%d\n", sizeof(p));
printf("%d", sizeof(pc));
int* pi = NULL;
short* ps = NULL;
long* pl = NULL;
float* pf = NULL;
double* pd = NULL;
通过运行结果可以得到
无论是什么类型的地址其指针大小都是一样的
既然指针的大小一样,那么为什么还有不同类型的指针,不搞一个统一类型的指针 因为是 指针类型是有很重要的作用的
指针类型的作用
作用一:指针类型决定了,指针在被解引用的时候,访问的权限。
//整型指针解引用访问了4个字节
//字符指针解引用访问1个字节
//....
作用二:指针类型决定了指针向前或者向后一步走了多大距离。
代码示例:
//指针类型的作用
int a = 0x11223344;
int* pa = &a;
char* pc = (char*) & a;
*pa = 0;
// int* + n ----> +n*sizeof(int)
// char* + n ----> +n*sizeof(char)
/*int arr[10] = { 0 };
char* p = (char*)arr;*/
//假设你希望访问这40个字节
int i = 0;
for (i = 0; i < 40; i++)
{
*p = 'x';
p++;
}
三、野指针
概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的) ------------> 野狗
1.野指针的成因:
1.指针未初始化
int* p ;//局部变量指针未初始化,默认为随机值
*p = 20;
2.指针越界访问
int arr[5] = { 1,2,3,4,5 };
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%p", *p);
p++;
}
//越界之后指针就会变成野指针。
3.指针指向的空间被释放
#include<stdio.h>
int* test()
{
int a = 10;
printf("%d\n", a);
return &a;
}
int main()
{
int *p=test();//这边用一个指针来接收这个test()
*p = 100;//这时候a的地址已经被释放了,再去用*p的话就会变成野指针。
return 0;
}
2.如何规避野指针
1.指针的初始化 2.小心指针越界 3.指针指向空间释放及时置NULL 4.避免返回局部变量的地址 5.指针使用之前检查有效性 //如果指针为空指针就不用
int a = 10;
int* p = &a;
int* q = NULL; //0地址,是不允许被访问的
if (q != NULL)
{
//....这时候才能去访问q
}
四、指针的运算
1.指针+-整数 2.指针-指针 3.指针的关系运算
1、指针+整数
const int n = 5;
float values[n];
float* vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[n];)
{
*vp++ = 0; //把*vp置使成0之后vp++
//指针加一 会跳过一个float类型的元素
}
2、指针-指针
|指针-指针| 得到的是指针和指针之间元素的个数
前提:两个指针必须指向同一块空间
int arr[10] = { 0 };
printf("%d\n", &arr[9] - &arr[0]);
//例子:定义一个属于自己的求字符串长度的函数
char arr[] = "abcdef";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
3.指针关系运算
/*for (vp = &values[5]; vp >= &values[0];)
{
*--vp = 0; //先-- 再解引用 赋值成0
}*/
//代码的简化
for (vp = &values[5]; vp >= &values[0];vp--)
{
*vp = 0; //先-- 再解引用 赋值成0 但是还是要避免中情况的代码出现
}
上述简化代码,实际上在绝大部分的编译器上使可以顺利完成的,然而我们还是应该避免这样写,因为标准并不保证它可行。
标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针进行比较,但不允许与指向第一个元素之前的那个内存位置的指针进行比较。
五、指针和数组
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
printf("%p\n", arr);
printf("%p\n", &arr[0]);//数组名就是首元素的地址
但是
printf("%d\n", sizeof(arr)); //这里面的大小是40,并不是一个首元素指针的地址
说明,数组名是首元地址。但是有两个例外
1.sizeof(数组名),数组名表示的是整个数组,计算的是整个数组的大小。
2.&数组名,数组名表示整个数组,取出的是整个数组的地址.
指针与数组的关系
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
//下标 0 1 2 3 4 5 6 7 8 9
int* p = arr;//既然arr代表首元素的地址,那么将其赋给指针p之后说明p也有能力去访问数组后面的内容。
//用指针打印数组;数组在内存当中是连续存放的
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));//p+i表示第i个整型的下标,从0跳过i个整型,*(p+i)表示解引用,输出内容。
}
数组和指针不是一个东西,可以通过指针来访问数组
六、二级指针
int a = 10;
int* pa = &a;//pa是个指针变量,是个一级指针
int** ppa = & pa;//ppa是一个二级指针。
**的解释
//用ppa找到a *ppa ----> pa ; *pa ----> a ;
// * *ppa ----> a;
//多级指针同理
七、指针数组
数组指针的定义形式
dataType *arrayName[length];
定义
如果一个数组中的所有元素保存的都是指针,那么我们就称它为指针数组
int arr[5];//整型数组 - 存放整型的数组
char ch[6];//字符数组 - 存放字符的数组
int a = 30;
int b = 35;
int c = 40;
int d = 45;
int e = 50;
int* arr2[5] = {&a,&b,&c,&d,&e}; //指针数组 - 存放指针的数组
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", *(arr2[i]));
}
整改
int data1[] = { 1,2,3,4,5 };
int data2[] = { 6,7,8,9,10 };
int data3[] = { 11,12,13,14,15 };
int data4[] = { 16,20,30,45,55 };
//arr就是指针数组
int* arr[4] = { data1,data2,data3,data4 };
//打印
int i = 0;
for (i = 0; i < 4; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
///printf("%0.2d ", arr[i][j]); //模拟的二维数组,加上[]之后就不需要解引用了,因为[]本身访问数组的某个元素,相当于解引用了。
printf("%0.2d ", *(arr[i]+j));
}
printf("\n");
}
return 0;