C语言学习-指针基础

                                                                                        C语言指针基础

1.指针的重要性

 

指针是C语言的重要概念,也是C语言及其扩展语言(C++等)的重要特色。指针可以使程序变得简洁、紧凑、高效。每一个学习C语言的人都应当深入学习和掌握指针。可以说,没有掌握指针,就是没有学会C语言

指针的概念复杂也比较灵活,因此初学者经常会犯错。请同学们务必多思考、多练习、谨慎使用。

 

2.基本概念

 

地址:在计算机内部存储器(简称内存)中,每一个字节单元(8位),都有一个编号,称为地址(在32位计算机中,地址是由32位(bit)构成的2进制数,占4字节(byte),通常由一个8位的16进制数来表示。)

指针:在C语言中,专门用来保存地址的变量,称为“指针变量,简称指针。在不影响理解的前提下,“指针”、“指针变量”和“地址”不做区分,统称“指针”。

指针的目标:指针的目标变量简称为指针的目标。指针变量用于存放该对象(也可以理解为就是一块内存区域)的首地址。

C语言学习-指针基础

 

3.定义指针变量

 

用法:

<数据类型名> *<指针变量名>

说明:

  1. <数据类型名>:指针指向的数据类型,是指针的目标的数据类型,而非指针本身的数据类型
  2. *<指针变量名>:指针变量的变量名。前面加*代表这是一个指针变量

示例:

char *s;  //定义一个指向char类型数据的指针变量s

int *p;     //定义一个指向int类型数据的指针变量p

float *f;   //定义一个指向float类型数据的指针变量f

注意:在C语言的官方文档内,推荐定义指针时指针运算符" * "要紧贴指针变量名,而不要挨着数据类型名。

 

4.指针变量的引用

 

4.1 运算符

在这里需要熟练掌握两个与指针有关的运算符:&*

&:取地址运算符

*定义指针变量/取指针所指向空间的内容(或称“间接访问运算符”)

两个运算符互为逆运算。例如”&a”就是取变量a的地址,而”*b”就是取指针变量b所指向的内存单元里的内容。通过一个指针访问它所指向的对象的值称为变量的简介访问。

4.2 引用

1)给指针变量赋值

如果我们已经定义了一个int型变量a,和一个int型指针p; 则我们可以使用&给指针变量赋值,即让指针变量p指向变量a

int a;

int *p;

p = &a;   //将a的地址取出赋值给指针变量p

2)指针变量的初始化

我们可以在定义指针变量的时候对指针变量进行初始化。

如:

int *p = &a;   //初始化指针变量p指向变量a

3)引用指针变量所指向的内容

如果已执行p = &a,表示指针p已指向变量a,则我们可以使用指针p来引用变量a。

如:

printf("%d\n", *p); //*p表示将指针变量p的内容取出

或者可以通过指针对指向的变量进行操作。如:

*p = 1;//相当于a = 1

4)引用变量本身的内容

指针变量也是可以输出的,用格式控制%p

printf("%p",p);      //打印指针p所存放的地址

总结:

  1. 指针变量,内部存放的是目标的地址;
  2. *p 取目标的值,目标内存数据;
  3. &a 取变量的内存地址;

5)指针变量占内存空间大小

指针变量存储的是一个32位2进制数(8位16进制数),因此占内存4字节。

注意

指针变量存储的是一个地址值,而地址是4字节,因此指针大小就是4字节。指针占内存空间大小与指针类型无关。

 

5.指针运算

 

5.1算术运算   

C语言学习-指针基础

注意:

不同数据类型的两个指针实行加减整数运算是无意义的

px + n 代表指针向地址大的方向移动 n 个 数据。

移动后的地址量是: (px) + sizeof(px的类型) * n

px - n 代表指针向地址小的方向移动 n 个 数据。

移动后的地址量是: (px) - sizeof(px的类型) * n

 

px++  指针变量向地址大的方向移动一个数据。

px--  指针变量向地址小的方向移动一个数据。

px - py 表示两个相同类型指针间相差数据的个数,而不是一个地址量。

px - py的结果是  (px - py) /sizeof(数据类型)

 

5.2关系运算

C语言学习-指针基础

指针关系表示两个指针在内存位置的高低关系

不同数据区域间的指针,关系运算没有意义。(不同数据类型的指针之间关系运算没有意义)

指针和除0外的整数比较没有意义,和0比较可以判定指针是否为空。(标准写法为if (NULL == p) 标识指针不指向任何地方).

 

5.3赋值运算

赋值运算指的是通过赋值运算符向指针变量传递一个地址值。这个值是地址常量指针变量(同类型),不能是普通整数(0可以表示空值)。

指针赋值运算常见的有以下几种形式:

1)把一个普通变量的地址赋给一个具有相同数据类型的指针

例如:

       double  x=15,  *px  ;

        px=&x;

2)把一个已有值的指针变量赋给具有相同数据类型的另一个指针变量,例如,

       float  a , *px,  *py  ;

       px = &a  ;

       py = px  ;

3)把一个数组的地址赋给具有相同数据类型的指针。例如,

       int  a[20],  *pa;

       pa = a;   //等价 pa = &a[0]

4)把零赋给一个指针。例如,

       int  *pa;

          pa = NULL;     //表示指针不指向任何对象

5)把表达式的值赋给一个具有相同数据类型的指针变量:

       int  n = 2;

       double  a[20],  *px,  *py ;

       px = a ;

          py = px + n ;

 

6.const指针

 

const 表示的使变量常量化,即不可修改。

int  const a = 9;

a = 10; //报错,a为const修饰不可改变。

const int a 与 int const a, const可以在int的左右位置。

const 在遇到指针时会发生一些变化

const常用来修饰指针变量,有以下三种用法:

1)常量化的指针的对象

      const  <数据类型>  *<指针变量名称>;

说明:常量化指针目标是限制通过指针改变其对象的值。

2)常量化指针变量

     <数据类型>  *const <指针变量名称>= <指针运算表达式>;

说明:常量化指针变量,使得<指针变量>的值不能修改(即只能指向同一个对象)。但可以通过 * <指针变量名称>

修改指针所指向的对象的值。  

3)常量化指针变量及其对象

     const  <数据类型>  * const <指针变量名> = <指针运算表达式>  ;

说明:常量化指针变量及其对象,使得既不可以修改<指针变量>的值,也不可以通过*<指针变量名称>修改指针所指向对象的值

 

   int a = 9;

   int b = 12;

   const  int  *p = &a; // const 修饰的是*p   p 指向变量a int  const  *p     = &a;  //和上面效果相同, 都表示指针变量p指向a,且*pa不可变

  *p  = 10 ; // 通过p改变a的值,但*pconst类型,不可改变。

   p = &b; //可以改变p的值,(即指向)

   *p =  11;// 同样不可以

   int *const q = &a;  //const 修饰的是 q 所以q是不能改变的,即不能改变q的指向

   *q = 111;

    q = &b; // q指向b,报错。

 

7.void指针

 

一般情况下,指针的数据类型取决于该指针指向的对象的数据类型,例如int *。但是有些情况下,我们暂时无法确定指针所指向数据的类型,此时我们可以使用空类型指针void *来暂时表示指针的类型。

指针变量指向不确定数据类型的变量的时候,可以定义为void型指针,因为void类型指针可以赋值给其他任意类型的指针,而其他类型不能相互赋值。

一般形式为:

void   *<指针变量名> ;

例如:malloc函数

void * malloc(size_t size);

malloc 函数因为不知道分配空间的具体用途,所以返回void型地址。(后面再详细学习)

 

 

8.小结:指针自增与自减

 

  • p++(或 p+=1): 使p指向下一个元素
  • *p++:++与* 具有相同优先级且结合方向自右向左, 等价于*(p++), 先取*p的值,然后p再自加,指向下一个元素。
  • *(p++) 与 *(++p) 作用不同。 前者是先取*p的值,再使p自加。后者先使p自加,再取自加后指向的内容。
  • ++(*p): 表示将p指向的元素的值加1.