一、指针基础
指针的概念
地址表示内存单元的编号,也被称为指针。指针既是地址,也是一种专门用于处理地址数据的数据类型。
例如,变量a的地址或者十六进制表示的0x1000都可以视作指针。
指针变量的定义
语法:基类型 * 指针变量名。
基类型可以是之前学过的各种数据类型,如整型、浮点型、字符型、结构体类型、函数类型等,它表示指针所指向的内存空间能够存放的数据类型。例如:int *p; 中,p就是一个指向整型数据的指针变量。
*在定义时表示定义的是指针类型的变量。
指针变量名需符合标识符命名规则。
指针类型
例如:在int *p;中,int *整体被称为指针类型,表示指向整型数据的指针类型。
指针变量的引用
例如:int a = 10; int *p = &a; ,p指向 a 。
*是指针运算符,为单目运算,运算对象只能是指针(地址)。
*p表示访问 p 所指向的基类型的内存空间,这是一种间接访问。其过程包括:首先拿出 p 中的地址,在内存中定位;然后偏移出 sizeof(基类型) 大小的一块空间;最后将偏移出的这块空间当作一个基类型变量来看。
例如,*p 的运算效果相当于直接访问的变量 a 。
类似地,对于int a; ,a的数据类型是 int ,&a的数据类型是 int* ,对于 float b; ,&b的数据类型是 float* 。
指针变量的初始化
如果指针变量没有初始化,此时的值是随机值(野指针)。
初始化可以让指针变量有明确的指向,例如:int a; int *p = &a; 或者 int *p = NULL(NULL 表示空指针,即 0 号地址)。
赋值
例如:int *p; p = &a;
定义多个指针变量
例如:int *p, *q; 表示定义了两个指针变量。
注意:定义时候的 * 是修饰变量名的,表示定义的是一个指针类型的变量。
为什么需要指针指针
指针可以实现被调函数修改主调函数中的数据。
二、指针作为函数的参数
1、形参是指针类型的变量,用来接收实参(实参是要操作的内存空间的地址)。
2、实参:要修改谁,就把谁的地址传进去,但要保证空间有效。
3、注意:被调函数中一定要有*p的运算(间接访问的操作)。
4、值传递只是实参数据赋值给了形参,而地址(指针传递)传递的是地址,可以实现被调修改主调。
练习:
#include <stdio.h>
// 函数:实现两个数相加,并将结果存储在指针所指的变量中
void ADD(int *a, int b)
{
*a = *a + b; // 将指针 a 所指变量的值与 b 相加,并将结果存储回指针所指的变量
}
// 函数:找出两个数中的最大值和最小值,并通过指针返回
void maxandmin(int a, int b, int *max, int *min)
{
*max = a > b? a : b; // 如果 a 大于 b,将 a 赋值给指针 max 所指的变量,否则将 b 赋值给它
*min = a < b? a : b; // 如果 a 小于 b,将 a 赋值给指针 min 所指的变量,否则将 b 赋值给它
}
// 函数:交换两个指针所指变量的值
void swap(int *a, int *b)
{
int temp = *a; // 临时变量 temp 存储指针 a 所指变量的值
*a = *b; // 将指针 b 所指变量的值赋给指针 a 所指的变量
*b = temp; // 将临时变量 temp 的值赋给指针 b 所指的变量
}
// 主函数
int main()
{
int a = 2; // 定义并初始化变量 a 为 2
int b = 3; // 定义并初始化变量 b 为 3
int max; // 定义变量用于存储最大值
int min; // 定义变量用于存储最小值
// ADD(&a,b); // 调用 ADD 函数,将 b 的值加到 a 上
// printf("sum = %d\n",a); // 输出相加后的结果
// maxandmin(a,b,&max,&min); // 调用 maxandmin 函数找出 a 和 b 的最大值和最小值
// printf("max = %d, min = %d\n",max,min);
printf("a = %d b = %d\n",a,b); // 输出原始的 a 和 b 的值
swap(&a,&b); // 调用 swap 函数交换 a 和 b 的值
printf("a = %d b = %d\n",a,b); // 输出交换后的 a 和 b 的值
return 0;
}
三、指针与一维整型数组
1、数组名代表数组首元素的地址,就是数组所在空间的首地址。例如:定义一个指向整型数组的指针变量可以写成 int *p = a; 或者 int *p = &a[0]; ,表示 p 指向了数组 a 。
2、数组名(数组首元素的地址)和 &a[0] 等价。
3、*p 等价于 a[0] 。
4、指针的运算:
&:取地址运算符。
*:解引用运算符。
p + 1:指针的算术运算,其偏移量取决于指针所指向的数据类型的大小。
指针的比较:
> >= < <= == !=
可以比较两个指针的大小或相等关系,但前提是这两个指针指向同一个数组或具有相同的基类型。
#include <stdio.h>
// 函数:打印数组元素
void printfArray(int *begin, int *end)
{
// 只要起始指针不超过结束指针,就持续执行循环
while (begin <= end)
{
// 打印起始指针所指向的元素,并将起始指针向后移动一位
printf("%d ",*begin++);
}
// 换行,使输出更清晰
printf("\n");
}
// 函数:找出数组中的最大值
int maxArray(int *a, int len)
{
int i; // 定义循环变量
// 初始化最大值为数组的第一个元素
int max = *a;
// 遍历数组的每一个元素
for (i = 0; i < len; i++)
{
// 如果当前最大值小于数组中的某个元素
if (max < *(a + i))
{
// 更新最大值为该元素
max = *(a + i);
}
}
// 返回最大值
return max;
}
// 函数:反转数组元素的顺序
void reversedOrder(int *begin, int *end)
{
int temp; // 定义临时变量用于交换元素
// 只要起始指针小于结束指针,就持续执行循环
while(begin < end)
{
// 交换起始指针和结束指针所指向的元素
temp = *begin;
*begin = *end;
*end = temp;
// 起始指针向前移动一位
begin++;
// 结束指针向后移动一位
end--;
}
}
// 函数:选择排序
void choieSort(int *begin, int *end)
{
int *p = begin; // 定义指向起始位置的指针
int *q = NULL; // 定义用于遍历的指针
// 外层循环控制排序的轮数
for (p = begin; p < end; p++)
{
// 内层循环在每一轮中找出最小元素
for (q = p + 1; q <= end; q++)
{
// 如果当前元素大于后面的元素
if (*p > *q)
{
// 交换两个元素
int t = *p;
*p = *q;
*q = t;
}
}
}
}
// 函数:冒泡排序
void bubbleSort(int *begin, int *end)
{
int *p = begin; // 定义指向起始位置的指针
int *q = NULL; // 定义用于遍历的指针
// 外层循环控制排序的轮数
for (p = end; p > begin; p--)
{
// 内层循环在每一轮中比较相邻元素
for (q = begin; q < p; q++)
{
// 如果当前元素大于下一个元素
if (*q > *(q + 1))
{
// 交换两个相邻元素
int t = *q;
*q = *(q + 1);
*(q + 1) = t;
}
}
}
}
// 函数:插入排序
void insertSort(int *begin, int *end)
{
int *p = begin; // 定义指向起始位置的指针
int *q = NULL; // 定义用于遍历的指针
// 从第二个元素开始遍历
for (p = begin + 1; p <= end; p++)
{
int t = *p; // 保存当前要插入的元素
q = p; // 记录当前位置
// 寻找插入位置
while (q > begin && *(q - 1) > t)
{
// 将较大的元素向后移动
*q = *(q - 1);
q--; // 指针向前移动
}
// 插入元素
*q = t;
}
}
// 函数:折半查找
int * binaryFind(int *begin, int *end, int n)
{
int *mid = NULL; // 定义指向中间位置的指针
int *ret = NULL; // 定义用于存储查找结果的指针
// 只要查找范围存在(起始指针小于等于结束指针)
while (begin <= end)
{
// 计算中间位置
mid = begin + (end - begin) / 2;
// 如果要查找的值小于中间值
if (n < *mid)
{
// 在左半部分继续查找
end = mid - 1;
}
// 如果中间值小于要查找的值
else if (*mid < n)
{
// 在右半部分继续查找
begin = mid + 1;
}
// 如果找到匹配的值
else
{
// 记录找到的位置
ret = mid;
break; // 退出循环
}
}
// 返回查找结果,如果未找到则为 NULL
return ret;
}
// 函数:折半查找(递归)
int *binaryFindR(int *begin, int *end, int n)
{
int *mid = begin + (end - begin) / 2; // 计算中间位置
int *ret = NULL; // 定义用于存储查找结果的指针
// 如果起始指针大于结束指针,说明未找到
if (begin > end)
{
return NULL;
}
// 如果要查找的值小于中间值
if (n < *mid)
{
// 在左半部分递归查找
end = mid - 1;
ret = binaryFindR(begin, end, n);
}
// 如果中间值小于要查找的值
else if (*mid < n)
{
// 在右半部分递归查找
begin = mid + 1;
ret = binaryFindR(begin, end, n);
}
// 如果找到匹配的值
else if (*mid == n)
{
// 记录找到的位置
ret = mid;
}
// 返回查找结果,如果未找到则为 NULL
return ret;
}
// 函数:交换两个指针所指向的值
void swap(int *a, int *b)
{
int t = *a; // 临时存储指针 a 所指向的值
*a = *b; // 将指针 b 所指向的值赋给指针 a 所指向的位置
*b = t; // 将临时存储的值赋给指针 b 所指向的位置
}
// 函数:快速排序
void quickSort(int *begin, int *end)
{
// 记录起始位置和结束位置
int *p = begin;
int *q = end;
// 选择起始位置的元素作为基准值
int *k = begin;
// 只要起始指针小于结束指针,就持续执行循环
while (begin < end)
{
// 从右向左找小于基准值的元素
while(begin < end && *end >= *k)
{
end--;
}
// 从左向右找大于基准值的元素
while (begin < end && *begin <= *k)
{
begin++;
}
// 交换找到的两个元素
swap(begin, end);
}
// 如果起始指针和结束指针相遇
if (begin == end)
{
// 将基准值与相遇位置的元素交换
swap(k, begin);
}
// 如果起始位置到结束位置减 1 的范围内还有元素,继续排序
if (p < end - 1)
{
quickSort(p, end - 1);
}
// 如果起始位置加 1 到结束位置的范围内还有元素,继续排序
if (begin + 1 < q)
{
quickSort(begin + 1, q);
}
}
// 主函数
int main()
{
int a[] = {10,2,8,5,4,6,7,3,9,1}; // 定义并初始化整数数组
int len = sizeof(a)/sizeof(a[0]); // 计算数组的长度
/*printf("max = %d\n",maxArray(a,len));
printfArray(a,a+len-1); //可以改变打印的起始位置和结束位置
reversedOrder(a,a+len-1);
printfArray(a,a+len-1);*/
//choieSort(a,a+len-1);
//bubbleSort(a,a+len-1);
printfArray(a,a+len-1);
//insertSort(a,a+len-1);
//printfArray(a,a+len-1);
/*int n;
scanf("%d",&n);
//int *ret = binaryFind(a,a+len-1,n);
int *ret = binaryFindR(a,a+len-1,n);
if (ret==NULL)
printf("no found\n");
else
printf("found\n");
*/
quickSort(a,a+len-1); // 对数组进行快速排序
printfArray(a,a+len-1); // 打印排序后的数组
return 0;
}
快速排序的思路