指针: 就是地址
表示方法:
printf("%p\n", 0x123456678);
在高级编程语言中, 地址一般是获取由系统分配的地址(即获得变量的地址)
使用取地址运算符 & 来获得变量的地址, 例如 &num
凡是使用指针的地方, 绝对不允许显式的出现具体的地址的数值. 除了 NULL 以外
NULL 也是地址, 其值为 0. 表示的含义如同回收站, 或汽车终点站的含义
指针变量
就是存储指针的变量, 就是存储地址的变量
使用寻址运算符 *(或取值运算符) 来获取地址所表示的内存的值
定义的语法:
所指向类型 *变量名 = NULL;
int *pNum = NULL;
定义了一个 pNum 变量, 该变量用来存储指向 int 类型的地址, 初始值为 NULL
可以直接这么用 int *pNum;
if( pNum != NULL )
...
如何使用(赋值 取值)
1, 定义的时候初始化
int *p = #
2, 先定义后初始化
int *p = NULL;
p = #
将地址赋值给指针变量的时候, 接收地址的是变量 p, 而不是 *p
int* p = #
int* p1, p2, p3; // 定义了int类型的指针变量 p1 和int类型的变量p2 p3
int * p1
p 可以打印地址
*p 可以打印该地址的值
// 指针的类型, 决定了指针变量寻址的时候的读取方式以及做数学运算时的偏移量的单位长度
/ 指针是有类型
// 指针的类型, 决定了寻址的时候一次性读入多少个字节, 以及如何解析这些数据
// *p + n 取出 p 所指向的内存中的变量, 将其值 +n
// *(p + n) *(p 里面存储的地址 + 类型单位长度 * n)
指针作为函数的参数, 语法
void func(int *p) { ... }
调用:
int num;
func(&num);
传参过去的时候相当于 int *p = #
*/
// int *p = #
// 函数中的 p 也指向了 main 函数中的 num 变量
// 使用 *p 可以直接操作 num 的内存
// 如何函数参数, 传递的是变量的地址, 利用指针在函数内外可以随意的操作同一个变量
// 如果将地址作为参数传递, 就称为引用传递
void update1(int num1, int num2) {
int temp = num1;
num1 = num2;
num2 = temp;值 传递
}
void update2(int *num1, int *num2) {
// int *num1 = &n1
// int *num2 = &n2
int temp = *num1;
*num1 = *num2;
*num2 = temp; 引用传递
}
, 指向数组元素的指针
// 2, 指向数组的指针
// 3, 存储指针的数组
// 4, 二级指针或多级指针
// 使用一个指针变量指向 nums 的第 0 个元素
int *p = nums;
// 遍历
// for(int i = 0; i < length; i++) {
// printf("%d\n", nums[i]);
// }
// for(int i = 0; i < length; i++) {
// printf("%d\n", *(p + i));
// }
for(int i = 0; i < length; i++) {
printf("%d\n", *p++);
}
// 如果 p 是指向 int nums[n] 这个数组的第0个元素的时候
// int *p = nums;
// 那么 访问数组中的第 i 个元素的语法可以是 *(p+i)
int length = sizeof(nums) / sizeof(int);
for(int i = 0; i < length / 2; i++) {
// int temp = nums[i];
int temp = *(p + i);
// nums[i] = nums[length - 1 - i];
*(p + i) = *(p + length - 1 - i);
// nums[length - 1 - i] = temp;
*(p + length - 1 - i) = temp;
}
for (int i = 0; i < 6; i++) {
printf("%d\t", *(p+i));
}
printf("\n");
// int *p ;
// p 是指向 int 类型数据的指针变量, 指向 int 类型数据的地址的, 其基本偏移单位为4个字节
// 一个指向一维数组的指针, 存储的就是一维数组的首地址
// 如何定义这个指针变量?
// int *p; *p 看成变量名
// char *p *p ...
// double *p
//
// 定义一个指向一维数组的指针变量, 也会有 *p 这种结果, 可以将其就看成数组名
// int (*p)[10]
// * 与 & 为互逆的运算
//$ 变量 取地址以后 得到地址, 对地址 寻址以后 就得到值
// int num; -> *(&num)
// 5 做 n分之一 运算 得到 5分之一
// 定义一个一维数组
// int nums[10];
// (*(&nums))
// (*p)
//
// int (*p)[10];
// 1
// int (*p1)[10]; //$ 指向一维数组的指针变量(数组指针)
// int *p1[10]; // 指针数组
// 2
int (*p1)[10];
int (*p2)[20];
// 这里两个数组指针有什么不同?
// p1 是指向一个长度为 10 的 int 类型的数组
// p2 是指向一个长度为 20 的 int 类型的数组
// p1 + 1 => 移动 40 个字节
// p2 + 1 => 移动 80 个字节
// 如何给指针变量赋值?
// 指针变量 = 地址
int array1[10];
int array2[20];
// array1 是数组的地址, 指向数组的第0个元素的地址 array1 + 1 => 移动 4 个字节
// &array1 是指向数组的地址 &array1 + 1 => 移动 40 个字节
语法:$
int array[][10];
int (*p)[10]; // p 是一个指向 长度为 10 的 int 类型的一维数组
array 二维数组的首地址 指向 array[0]
array[0] 第0行的首地址 指向 array[0][0]
array[0][0] 就是第0个元素
int nums[10]
nums + 0 == &nums[0]
nums + 2 == &nums[2]
nums + n == &nums[n]
在 二维数组 的角度上讨论
1, 从 array[0] 和 array[0][0] 上讨论
是不是就是 array[0] 是一个一维数组, array[0][0] 是它的元素
array[0] 是指向 array[0][0] 的
2, 从 array 和 array[0] 上讨论
是不是就是 array 是一个一维数组, 而 array[0] 是它的元素
array + 0 指向 array[0]
如果要为 p 赋值一个一维数组的地址, 可以使用 二维数组的数组名
p = array;
p 指向了 第0行
p + n 表示指向了 第n行
*(p+n) 表示第 n 行
*(p + n) 表示 n 行, 也表示指向 第 n 行的 第0个元素
*(p + n) + m 就是指向第n行的第m个元素
*(*(p + n) + m) 就是 array[n][m]
int array[][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
printf("%p\n%p\n%p\n", array, &array, array[0]);
// 准备一个指向一维数组的指针
int (*p)[3]; // 类型 变量名;
// 让 p 指向 array[0]
p = array; // p = &array[0]
// 遍历
// *( *( p + i ) + j )
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
printf("%d\t", *(*(p+i) + j) );
}
printf("\n");
}
int nums[][3] = {
1, 12, -3,
114,-95, 6,
7, 18, 89,
0, 11, 12 }; // 4 * 3
// 123
// 456
// 147
// 258
// 369
// int *p = *nums;
// int *p1 = nums[0];
int *p2 = &nums[0][0];
// 将一个二维数组, 从内存结构上, 看成一个一位数组
// int nums[4 * 3];
for $(int i = 0; i < 4 * 3 - 1; i++) {
for (int j = 0; j < 4 * 3 - 1 - i; j++) {
// if(nums[j] > nums[j+1]) {
if(*(p2 + j) > *(p2 + j + 1)) {
int temp = *(p2 + j);
*(p2 + j) = *(p2 + j + 1);
*(p2 + j + 1) = temp;
}
}
}
指针数组, 本质上是一个数组, 只是里面存储的是指针
定义的语法:
类型 *名字[长度]
案例:
int *nums[10]; // 是一个存储指针的数组(80个字节)
int (*p)[10]; // 指向数组的指针(8个字节)
char *array[] = {
"Hello World",
"Hello iOS",
"Hello itcast",
"Hello everybody"
};
// 1, 遍历每一个字符
int length = 4;
for (int i = 0; i < length; i++) {
// 这里 array[i] 是一个 字符串的首地址
// array[i]++;
// char *temp = array[i];
// temp++
// array[i] + j
int j = 0;
// while(array[i][j] != '\0') {
// printf("%c\n", array[i][j++]);
// }
while(*(array[i] + j) != '\0') {
printf("%c\n", *(array[i] + j++));
}
printf("\n");
}
// 2, 遍历每一个字符串
// int length = sizeof(array) / sizeof(char *);
//
// for (int i = 0; i < length; i++) {
// // array[i] 是地址
// printf("|%s|\n", array[i]);
//
// }
// 使用指针表示字符串: 可能没有足够的空间存储需要的字符
这里使用数组是可以的, 但是不一定可以使用指针
n级指针,访问变量的时候,就需要n个*
#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[]) {
char *strs[] = {
"China",
"America",
"Australia",
"France",
"German"
};
// strcmp(s1, s2) 函数的意义,就是一个关于 s1 和 s2 的表达式
// s1 > s2 => s1 - s2 > 0 => strcmp(s1, s2) > 0
for (int i = 0; i < 5 - 1; i++) {
for (int j = 0; j < 5 - 1- i; j++) {
if(strcmp(strs[j], strs[j+1]) > 0) {
char *temp = strs[j];
strs[j] = strs[j+1];
strs[j+1] = temp;
}
}
}
return 0;
}