指针是一个变量,它存储的是另一个变量的内存地址,而不是数据本身。通过指针可以间接访问和修改它所指向的变量。
数据类型 *指针变量名;
int *p; // 定义一个指向 int 类型变量的指针 p
char *q; // 定义一个指向 char 类型变量的指针 q
float *r; // 定义一个指向 float 类型变量的指针 r
关键操作符
-
取地址运算符 &:获取一个变量的内存地址。
-
解引用运算符 *:访问指针所指向地址中存储的值。
%p:地址格式说明符(以十六进制打印指针中存储的地址值。)
虽然%d也能打印地址但是会报警,虽然不会报错,如下:
注意事项
指针数组:
指针数组是指一个数组,其中的每个元素都是一个指针变量。它常用于存储多个地址(例如多个字符串、多个数组的起始地址等)。
数据类型 *数组名[数组长度];
-
数据类型:指针所指向的数据类型。
-
*:表示数组元素是指针。
-
数组名:标识符。
-
数组长度:元素个数。
p+i == &arr[i]
*(p + i)) == *(arr+i) == arr[i]
指针p通过加减1,来访问地址的值。
数组的值是通过下标也是索引来进行访问的。
数组就是数组,是一块连续的空间(数组的大熊啊和数组元素的个数和元素类型都有关系。)
指针(变量)就是指针(变量),是一个变量(4/8个字节)
数组名是地址,是首元素的地址
可以使用指针来访问数组
arr[i] == *(arr+i) == *(i+arr) == i[arr]
\] 下标引用符 相当于 加减 1+2 == 2+1 ,所以arr\[i\]==i\[arr\],但是一般都默认写为arr\[i\].

数组传参的本质是传递了数组首元素的地址,所以形参访问的数组和实参的数组是同一个数组的。
形参的数组是不会单独在创建数组空间的,所以形参的数组是可以省略掉数组大小的。
正确写法如下:

```cs
#include
void Print(int* p, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
}
int main()
{
int arr[5] = { 1,2,3,4,5 };
int sz = sizeof(arr) / sizeof(arr[0]);
Print(arr,sz);//arr数组名就是数组首元素的地址。
return 0;
}
```
## 冒泡排序
冒泡排序的核⼼思想就是:两两相邻的元素进⾏⽐较。

```cs
#include
void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{
int i = 0;
for (i = 0; i < sz - 1; i++)//5个数,进行4次排序
{
int j = 0;
for (j = 0; j < sz - i - 1; j++)//每一次排序确定一个数。
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
int main()
{
int arr[] = { 3,1,7,5,8,9,0,2,4,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
```

```cs
//⽅法2 - 优化
void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
int flag = 1;//假设这⼀趟已经有序了
int j = 0;
for (j = 0; j < sz - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
flag = 0;//发⽣交换就说明,⽆序
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
if (flag == 1)//这⼀趟没交换就说明已经有序,后续⽆序排序了
break;
}
}
int main()
{
int arr[] = { 3,1,7,5,8,9,0,2,4,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
```
## ⼆级指针
指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪⾥?

\&a:变量a的地址 0x100
p:指针p的值(存的就是\&a)0x100
\*p:就是值 10,因为p指向的是a的地址,\*p解引用后就得到了值。
\*pp :取pp指向的内容,就是p的值 0x100 pp指向的是p,所以\*pp就是解引用得到pp所指向的值,是p,而p是地址,想要得到数值,必须在解引用一次。
pp: 指针pp自己的地址 0x200
\*\*pp:两次解引用,得到a值。,此代码中把%p改为%d,就可得到10。
就相当于你想睡觉,得先进大门,在进卧室门,才能睡觉,而大门是pp,卧室门是p,睡觉是a,那么你在大门第一次解引用,得到的是你卧室门的地址,第二次解引用你才能睡觉;如果你直接在卧室门,那么你只需要解引用一次,就可以睡觉了。
## 指针数组模拟⼆维数组
```cs
#include
int main()
{
int arr1[] = {1,2,3,4,5};
int arr2[] = {2,3,4,5,6};
int arr3[] = {3,4,5,6,7};
//数组名是数组⾸元素的地址,类型是int*的,就可以存放在parr数组中
int* parr[3] = {arr1, arr2, arr3};
int i = 0;
int j = 0;
for(i=0; i<3; i++)
{
for(j=0; j<5; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
}
return 0;
}
```


parr\[i\]是访问parr数组的元素,parr\[i\]找到的数组元素指向了整型⼀维数组,parr\[i\]\[ j\]就是整型⼀维数组中的元素。
上述的代码模拟出⼆维数组的效果,实际上并⾮完全是⼆维数组,因为每⼀⾏并⾮是连续的。