指针-上

0 阅读10分钟

文章目录

1、指针变量和地址

2、 指针变量类型的意义 

3、 const修饰指针

4、 指针运算

5、 野指针

6、指针的使⽤和传址调⽤


前言

在C语言中,指针是一个非常重要的概念,它允许直接操作内存地址,从而提高程序的效率和灵活性。指针的基本用法包括声明指针变量、初始化指针、访问指针指向的值以及指针的算术运算。


 ​编辑

一、指针是什么

       指针,是C语言中的一个重要概念及其特点,也是掌握C语言比较困难的部分。指针也就是内存地址,指针变量是用来存放内存地址的变量,在同一CPU构架下,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。

1、指针变量和地址

       我们通过取地址操作符(&) 拿到的地址是⼀个数值,⽐如:0x006FFD70,这个数值有时候也是需要 存储起来,⽅便后期再使⽤的,那我们把这样的地址值存放在哪⾥呢?

答案是:指针变量中。

代码如下(示例):

int * p ;//表示声明了一个能指向int类型内存空间的指针

int a = 10;//定义一个int类型的变量,变量的值为10

p = &a;//表示将a的地址运用取地址操作将地址放进p里面,这时候的p指向的就是a的内存空

解引用操作符:

        *的作用是引用指针指向的变量值,引用其实就是引用该变量的地址,“解”就是把该地址对应的东西解开,解出来,就像打开一个包裹一样,那就是该变量的值了,所以称为“解引用”。 也就是说,解引用是返回内存地址中对应的对象。

解引用也可以改变该变量的数值。

需要注意的是,在变量声明的时候,*不能当做解引用使用,只是表示你声明的变量是一个指针类型。

2、指针变量类型的意义 

        指针变量是存放一个内存地址的变量,不同于其他类型变量,它是专门用来存放内存地址的,也称为地址变量。 定义指针变量的一般形式为:类型说明符*变量名。 类型说明符表示指针变量所指向变量的数据类型;表示这是一个指针变量;变量名表示定义的指针变量名,其值是一个地址,例如:**charp1**;表示 p1 是一个指针变量,它的值是某个字符变量的地址。

3、const修饰指针

1、 const放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。(但是指针变量本身的内容可变)

const int *p = &n;     //n里面的值不能被改变

//*p = 5;        //会报错

  1. 、const放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改。

(但是可以通过指针改变指针指向的内容)

int *const p = &n;        //指向n的行为不能被改变

//p = &m;        //会报错


#include <stdio.h>
 
int main()
{
    const int n = 1;
    //n = 1;    //左值指定const对象会报错
    printf("%d\n", n);
 
    //可以使用指针修改
    int *p = &n;
    *p = 5;
    printf("%d\n", n);
 
    return 0;
}

4、指针运算

递增一个指针

递增一个指针意味着让指针指向下一个内存位置。

指针的递增操作会根据指针所指向的数据类型进行适当的内存偏移。

我们喜欢在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,数组可以看成一个指针常量。下面的程序递增变量指针,以便顺序访问数组中的每一个元素:

#include <stdio.h>
 
const int MAX = 3;
 
int main ()
{
   // 定义一个整数数组
   int var[] = {10, 100, 200};
   // 定义一个整数变量 i 和一个整数指针 ptr
   int i, *ptr;
 
   // 将指针 ptr 指向数组 var 的起始地址
   ptr = var;
   // 循环遍历数组
   for ( i = 0; i < MAX; i++)
   {
      // 打印当前指针 ptr 所指向的地址
      printf("存储地址:var[%d] = %p\n", i, ptr );
      // 打印当前指针 ptr 所指向地址的值
      printf("存储值:var[%d] = %d\n", i, *ptr );
 
      // 将指针 ptr 移动到下一个数组元素的位置
      ptr++;
   }
   return 0;
}

编译结果如下:

```
存储地址:var[0] = e4a298cc 存储值:var[0] = 10 存储地址:var[1] = e4a298d0 存储值:var[1] = 100 存储地址:var[2] = e4a298d4 存储值:var[2] = 200
|                                                                                                                                |   |

递增字符指针:

#include <stdio.h>

int main() { char str[] = "Hello"; char *ptr = str; // 指针指向字符串的第一个字符

printf("初始字符: %c\n", *ptr);  // 输出 H

ptr++;  // 递增指针,使其指向下一个字符
printf("递增后字符: %c\n", *ptr);  // 输出 e

return 0;

}


![](<> "点击并拖拽以移动")

在这个示例中,ptr++ 使指针从 str[0] 指向 str[1]。因为 ptr 是一个 char 类型指针,所以它递增时会移动 sizeof(char) 个字节,即 1 个字节。

当上面的代码被编译和执行时,它会产生下列结果:

|                          |   |
| ------------------------ | - |
|                          |   |
| ```
初始字符: H 递增后字符: e
``` |   |
|                          |   |

递增结构体指针:

#include <stdio.h>

struct Point { int x; int y; };

int main() { struct Point points[] = {{1, 2}, {3, 4}, {5, 6}}; struct Point *ptr = points; // 指针指向结构体数组的第一个元素

printf("初始点: (%d, %d)\n", ptr->x, ptr->y);  // 输出 (1, 2)

ptr++;  // 递增指针,使其指向下一个结构体
printf("递增后点: (%d, %d)\n", ptr->x, ptr->y);  // 输出 (3, 4)

return 0;

}


![](<> "点击并拖拽以移动")

在这个示例中,ptr++ 使指针从 points[0] 指向 points[1]。因为 ptr 是一个 struct Point 类型指针,所以它递增时会移动 sizeof(struct Point) 个字节。

当上面的代码被编译和执行时,它会产生下列结果:

|                                  |   |
| -------------------------------- | - |
|                                  |   |
| ```
初始点: (1, 2) 递增后点: (3, 4)
``` |   |
|                                  |   |

# 递减一个指针

递减一个指针意味着让指针指向前一个内存位置。和递增指针类似,指针的递减操作也会根据指针所指向的数据类型进行适当的内存偏移。

对指针进行递减运算,即把值减去其数据类型的字节数,如下所示:

#include <stdio.h>

int main() { int arr[] = {10, 20, 30, 40, 50}; int *ptr = &arr[4]; // 指针指向数组的最后一个元素

printf("初始值: %d\n", *ptr);  // 输出 50

ptr--;  // 递减指针,使其指向前一个整数元素
printf("递减后值: %d\n", *ptr);  // 输出 40

ptr--;  // 再次递减指针
printf("再次递减后值: %d\n", *ptr);  // 输出 30

return 0;

}


![](<> "点击并拖拽以移动")

编译结果如下:

|                                     |   |
| ----------------------------------- | - |
|                                     |   |
| ```
初始值: 50 递减后值: 40 再次递减后值: 30
``` |   |
|                                     |   |

递减字符指针:

#include <stdio.h>

int main() { char str[] = "Hello"; char *ptr = &str[4]; // 指针指向字符串的最后一个字符 'o'

printf("初始字符: %c\n", *ptr);  // 输出 o

ptr--;  // 递减指针,使其指向前一个字符
printf("递减后字符: %c\n", *ptr);  // 输出 l

ptr--;  // 再次递减指针
printf("再次递减后字符: %c\n", *ptr);  // 输出 l

return 0;

}


![](<> "点击并拖拽以移动")

编译结果如下:

|                                     |   |
| ----------------------------------- | - |
|                                     |   |
| ```
初始字符: o 递减后字符: l 再次递减后字符: l
``` |   |
|                                     |   |

递减结构体指针:

#include <stdio.h>

struct Point { int x; int y; };

int main() { struct Point points[] = {{1, 2}, {3, 4}, {5, 6}}; struct Point *ptr = &points[2]; // 指针指向结构体数组的最后一个元素

printf("初始点: (%d, %d)\n", ptr->x, ptr->y);  // 输出 (5, 6)

ptr--;  // 递减指针,使其指向前一个结构体
printf("递减后点: (%d, %d)\n", ptr->x, ptr->y);  // 输出 (3, 4)

ptr--;  // 再次递减指针
printf("再次递减后点: (%d, %d)\n", ptr->x, ptr->y);  // 输出 (1, 2)

return 0;

}


![](<> "点击并拖拽以移动")

编译结果如下:

|                                                 |   |
| ----------------------------------------------- | - |
|                                                 |   |
| ```
初始点: (5, 6) 递减后点: (3, 4) 再次递减后点: (1, 2)
``` |   |
|                                                 |   |

#

# 5、 野指针

野指针指的是指针指向了一块随机的空间,不受程序控制。

**野指针产生的原因:**  
1.指针定义时未被初始化:指针在被定义的时候,如果程序不对其进行初始化的话,它会随机指向一个区域,因为任意指针变量(出了static修饰的指针)它的默认值都是随机的  
2.指针被释放时没有置空:我们在用malloc()开辟空间的时候,要检查返回值是否为空,如果为空,则开辟失败;如果不为空,则指针指向的是开辟的内存空间的首地址。指针指向的内存空间在用free()和delete释放后,如果程序员没有对其进行置空或者其他赋值操作的话,就会成为一个野指针  
3.指针操作超越变量作用域:不要返回指向栈内存的指针或者引用,因为栈内存在函数结束的时候会被释放。

**野指针的危害:**  
       指针指向的内容已经无效了,而指针没有被置空,解引用一个非空的无效指针是一个未被定义的行为,也就是说不一定导致错误,野指针被定位到是哪里出现问题,在哪里指针就失效了,不好查找错误的原因。

**规避方法:**  
1.初始化指针的时候将其置为nullptr,之后对其操作。  
2.释放指针的时候将其置为nullptr。

# 6、指针的使用和传址调用

**指针**可以用于在程序中传递和操作内存地址,从而使程序能够更高效地访问和操作内存中的数据。 这样说你可能,还是听不懂,那我们举个例子: 这一天有八位客人在前台登记了入住,分别是a,b,c,d,e,f,g,h。 他们一起住在酒店的一个楼层 这就类似于**指针**,通过地址能让我们准确的找到想找的人。

我们来看一下下面的代码

#include <stdio.h> void Swap1(int x, int y) { int tmp = x; x = y; y = tmp; } int main() { int a = 0; int b = 0; scanf("%d %d", &a, &b); printf("交换前:a=%d b=%d\n", a, b); Swap1(a, b); printf("交换后:a=%d b=%d\n", a, b); return 0; }


![](<> "点击并拖拽以移动")

结果数据居然没有进行交换,这是为什么呢?

原因很简单:

**在传参过程中,实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实参。**

**所以数据没有得到交换,那我们要如何做才能把数据交换呢**

**很简单,我们只要把要交换那两个数据的地址传过去就行了**

#include <stdio.h> void Swap2(intpx, intpy) { int tmp = 0; tmp = *px; *px = *py; *py = tmp; } int main() { int a = 0; int b = 0; scanf("%d %d", &a, &b); printf("交换前:a=%d b=%d\n", a, b); Swap2(&a, &b); printf("交换后:a=%d b=%d\n", a, b); return 0; }


![](<> "点击并拖拽以移动")

这样就交换成功了

![](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/eb8666fd74e740ea975f4b497a171f8c~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgbGN6bGx4:q75.awebp?rk3s=f64ab15b&x-expires=1778075026&x-signature=%2B45EmFVA1c3RIaPeTK0UGyd%2BgQ8%3D)![](<> "点击并拖拽以移动")​编辑

* * *

# []()总结

**ok,关于指针---上这一篇章就结束了 ,有什么错误或需要改正的地方可以提出来,我会虚心接受的,然后后续我会带来关于指针方面的更多内容,可以点个赞支持一下博主**

​