【C语言航路】第六站:指针初阶(上)

简介: 【C语言航路】第六站:指针初阶

一、指针是什么

这部分内容,在我们之前的文章中已经提及过

链接:【C语言航路】第一站:(初识C语言(终幕)

这里在简单的回忆一下

学习指针必须先要理解——内存,内存是电脑上的存储设备,一般都是4G/8G/16G等,程序运行的时候会加载到内存中,也会使用内存空间

我们将内存划分为一个个小格子,每一个格子是一个内存单元,也正好是一个字节的大小,对每一个内存单元进行编号,在生活中我们也将这一个个编号称作地址,而地址在c语言中又叫做指针

我们举一个例子,假设我们定义一个变量 int a=10;那么a是一个int类型的变量,需要占用四个字节的空间,而每个字节都有地址,&a取出的是哪一个的地址呢?其实取出的是第一个字节的地址(较小的地址),也就是说,下图中,&a最终取出来的地址是0x0012ff40 ,而这个地址我们可以存放到一个变量中,int* pa=&a,这颗*代表pa是一个指针,int代表pa所指向的类型是int类型,这个pa也叫做指针变量。

理解指针需要理解两个要点

1.指针是内存中一个最小单元的编号,也就是地址

2.平时口头语中所说的指针,通常指的是变量,是用来存放内存地址的变量

总结:指针就是地址,平时口头语中的指针通常指的是指针变量

我们看这段代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
  int a = 10;
  int* p = &a;
  printf("%p\n", &a);
  printf("%p\n", p);
  return 0;
}

运行结果为,打印出来的地址是一样的

我们还可以看这段代码,使用*来解引用指针

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
  int a = 10;
  int* p = &a;
  printf("%p\n", &a);
  printf("%p\n", p);
  *p = 20;
  printf("%d", a);
  return 0;
}

运行结果为

总结:指针变量,就是用来存放地址的变量。(存放在指针中的值都被当成地址处理)。

那这里的问题是: 一个小的单元到底是多大?   其实是1个字节

那么如何编址呢?

经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。 对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电 平(低电压)就是(1或者0)

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000001

.....

11111111 11111111 11111111 11111111

这里就有2的32次方个地址。 每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB)

4G的空间进行编址。 同样的方法,那64位机器,如果给64根地址线,那能编址多大空间,我们也可以计算出来

这里我们就知道了:

在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以 一个指针变量的大小就应该是4个字节。

那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地 址。

总结: 指针变量是用来存放地址的,地址是唯一标示一个内存单元的。 指针的大小在32位平台是4个字节,在64位平台是8个字节

二、指针和指针类型

1.指针类型的意义

我们在上面了解了

32位机器上,地址是4个字节,指针变量的大小也是4个字节

64位机器上,地址是8个字节,指针变量的大小也是8个字节

那在这里就有人产生困惑了,反正都是4个或者8个字节,那为什么要区分int*,char*.....这些呢?为什么不直接弄一个通用指针呢?

其实既然计算机没有这个通用指针,那就说明这些类型是有意义的。我们现在就来探讨以下这些指针的类型的意义。

为了了解这个内容,我们先看这个代码

#include<stdio.h>
int main()
{
  int a = 0x11223344;
  int* pa = &a;
    *pa=0;
  return 0;
}

我们打开调试,窗口,内存,并将列改为4

继续往下走

我们发现,四个字节全部被改为0

我们在看这一段代码

#include<stdio.h>
int main()
{
  int a = 0x11223344;
  //int* pa = &a;
  char* pc = &a;
  *pc = 0;
  return 0;
}

我们仍然监视内存

继续往下走

我们发现只改变了一个字节。

所以我们得出结论:指针类型是有意义的

指针类型决定指针进行解引用时候,一次访问几个字节(访问权限)

int*访问四个字节 char*访问一个字节,float*访问四个字节

我们在继续看这个代码

#include<stdio.h>
int main()
{
  int a = 0x11223344;
  int* pa = &a;
  char* pc = &a;
  printf("%p\n", pa);
  printf("%p\n", pa+1);
  printf("%p\n", pc);
  printf("%p\n", pc+1);
  return 0;
}

运行结果为

由此我们发现,pa+1跳过了四个字节,pc+1跳过了一个字节

所以我们得出

指针类型决定指针的步长(指针+1到底跳过几个字节)

字符指针+1,跳过一个字节

整型指针+1,跳过四个字节

我们可以看这个代码

#include<stdio.h>
int main()
{
  int a = 0x11223344;
  char* pc = (char*)&a;
  int i = 0;
  for (i = 0; i < 4; i++)
  {
    *pc = 0;
    pc++;
  }
  return 0;
}
我们先取出a的地址放到一个char*类型的指针中,因为a取地址后是一个int*类型的指针,所以要强制类型转换。对其进行遍历四次,每次只能改一个字节的空间,四次刚好将a改为0。

我们在这里在总结一下指针类型的意义:

1.

指针类型决定指针进行解引用时候,一次访问几个字节(访问权限)

int*访问四个字节 char*访问一个字节,float*访问四个字节

2.

指针类型决定指针的步长(指针+1到底跳过几个字节)

字符指针+1,跳过一个字节

整型指针+1,跳过四个字节

2.指针+-整数

我们在上面说过,指针的不同类型,其实提供了不同的视角去观看和访问内存

char*一次访问1个字节,+1跳过一个字节

int *一次访问4个字节,+1跳过四个字节

当然在这里,除了+1,还可以-5,+2等操作

比如int*  pa=10;

pa+4  其实就是向后走4*sizeof(int)个字节,也就是+16个字节

pa-5   其实就是向前走5*sizeof(int)个字节,也就是-20个字节

也就是指针类型决定了指针向前或向后走一步有多大(距离)

3.指针解引用

这里也同样在前面说过了

指针的类型决定了对指针解引用时有多大权限(能操作几个字节)

比如char*指针解引用一次就能操作一个字节,而int*指针解引用一次可以操作4个字节

三、野指针

概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

1.野指针的成因

(1)指针未初始化

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

如上代码所示,指针是一个局部变量未初始化,默认未随机值,这样随意进行修改指针的值是非常危险的行为。

(2)指针越界访问

#include <stdio.h>
int main()
{
    int arr[10] = { 0 };
    int* p = arr;
    int i = 0;
    for (i = 0; i <= 11; i++)
    {
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++) = i;
    }
    return 0;
}

如上代码所示,指针的指向范围超出了数组arr的范围,p就是一个野指针,此时如果随意修改指针里的内容是很危险的,假如这个野指针指向的恰好就是本程序已有的其他值,那么就会出现问题。

相关文章
|
1月前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
93 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
1月前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
59 9
|
1月前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
46 7
|
2月前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
2月前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
146 3
|
2月前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。
|
2月前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
45 1
|
1月前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
70 10
|
1月前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
53 9
|
1月前
|
存储 Unix Serverless
【C语言】常用函数汇总表
本文总结了C语言中常用的函数,涵盖输入/输出、字符串操作、内存管理、数学运算、时间处理、文件操作及布尔类型等多个方面。每类函数均以表格形式列出其功能和使用示例,便于快速查阅和学习。通过综合示例代码,展示了这些函数的实际应用,帮助读者更好地理解和掌握C语言的基本功能和标准库函数的使用方法。感谢阅读,希望对你有所帮助!
45 8