指针的深入理解
文章目录
前言
哈喽,各位小伙伴大家好!好久不见,今天小编继续带着大家深入理解指针。今天的内容可能会有点难理解,不过走上坡路总是吃力的!加油,向大厂冲锋!杀gigi!
一.一维数组传参的本质
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void test(int *arr)//接收数组首元素地址
{
int len1 = sizeof(arr) / sizeof(arr[0]);//arr数组首元素的地址,64位环境下指针大小为8字节
printf("%d", len1);// 首元素int类型4个字节 8/4=2
}
int main()
{
int arr[10] = { 0 };
int len = sizeof(arr) / sizeof(arr[0]);//sizeof(数组名)计算的是整个数组的大小=40
printf("%d\n", len);// 首元素int类型4个字节 40/4=10
test(arr);//arr数组名,没有&,也没有单独放在sizeof里,是数组首元素地址
return 0;
}
大家看一下这段代码,大家觉得代码输出的结果是什么?有人会觉得都是数组的大小/数组受元素的大小==40/4=10。那结果就是两个10。是不是呢?
大家可以看到结果是一个10,一个2。那2是咋来的呢?这里就涉及到了一维数组传参的本质。test(arr)这里的数组名没有&,也没有单独放在sizeof里面,所以这里的arr是数组首元素的地址。所以test函数的sizeof(arr)这里的arr并不是数组的地址,是数组首元素的地址。地址就是指针,指针的大小在64位环境下是8个字节。8/4=2。所以一维数组传参的本质传的是数组首元素的地址,写成数组接收是为了方便理解,实际本质上传的是地址,用指针变量接收。
- 结论一:一维数组传参的本质是传数组首元素的地址。
二.冒泡排序(优化版)
冒泡排序
大家可以看到, 冒泡排序的思想就是进行 n-1躺的排序, 每趟排序从左向右或从右到左进行 相邻两数的比较交换, 满足条件就交换,每次比较后两个比较数字向左或向右 移动一位。每趟进行 n-1-i次排序,每趟排序将 最大或最小的数排序好。所以我们用 for循环控制趟数,里面嵌套一层 for循环控制每趟比较次数, if判断是否满足,满足就叫 交换即可。
但是像这样的数组 无需排序,如果按照 原来的思路仍会按部就班的排序,所以我们可以对他 进行优化。用 flag做标记, 判断是否数组 本身就是有序,如果是就直接 break跳出即可。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int count = 0;
void bubble_sort(int arr[],int sz)
{
for (int i = 0; i < sz - 1; i++)//控制趟数
{
int flag = 0;
for (int j = 0; j < sz - 1 - i; j++)//控制比较次数
{
count++;//记录循环次数
if (arr[j] > arr[j + 1])//判断是否交换
{
flag = 1;
int tmp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = tmp;//交换
}
}
if (flag == 0)
break;//已经排好序直接结束循环
}
printf("count=%d\n", count);//输出循环次数
}
void printf_arr(int arr[], int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);//遍历打印数组
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
int sz = sizeof(arr) / sizeof(arr[0]);//数组长度
bubble_sort(arr,sz);
printf_arr(arr, sz);
return 0;
}
这里我们用count变量记录循环次数,对比优化前后的循环次数。
- 优化前
- 优化后
大家可以看到优化前需要循环45次,优化后循环9次即可。这就是优化后的效果
三.二级指针
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
int a = 10;
int* p = &a;//一级指针
int** pp = &p;//二级指针
int*** ppp = &pp;//三级指针
printf("%d", ***ppp);//三级指针解引用三次,才能找到一级指针指向的变量
return 0;
}
指针变量也是变量,是变量就有地址,那指针变量的地址存在哪里呢?答案是存在二级指针。二级指针就是接收一级指针地址的指针。以此类推,三级指针就是接收二级指针地址的指针。
*说明这是一个指针,前面的int加上一个星号说明指针指向的是个一级指针,那么这个指针就是二级指针。大家可以发现有多少个星号就说明这是几级指针。
- 结论二:二级指针是存放一级指针地址的指针,有多少个星号说明是几级指针。
四.指针数组
4.1指针数组的概念
指针数组有数组又有指针,那他是指针还是数组呢?这里我们不妨类比一下。
这里我们就知道指针数组其实是数组,是一个每个元素为指针的数组,可以是字符指针数组,也可以是整形指针数组。那指针数组变量的类型是什么呢?
- 整形指针数组
- int* arr[10] 数组有十个元素,每个元素是一个整形指针。
- 字符指针数组
- char* arr[10] 数组有十个元素,每个元素是一个字符指针。
所以我们又可以得出一个结论:
- 结论三:指针数组是数组,每个元素是一个指针。
4.2指针数组模拟二维数组
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
int a[5] = { 1,2,3,4,5 };
int a1[5] = { 2,3,4,5,6 };
int a2[5] = { 3,4,5,6,7 };
int* arr[3] = { a,a1,a2 };//存放三个数组首元素的地址,是一个指针数组。
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);//arr[i]拿到第i个数组的首元素的地址,
}// arr[i][j]拿到第i个数组的首元素的地址后
printf("\n");// 访问该数组第j个元素。
}
return 0;
}
我们创建一个指针数组存放三个数组首元素的地址,所以arr[i]就能拿到数组的地址,arr[i][j]拿到数组地址后通过下标j来访问数组的每个元素。这里我们就实现了二维数组的模拟。
五.字符指针变量
5.1字符指针指向字符
字符指针前面我们已经有所提及了,一般使用情况是这样子的。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
char ch = 'w';
char* pc = &ch;
printf("%c\n", *pc);
*pc = 'q';
printf("%c\n", ch);
return 0;
}
那字符指针是不是只能指向单个字符呢?答案是否定的。
5.2字符指针指向字符串
字符指针不止可以指向单个字符,也能指向字符串。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
char* pc = "hello world";//放的是字符串首字符的地址
printf("%c\n", *pc);//打印第一个字符
return 0;
}
那这时的字符指针变量又该如何理解呢?是把整个字符串放在指针里面吗?不是的。我们可以吧字符串理解为一个字符数组,把数组名也就是数组首元素的地址赋给pc。
所以代码其实下面这个和这个的效果是一样的。
int main()
{
char arr[] = "hello world";//放的是字符串首字符的地址
char* pc = arr;
printf("%c\n", *pc);//打印第一个字符
return 0;
}
那这两个代码是不是等价的呢?注意这两个代码还是有区别的!第一段代码pc指向的是常量字符串,而第二段代码是把字符串放在数组中,数组是可以修改的。这里我们来验证一下。
大家可以看见,当我们解引用修改变量时直接报错。说明常量字符串是不可修改的。
- 结论四:字符串放在指针的是首字符的地址,常量字符串不可修改。
5.3代码练习
学以致用,接下来我们看一下这代码的结果。
int main()
{
char str1[] = "hello bit";
char str2[] = "hello bit";
char *str3 = "hello bit";
char *str4 = "hello bit";
if (str1 == str2)
printf("str1 and str2 are same\n");//1
else
printf("str1 and str2 are not same\n");//2
if (str3 == str4)
printf("str3 and str4 are same\n");//3
else
printf("str3 and str4 are not same\n");//4
return 0;
}
大家觉得这段代码的结果是啥呢?
发现结果是2和3。为什么呢?
数组有自己独立的空间,两个数组的空间是不同的地址也就不同。但是str3和str4指向的是两个相同内容的常量字符串,两份相同的常量字符串没必要保存两份,所以他们都指向同一块空间,都指向h的地址。
后言
今天的内容还是挺多的,需要大家多加消化。感谢大家的耐心阅读,今天就分享到这里,咱们下期间见!拜拜咯~