C语言:指针

学习 C 语言的指针既简单又有趣。通过指针,可以简化一些 C 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的。所以,想要成为一名优秀的 C 程序员,学习指针是很有必要的。

正如您所知道的,每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。请看下面的实例,它将输出定义的变量地址:

实例

#include <stdio.h>
int main()
{
   int  var1;   
   char var2[10]; 
   printf("var1 变量的地址:%p\n", &var1  );   
   printf("var2 变量的地址:%p\n", &var2  ); 
   return0;
}

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

var1 变量的地址:0x7fff5cc109d4
var2 变量的地址:0x7fff5cc109de

通过上面的实例,我们了解了什么是内存地址以及如何访问它。接下来让我们看看什么是指针。

什么是指针?

指针和其他的int, float等类似, 是一种类型. 有类型就有相应类型的变量和常量. 本文主要讨论变量的情况.

指针变量就是一种变量, 和其他种类的变量类似, 但指针和其他变量又有区别.

首先C语言作为一种类型语言, 每个变量都会有几个属性.

  • 变量名称.
  • 变量类型.
  • 变量的值.

例如int a = 3, 变量名称就是a, 变量类型是int, 变量的值是3, 如果不提供初始值, 那么变量的值可能是一个随机值.

也就是说, 任何时候看到一个变量, 就会有这3个属性.

对于指针变量, 可以认为有4个属性.

  • 指针变量的名称.
  • 指针变量的类型, 即指针类型.
  • 指针变量的值, 即一个地址.
  • 指针变量的值所指向的内存里的数据类型. 本文称做"指向类型".

可以看到指针变量的关键在于指针所指向的内存里面数据的类型.

例如int a = 3; int *b = &a;, 指针变量名称是b, 指针变量类型是指针, 变量b的值是变量a的内存地址. 变量b所指向的内存的数据类型是int. 指针变量多了一个"变量b所指向的内存的数据类型是int”, 本文将指针变量所指向的内存的数据类型称做指向类型.

任何时候看到一个指针就需要关注4点内容: 名称, 指针类型, 指针值, 指向类型. 搞清楚这几个内容, 就可以弄明白指针怎么回事, 当然还要记忆 一些例外的情形.

指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:

type *var-name;

在这里,type 是指针的基类型,它必须是一个有效的 C 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:

int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *ch;     /* 一个字符型的指针 */

所有实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是一样的,都是一个代表内存地址的长的十六进制数。

不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。

如何使用指针?

使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 * 来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作:

实例

#include <stdio.h>
int main()
{
   int  var = 20;   /* 实际变量的声明 */
   int  *ip;        /* 指针变量的声明 */
   ip = &var;  /* 在指针变量中存储 var 的地址 */
   printf("Address of var variable: %p\n", &var  ); 
   /* 在指针变量中存储的地址 */
   printf("Address stored in ip variable: %p\n", ip); 
   /* 使用指针访问值 */
   printf("Value of *ip variable: %d\n", *ip); 
   return0;
}

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

Address of var variable: bffd8b3c
Address stored in ip variable: bffd8b3c
Value of *ip variable:20

类型

对于C语言来说, 搞清楚变量的类型相当重要, 涉及到指针的时候就更加重要. 看到一个指针变量后需要理解其指向类型.

例如char * const * (*next)(), next是一个指针, 那么其指向类型是什么? 这个声明/定义比较复杂, 日常编程可能就会碰到比较 复杂的情况, 所以要搞清楚指针首先要懂得怎么看一个声明/定义的变量的类型.

如果看到一个变量的声明或者定义, 那么就需要弄明白变量的类型.

理解类型的规则

  1. 从变量名称开始读取, 然后依照优先级按顺序处理.
  2. 优先级从高到低 a. 括号内优先级高. b. 后缀操作符, ()表示一个函数, []表示一个数组. c. 前缀操作符, *表示"指向...的指针"
  3. 如果const, volatile后面为类型(int, long等), 那么作用于类型, 其他情况下作用于const, volatile左边的指针*.
char * const * (*next)()

按照上面的规则来理解next的类型

  1. 括号内的优先级最高, 即首先看(*next)
  2. next左边为*, 因此next是一个指针类型
  3. 然后后缀()的优先级更高, 因此next是一个指针, 指向一个函数.
  4. 接着是const右边的*, 表示next是一个指针, 指向一个函数, 该函数返回值类型为一个指针.
  5. char * const看作一个整体为指向字符的常量指针.

整个来说: next是一个指针, 指向一个函数, 函数的返回值也是一个指针, 指向一个类型为char的常量指针.

C语言为类型语言, 即每个变量都有类型. 类型在变量的赋值, 函数传参, 编译检查等等方面都会用到.

类型可以确定数据的大小和操作.

例如int a = 3, 那么在内存中会存储一个数据3, 那么对于int类型具体来说.

  1. 这个数据3会占用4字节(常见32位机器与64位机器上int类型占用4字节). 实际上是有4字节的内存, 内容是0×00000003. 因此int类型就规定了占用的内存大小.
  2. 对于int类型就可以进行+,-,*,/等操作, 但是不能进行取指针值(*a)的操作. 能够进行什么操作, 也是由类型规定的.

那么对于指针来说, 其指向类型就非常重要, 指向类型就规定了指针的值所指向的内存的数据是什么类型, 也就是占用多大内存, 可以进行什么操作.

只要类型确定, 那么便可以用sizeof计算类型占用的内存大小, 这个是编译阶段便可以确定的.

对于指针类型来说, 所有指针类型占用的内存大小基本都是一样的, 例如在32bit的机器上占用4字节, 在64bit的机器上占用8字节.

下面代码的变量a和变量b都是指针类型, 但是指向类型不同. 因此sizeof(a)和sizeof(b)的值相等, 但是sizeof(*a)和sizeof(*b)不相等.

int *a;
double *b;
sizeof(a) == sizeof(b);
sizeof(*a) != sizeof(*b);

C 中的 NULL 指针

在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为指针。

NULL 指针是一个定义在标准库中的值为零的常量。请看下面的程序:

实例

#include <stdio.h>
int main()
{
   int  *ptr = NULL; 
   printf("ptr 的地址是 %p\n", ptr  ); 
   return0;
}

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

ptr 的地址是 0x0

在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。

如需检查一个空指针,您可以使用 if 语句,如下所示:

if(ptr)     /* 如果 p 非空,则完成 */
if(!ptr)    /* 如果 p 为空,则完成 */

本文分享自微信公众号 - 计算机二级C语言(gh_044a0595bf44),作者:点此关注☞

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-04-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 全国二级C知识点总结4-指针

    printf(“%d,%d,%d,%d\n”,m,n,*p,*q) ;

    用户6755376
  • C 指针的算术运算

    C 指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。

    用户6755376
  • 指针讲解:*&p和&*p

    一讲到指针,不少同学就会觉得云里雾里。首先要明白,指针和地址是一个概念;然后明白指针和指针变量的区别。

    用户6755376
  • 野指针

    指向非法的内存地址指针叫作野指针(Wild Pointer),也叫悬挂指针(Dangling Pointer),意为无法正常使用的指针。

    Dabelv
  • C语言指针5分钟教程

    指针、引用和取值 什么是指针?什么是内存地址?什么叫做指针的取值?指针是一个存储计算机内存地址的变量。在这份教程里“引用”表示计算机内存地址。从指针指向的内 存...

    猿人谷
  • C语言最难啃的三块硬骨头

    提到C语言很多初学者都觉得,学到中间就进行不下去了,因为碰到了几个硬骨头死活翻不过去,于是很多人给C语言下结论太难了,太靠近底层了,特别是那几块难啃的骨头,直接...

    程序员互动联盟
  • Golang语言--指针

    在Go中指针是很容易学习的。一些进入编程任务,指针更容易操作,如通过引用调用,需要要使用指针来执行。所以学习指针成为完美Go程序员很有必要。让我们开始学习指针的...

    李海彬
  • 基础知识 | 每日一练(59)

    士人有百折不回之真心,才有万变不穷之妙用。立业建功,事事要从实地着脚,若少慕声闻,便成伪果;讲道修德,念念要从虚处立基,若稍计功效,便落尘情。 ...

    闫小林
  • 视频流媒体平台编译中如何运用Go语言指针?

    本文讲的也是我们在编译流媒体平台EasyNVR的时候,碰到的go语言指针问题,就打算为大家介绍一下Go语言指针的运用。

    EasyNVR
  • 142. Linked List Cycle II

    首先要证明链表有环: 用快慢两个指针解决。快指针每次走两步,慢指针每次走一步。如果有环,则一定会最终在环内某点相遇。下面证明这一点:

    平凡的学生族

扫码关注云+社区

领取腾讯云代金券