【C生万物】 指针篇 (初级)下

简介: 【C生万物】 指针篇 (初级)

正文


2.成因


2.1指针未初始化

#include <stdio.h>
int main()
{
  int* p; // 局部变量指针未初始化,默认为随机值
  *p = 20;
  return 0;
}

此处就像随机找个房间闯入... ...


2.2指针越界访问


在数组中存在越界访问:

#include <stdio.h>
int main()
{
  int arr[10] = { 0 };
  int* p = arr;
  int i = 0;
  for (i = 1; i <= 11; i++)
  {
    *(p++) = i; // 当i等于11时,数组越界访问
  }
  return 0;
}

当指针指向的范围超出数组arr的范围时,p就是野指针。


2.3指针指向的空间释放


试想,这有一块空间,有个指针指向它,突然这块空间被释放了,

指针不见了空间,那它不就变野了吗?

以上是笼统的说法,具体到动态内存开辟篇讲解


3.如何规避


这里总结几种规避野指针的方法:

• 指针初始化

• 小心指针越界

• 指针指向空间释放,及时置NULL

• 避免返回局部变量的地址

• 指针使用之前检查有效性


敲代码时养成好习惯。

对于检查有效性,这里详细解释下:

#include <stdio.h>
int main()
{
  int* p = NULL;
  int a = 10;
  p = &a;
  if (p != NULL)
  {
    *p = 20;
  }
  return 0;
}


例如这段代码中,要改变 a ,提前用 p 保存了 a 的地址,在访问之前检查一下 p 是否为空,确保指针的有效性。


Part4:指针运算


1.指针 +- 整数


用下面这段代码测试:

#include <stdio.h>
int main()
{
  int n = 10;
  char* pc = (char*)&n;
  int* pi = &n;
  printf("%p\n", &n);
  printf("%p\n", pc + 1); // char*类型的指针
  printf("%p\n", pi);
  printf("%p\n", pi + 1); // int*类型的指针
  return 0;
}


运行结果:

aa655f1ed56ddaa8031968c07a955091_905d90c48d4744cb9725219bd5854f74.png

指针 + 1,地址走了多大由指针类型决定。

运用这一点,我们可以这样遍历数组:

int main()
{
  float values[5];
  float* vp;
  for (vp = &values[0]; vp < &values[5];)
  {
    *vp++ = 0; // 后置++,先使用后++
  }
  return 0;
}


2.指针 - 指针


在计算数组长度的场景下可以利用指针 - 指针

模拟 strlen 函数:

int my_strlen(char *s)
{
       char *p = s;
       while(*p != '\0' ) // 数组以 \0 结尾
              p++;
       return p-s;
}


3.指针的运算关系


标准规定:

允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。


Part5:指针与数组


在数组篇讲过:

• sizeof(数组名),计算整个数组的大小,内部单独放一个数组名,表示整个数组;

• &数组名,取出的是数组的地址,数组名表示整个数组。


除以上两种情况之外,所有的数组名都表示数组首元素的地址。

借用下面这段程序解释:

#include <stdio.h>
int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
  int* p = arr; // 指针存放数组首元素的地址
  int sz = sizeof(arr) / sizeof(arr[0]);
  for (int i = 0; i < sz; i++)
  {
    printf("&arr[%d] = %p <=> p+%d = %p\n", i, &arr[i], i, p + i);
  }
  return 0;
}


运行结果:

d553ffd860b80472cbbe955f1fef0ca1_4dbd44e1eda84e9ebb722bc41d3fc167.png

嗯,除了利用下标,是不是又会了一种访问数组元素方法呢?

#include <stdio.h>
int main()
{
  int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
  int* p = arr; //指针存放数组首元素的地址
  int sz = sizeof(arr) / sizeof(arr[0]);
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", *(p + i));
  }
  return 0;
}


运行结果:

28c20662230b413ddfdbceedb989f3b2_ad7b07831200468e83f782b671c2ad01.png


Part6:指针数组


数组我们是懂的,那么指针数组是什么?

先说指针数组是指针还是数组:

答案是数组,那它有什么特别之处呢?

其实它存储的数据类型是指针,就这里特别。

表示法:

指针类型 + 指针数组名称 + [];

例如:

int* arr[5];


arr 是一个数组,有五个元素,每个元素是一个整形指针 


Part7:二级指针


前面讲的指针都是一级指针,那么什么是二级指针呢?

对的,就是指向指针的指针 / 指针变量的地址

d716b7ff2bb1a4d0a070cf6c943761d3_8941a9dff8ed45ab82b504ba341ab7b0.png

多一级就多一颗 *

对二级指针的运算有:

• *ppa 通过对ppa中的地址进行解引用,这样找到的是 pa *ppa 其实访问的就是 pa

int b = 20;
*ppa = &b;//等价于 pa = &b;


• **ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a

**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;



总结:

指针,指针,看完后你应该就觉得指针其实没那么难,学会了它,你就可以处理很多问题。

码文不易

如果你觉得这篇文章还不错并且对你有帮助,不妨支持一波哦  💗💗💗


目录
相关文章
C生万物 | 从浅入深理解指针【最后部分】(二)
C生万物 | 从浅入深理解指针【最后部分】(二)
|
6月前
|
存储 C++
C生万物 | 从浅入深理解指针【第三部分】(转移表的实现)
C生万物 | 从浅入深理解指针【第三部分】(转移表的实现)
C生万物 | 从浅入深理解指针【第二部分】(二)
C生万物 | 从浅入深理解指针【第二部分】(二)
|
6月前
|
编译器
C生万物 | 从浅入深理解指针【第二部分】(一)
C生万物 | 从浅入深理解指针【第二部分】 前言: 如果没有看过第一部分的话,推荐先看第一部分,然后再来看第二部分~~
|
6月前
|
存储 C语言 C++
C生万物 | 从浅入深理解指针【第一部分】(一)
C生万物 | 从浅入深理解指针【第一部分】
|
6月前
|
机器学习/深度学习 安全 程序员
C生万物 | 从浅入深理解指针【第一部分】(二)
C生万物 | 从浅入深理解指针【第一部分】(二)
|
6月前
|
C语言 C++
C生万物 | 从浅入深理解指针【最后部分】(一)
C生万物 | 从浅入深理解指针【最后部分】(一)
C生万物 | 从浅入深理解指针【第四部分】(qsort的使用和模拟实现)
C生万物 | 从浅入深理解指针【第四部分】(qsort的使用和模拟实现)
|
11月前
|
存储 编译器 C语言
C生万物 | 指针进阶 · 炼狱篇-3
C生万物 | 指针进阶 · 炼狱篇
68 0
|
11月前
C生万物 | 指针进阶 · 炼狱篇-2
C生万物 | 指针进阶 · 炼狱篇
51 0