1. 指针的详细概念
- 在C语言中,指针是一种变量,它存储的是另一个变量的内存地址。
- 想象计算机的内存是一系列的小盒子,每个盒子都有一个编号(即内存地址),用来存放数据。变量就存放在这些盒子里。指针变量存放的不是常规的数据内容,而是这些盒子的编号,也就是其他变量的内存地址。
- 例如,有一个变量 int num = 10; ,它存储在内存中的某个位置,假设这个位置的地址是 0x100 。可以定义一个指针变量 int *p; ,然后通过 p = # 将变量 num 的地址( &num 表示取 num 的地址)赋给指针变量 p 。此时, p 中存储的值就是 0x100 。
- 通过指针可以间接地访问它所指向的变量。使用 * 运算符(间接访问运算符), *p 就可以访问到 p 所指向的变量的值。在上述例子中,因为 p 指向 num ,所以 *p 的值就是 10 。
- 指针在很多场景下非常有用:
- - 函数参数传递:可以将变量的地址传递给函数。这样函数就能通过指针修改原始变量的值,而不是仅仅操作变量的副本。
- - 动态内存分配:使用指针可以在程序运行过程中动态地申请和释放内存空间,如 malloc 和 free 函数就和指针密切相关,能让程序更灵活地利用内存。
- 指针类型
- 整型指针是用来存放整型的地址
- 字符指针是用来存放字符的地址
- 数组指针是用来存放数组的地址
2. 解应用操作符*
- 当定义一个指针变量,例如 int* p(int*为p变量的类型,如果在一个连续存储的空间中,P变量每次向后访问一个int大小的空间); ,此时 p 的值是一个内存地址。通过 *p 可以访问这个地址中存储的实际值。
- 假设你有 int num = 10; int *p = # ,这里 &num 获取 num 的地址并赋值给 p 。使用 *p 就可以获取 num 的值。如果执行 *p = 20; ,实际上是修改了 num 的值,因为 p 指向 num 所在的内存位置, *p 提供了一种间接修改 num 的方法。C语言中,可以直接把 &num 赋给变量 p ,这里 p 的类型应该是 int * (指向整数的指针)。
例如:
int num = 10;
int *p;
p = #
- * 用于间接访问指针所指向的内容。当我们把 &num 赋值给 p 后, p 存储的是 num 的内存地址。如果要访问 num 的值,就需要使用 * 运算符,如 *p ,它会返回 p 所指向的内存地址中的值。
3. 指针在数组中的应用
- 对于 *(p + i) 和 arr[i] 等价的原因如下:
- 假设 arr 是一个数组, p 是一个指针,并且 p 指向数组 arr 的首地址(即 p = &arr[0]; )。数组在内存中是连续存储的, arr[i] 表示访问数组 arr 中的第 i 个元素。 p + i 实际上是一个地址计算,表示从指针 p 的位置偏移 i 个单位(每个单位的大小是数组元素类型的大小)后的地址,而 *(p + i) 就是访问这个偏移后的地址所指向的对象,其效果等同于直接访问数组中的第 i 个元素 arr[i] 。
- 典型代码 int (*p2)[10] = &arr
- 1. 变量声明部分
- - int (*p2)[10] :这是在声明一个指针变量 p2 。这里的 * 表示 p2 是一个指针,而括号 () 很关键,它表明 p2 指向的是一个包含 10 个 int 类型元素的数组,而不是指向一个普通的 int 类型数据。也就是说 p2 的类型是“指向包含 10 个 int 元素的数组的指针”。
- 2. 赋值部分
- - &arr :假设 arr 是一个已经定义好的 int 类型数组,且数组元素个数恰好为 10 个(类型要匹配)。 & 是取地址运算符,这里 &arr 取得的就是整个数组 arr 的地址,其类型刚好是“指向包含 10 个 int 元素的数组的指针”,所以可以将其赋值给 p2 。
- 3. 后续使用示例
- 例如,要访问数组 arr 中的元素,可以通过 p2 来间接操作。像 (*p2)[3] 就可以访问到 arr 数组中的第 4 个元素(数组下标从 0 开始计数),这里先通过 *p2 得到 p2 所指向的数组(也就是 arr 本身),然后再用 [3] 去访问这个数组中的第 4 个元素。
- 4. 对于二维数组arr[r][c],对其应用操作为int (*p)[5] = &arr,对其数组元素的访问操作有两种
- ①. *(*(p+i) +j )
- ②. p[i][j]
- 5. p变量的类型(对理解指针操作十分重要)
- 在 int (*p2)[10] = &arr 中而言,p变量的类型为int (*)[10],即把数组的地址赋给这样类型的p变量。在对p变量进行声明,去掉p变量即为它的类型。
- 在C语言中,“退化”是一种隐式类型转换。当我们用 int (*p)[10] = &arr; 定义指针 p , p 指向一个包含10个 int 元素的数组。当使用 *p 时,本来 *p 代表的是一个数组(包含10个 int 元素),但是在很多操作场景下,这个数组会被当作一个指向其首元素的指针来处理,这种从“数组类型”转变成“指向其首元素的指针类型”的现象就称为“退化”。例如, *p 原本是一个数组,但在表达式 *p + 1 中,它就像一个指向 int 类型的指针,这样才能进行指针加法运算,编译器会自动将 *p (数组)转换为指向其首元素的指针,以符合指针运算的规则。一般来说,sizeof(arr)和&arr中,arr此时表示的都是整个数组的地址。在二维数组中,arr为第一行的地址,而arr[0]会退化成第一行第一个元素的地址。
- ① 在c语言中,int arr[10];int (*p)[10] = &arr,*p+1会发生什么
- 1. 首先分析定义:- 对于 int arr[10]; ,定义了一个包含10个 int 元素的一维数组 arr 。- 对于 int (*p)[10]=&arr; ,定义了一个指针 p ,它是一个指向包含10个 int 元素的数组的指针,并且初始化为 arr 数组的地址。
- 2. 然后看 *p + 1 这个表达式:
- - *p :对 p 进行解引用。由于 p 是指向包含10个 int 元素的数组的指针, *p 就相当于得到了这个数组本身,在这种情况下, *p 的类型“退化”为 int* ,它指向 arr 数组的第一个元素。
- - *p + 1 :在 *p (此时为指向 arr 第一个元素的指针)的基础上进行指针运算。它会使指针向后移动一个 int 类型大小的字节数。所以 *p + 1 指向了 arr 数组中的第二个元素。
- ② 对于二维数组arr[3][5],int (*p)[5] = &arr,*(p+1)又有什么不同
- 1. 分析 int (*p)[5]=&arr; - p 是一个指针,它指向包含5个 int 元素的数组(也就是二维数组 arr 的一行)。
- - p + 1 :根据指针运算规则,由于 p 指向的类型是“包含5个 int 元素的数组”,所以 p+1 会跳过一个“包含5个 int 元素的数组”的大小。在内存中,它会指向二维数组 arr 的第二行(假设 int 类型占4字节,那么 p + 1 会在内存中跳过 20 字节)。
- 2. 分析 *(p + 1) :
- - 当对 p + 1 进行解引用( *(p + 1) )时,得到的是 p + 1 所指向的那一行数组(即二维数组 arr 的第二行)。它的类型“退化”为 int* ,可以用来访问第二行的元素。例如, (*(p + 1))[0] 就可以访问二维数组 arr 第二行的第一个元素。这和之前一维数组的情况不同,因为在一维数组中, *p 只是简单地得到首元素,而在这里, *(p + 1) 得到的是二维数组的另一行元素。