C语言-指针

Posted 霏ིྀ宇ིྀ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言-指针相关的知识,希望对你有一定的参考价值。

指针

指针类型相关的基础知识

  1. 计算机内存储器地址的编码方法

欲学好指针类型,首先要知道计算机内存储器地址的编码方法。计算机内存储器里存储的信息都是由1和0构成的二进制数来表示,每一位二进制数位是表示信息的最小单位,计算机技术规定在内存储器里用八位二进制数位来存储信息,这八位二进制数位叫一个字节,即在内存储器里是以字节为单位存储信息的,如大写英文字母A,在内存储器里用01000001表示。把能表示八位二进制数的存储空间叫一个字节存储单元。

  1. 存储单元的地址

C语言中数据有基本类型(字符型、整型、长整型、实型、枚举型)、构造类型、指针类型等。不同数据类型在内存中存储时,占用的字节数不同,如字符型需要1个字节空间,整型需要2个字节空间,长整型需要4个字节空间等。把不同数据类型存储时需要的N个字节看成一个整体,叫一个存储单元。对字符型N=1,整型N=2,长整型N=4等。对1个字节构成的存储单元,其字节单元地址编号就是该存储单元的地址,对多个(N>=2)字节构成的存储单元,其低字节单元地址为该存储单元的地址,也叫存储单元的首地址。

  1. 指针类型

语言中的指针类型有指针常量和指针变量。指针常量简称为指针,即存储单元的地址。可以认为配置不同容量的内存,其指针值的范围是不同的,但都是从0起始的。每个存储单元的指针值都是固定不变的。指针变量就是用来存放指针的变量。指针变量是一种较特别的变量,它的值是一些特定的整数值,不是任何整数(如负整数)都可以存放到指针变量,指针变量与整数加减运算也不同于数学中的运算方法。指针变量也有自己的内存空间,空间的大小由编译器决定,在Turbo C 2.0下是2个字节,在Visual VC++下是4个字节。

  1. 变量的指针

在编译C语言程序时,要在内存为各种变量分配相应的存储单元,相应的变量名称对应着存储单元的首地址,这首地址就是变量的指针。可以将一变量的指针赋给一指针变量(存到指针变量代表的存储单元中),这时就说指针变量指向了该变量(也可以说指针变量指向了该变量表示的存储单元)。对存储单元的存取操作即可以用变量名,也可以通过指向该变量的指针变量来进行,这要用到指针运算符“*”

  1. 指针变量类型和它所指向的数据类型

指针变量的类型以声明它时其前面的“*”为标志,无论指针变量指向何种数据类型,它所占用的内存空间大小是一定的(如2个字节),这与其他数据类型的变量所占有的内存空间大小一定一样,所以指针变量的类型就是指针类型,与它所指向的数据类型无关。有资料把可指向不同数据类型的指针变量说成有整型指针(int*)变量、实型指针(float *)变量、字符型指针(char *)变量等,如果非要这样说,理解成指针变量可指向什么数据类型变量就叫什么数据类型指针变量为好,如 int *p;的声明p可以指向整型变量,说p是整型指针变量为好。

声明指针变量时,还要声明允许指针变量指向的数据类型,这由声明指针变量时“*”前面的“基类型”决定。指针类型加减整数运算时移动的位置大小由指向的数据类型来决定。指向同一种数据类型的不同指针变量间可以进行加减或大小比较运算,指向不同数据类型的指针变量不能进行加减运算或大小比较运算。指针变量一旦被“基类型”声明可指向某种数据类型,就不能再指向其他别的数据类型。

指针类型中的指针变量还可进行自增自减运算,而指针不能。指针变量既可以是左值,也可以是右值,指针只能是右值。由指针、指针变量与运算符构成指针类型表达式。下面说的“&”作为单目运算符时与一个变量结合就构成指针表达式,如&a,&p等,而“*”作为单目运算符只可与指针类型结合,构成指针类型指向的数据类型表达式,如有:

int *p,a,b;

p=&a;

b=*p;

*p=10;

*&a=20;

其中“b=p;”的p就是整型表达式,也是一个整型变量,&a是指针类型表达式,*&a是一个整形表达式,也是一个整形变量。

  1. “&”和“*”运算符

C语言中有“&:按位与运算符,是双目运算符,结合性是由左到右”和“&:取地址运算符,是单目运算符,结合性是由右到左”,有“:乘法运算符,是双目运算符,结合性是由左到右”和“:指针运算符,是单目运算符,结合性是由右到左(“*”在声明指针变量时也用到了)”。

对于取地址运算符&只能与一个变量结合构成指针类型表达式,如有变量name,则&name就得到变量name的指针。

对于指针运算符*,意义是“取其指向的内容”,这里说“取其指向的内容”不是指存储单元里存放的值,而是表示指针变量指向的变量。在有的书中说“例如:&a为变量a的地址,*p为指针变量p所指向的存储单元的内容(即p所指向的变量的值)[1]”,这种说法值得商榷。笔者认为用“*p代表指针变量p所指向的存储单元(即p所指向的变量)”的说法比较合适,更直接说
p是一个变量,因为p可以是左值。如下代码:

void main()

int a,*p;

 p=&a;

 *p=10;

 printf(%d,%d\\n”,a,*p);

 *&a=20;

输出a和p的结果都为10,说明p与a等价。通过 *&a= 20;语句还可以改变a的值,也说明 &a与a等价,&a可以是左值。

  1. 数组的指针

C语言中的数组是一种线性构造型数据类型,特点是:

(1) 数组中数据元素是有序的。

(2) 数组中每一个元素都是具有相同数据类型的拥有不同下标的同名变量,每个元素用下标来访问。

(3) 数组在内存中按下标递增的次序在地址连续的一片内存区域中存放,最低的地址对应于第一个数组元素,最高的地址对应最后一个元素,程序运行中数组大小不可改变。

(4) 二维数组或多维数组可看成多重一维数组的结合。如二维数组可看成两个相结合的一维数组,即一维数组中的每个元素又是一个一维数组的数组名。

数组名表示数组的入口指针,每个数组元素的指针由取地址运算符&与每个元素构成指针表达式得到。

二维数组可看成由若干行,每行又由若干列元素组成的有序元素的集合。二维数组名表示数组的入口,指向第一行一整行,叫行指针,数组元素的指针指向每一个元素,叫列指针。其实一维数组名就是列指针,指向第一个元素,这也是一维数组与二维数组一个很重要的区别。行指针和列指针可以互相转换,用“&、”两个运算符可以达到转换的目的。“&”把列指针转成行指针,“”把行指针转成列指针,这种转换不改变指针值的大小,改变了指针变量的指向。例如有int
a[N][M];声明一个 N行M列的二维数组。a
是数组的入口指针,是行指针,a[0]、a[1]、a[2]是列指针,&a[0][0]是元素a[0][0]的指针,其中a、*a、&a[0]、a[0]、&a[0][0]的值相等,但a、&a[0]指向行,*a、a[0]、&a[0][0]指向列。a与&a[0]等价,a[0]与
&a[0][0]等价,*a与a[0]、&a[0][0]也等价。

对于二维数组的第i行有:a+i 与 &a[i]等价,*(a+i)与a[i]等价,但不能说a+i与a[i]等价,尽管它们的值相等,a+i是指向第i行的行指针,a[i]是指向第i行第0列元素的,即是指向a[i][0]的列指针。

二维数组中指向第i行第j列的指针可以是如下形式:&a[i][j]、a[i]+j和*(a+i)+j,对第i行第j列元素的存取可以用如下形式:a[i][j]、(a[i]+j)和(*(a+i)+j)。

a+i(i>=0)也相当于是指向第i行有M个元素的一维数组的指针。

a又相当一有N个指针元素的指针数组,a相当指向指针的指针,N个元素不是指针(下标)变量,而是指针[3-5]。

8用指针变量引用数组的元素

当指针变量指向数组时,可以用该指针变量引用数组的元素。

用指针变量引用一维数组的元素,既可以用指针法(这要用到“*”指针运算符),也可以用下标法(即把指针变量当成数组名),相对简单。

用指针变量引用二维数组的元素,相对复杂些,当指针变量指向数组时,既可以是指向行,也可以是指向列。当指针变量指向列,如果把指针变量当成数组名,就可以把二维数组当成一维数组来对待,一维数组元素的个数由二维数组的行数与列数的乘积得到。如下代码:

void main()

int a[3][4]=1,2,3,4,5,6,7,8,9,10,11,12;

int (*p)[4];

p=a;/*p指向行 */

printf(%d\\n”,*(*(p+1)+2));

printf(%d\\n”,p[1][2]);


程序运行后输出a数组中a[1][2]元素的值。

void main()

int a[3][4]=1,2,3,4,5,6,7,8,9,10,11,12;

int *p;

p=a[0];/*p指向列 */

printf(%d\\n”,*(p+6));

printf(%d\\n”,p[6]);


程序运行后也是输出 a[1][2]元素的值[3,6-7]。

指针与指针变量的概念,指针与地址运算符

指针

C语言中,任何数据都会占用内存单元的。计算机内存的每个存储位置都对应唯一的存储地址,而变量名作为变量数据存储区域所取的名字,代表的是一个内存编号,而这个内存编号对于用户来说是未知的。如定义:int a=12

变量名a代表某个内存单元,而变量值12则是这块内存单元里面的内容。在整个程序运行过程中,通过变量名来访问变量,变量名a所代表的这块内存单元不变,所改变的只是这块内存单元里面的值。

C语言也支持使用变量存储地址来对变量进行存取操作,要取得变量的存储地址,可使用取地址运算符&,如&a表示变量a的存储地址;而变量的存储地址就是指针。

指针变量

指针类型就是C语言中用于表示存储地址的数据类型,而专门用来存放变量存储地址的变量则称为指针变量

格式:数据类型 *指针变量名。跟普通变量不同的是,所有指针变量占用的内存单元大小是一样的,前面的数据类型代表的是指针变量所指向变量的数据类型。如以下代码: int a,*b;//a是一个int型变量,b是一个指向int型数据的指针变量
b=&a;//代表b指向a

使用指针变量的例子

#include <stdio.h>
int main()

    int a = 100, b = 10;        //定义整型a,b,并初始化
    int *pointer_1, *pointer_2; //定义指向整型数据的指针变量pointer_1,pointer_2
    pointer_1 = &a;             //把a的地址赋给指针变量pointer_1
    pointer_2 = &b;             //把b的地址赋给指针变量pointer_2
    printf("a=%d,b=%d\\n", a, b);
    printf("*pointer_1=%d,*pointer_2=%d\\n", *pointer_1, *pointer_2);
    return 0;

例子

double  *pd ;
char  *s1 , *s2 ; 

在这里定义了三个指针变量pd、s1和s2,其中指针变量pd的基类型为double类型,在指针变量pd中,只能存放double类型变量的地址,指针变量s1和s2的基类型为char类型,在指针变量s1和s2中只能存放char类型变量的地址

int  **p ;

以上是定义了一个指向指针的指针变量p,该指针变量p只能存放基类型为int类型的指针变量的地址。

int  *pi , **p , k ;

以上语句是在同一语句中,同时定义了指针变量pi、指向指针的指针变量p和变量k,这是允许的。

指针变量的基类型的作用

任何一个指针变量都是用于存放它所指向变量的地址,只要能存放地址就可以了,为何还要区别不同的基类型呢?
其原理是:不同的数据类型变量,C语言系统为它们开辟的存储空间的字节数是不同的,

定义指针变量

定义指针变量与定义普通变量非常类似,不过要在变量名前面加星号*,格式为:

类型名 *指针变量名;
类型 * 指针变量名=初值表达式;

其中,指针变量名是标识符,指针变量名之前的符号“*”,表示该变量是指针类型的。而最前面的“类型”,表示该指针变量能指向变量或函数的类型。初值表达式是一个地址表达式,如表达式中有某变量的地址表达式,则这个变量应是前面已定义的。

给指针变量赋值

使指针指向一个对象

  1. 通过求地址运算符(&)把一个变量的地址赋给指针变量

“&”是求地址运算符,该运算符为单目运算符,用于求变量的地址,且该变量必须为内存变量。

例如:

int  k=1 , j =2 , *q1 , *q2 , *p ;
      float  x=4.5 ;
      q1=&k ;
      q2=&j ;

以上第三条语句,是把变量k的地址赋给了指针变量q1,使指针变量q1中存放了变量k的地址,或称指针变量q1指向了变量k。同理,以上第四条语句,是把变量j的地址赋给了指针变量q2,使指针变量q2中存放了变量j的地址,或称指针变量q2指向了变量j。

注意:在使用 & 运算符求变量的地址,并赋给指针变量时,一定要确保所求地址的变量数据类型与存放该变量地址的指针变量基类型一致。

  1. 同类型指针变量之间可以直接赋值
    可以把指针变量的值赋给指针变量,但一定要确保这两个指针变量的基类型是相同的。
    接上例:p=q1 ;
    执行以上语句后,使指针变量p也存放了变量k的地址,也就是说指针变量p和q1同时指向了变量k

指针与地址运算符

&是取地址运算 对任意变量都可以进行取地址操作

*是取指针目标运算符

指针和指针变量的区别:

  1. 概念不同

“指针”是概念,“指针变量”是具体实现,指针也是一个变量,所以需要进行定义,而对于指针的定义,与一般变量一样。

  1. 存放地址不同

一个变量的(内存)地址称为该变量的“指针”,通过指针能找到以它为地址的内存单元。而指针变量是用来存放另一个变量的地址的(即指针)

指针和指针变量的关系

  1. 指针就是地址,地址就是指针。

  2. 地址就是内存单元的编号。

  3. 指针变量就是存放内存地址的变量。

  4. 指针和指针变量是两个不同的概念,但要注意的是,通常我们叙述时会把指针变量简称为指针,实际他们含义并不一样。

注:

指针里存的是100. 指针:地址 - 具体。

指针里存的是地址,指针:指针变量 -可变。

指针的好处:

1、直接访问硬件

2、快速传递数据(指针表示地址)

3、返回一个以上的值返回一个(数组或者结构体的指针)

4、表示复杂的数据结构(结构体)

5、方便处理字符串

6、指针有助于理解面向对象

C语言指针变量的运算(加法、减法和比较运算)

指针变量保存的是地址,而地址本质上是一个整数,所以指针变量可以进行部分运算,例如加法、减法、比较等,请看下面的代码:

#include <stdio.h>

int main()
    int    a = 10,   *pa = &a, *paa = &a;
    double b = 99.9, *pb = &b;
    char   c = '@',  *pc = &c;
    //最初的值
    printf("&a=%#X, &b=%#X, &c=%#X\\n", &a, &b, &c);
    printf("pa=%#X, pb=%#X, pc=%#X\\n", pa, pb, pc);
    //加法运算
    pa++; pb++; pc++;
    printf("pa=%#X, pb=%#X, pc=%#X\\n", pa, pb, pc);
    //减法运算
    pa -= 2; pb -= 2; pc -= 2;
    printf("pa=%#X, pb=%#X, pc=%#X\\n", pa, pb, pc);
    //比较运算
    if(pa == paa)
        printf("%d\\n", *paa);
    else
        printf("%d\\n", *pa);
    
    return 0;

可以参考:

http://c.biancheng.net/view/1992.html

指针运算

  1. 赋值(=)
    指针变量只能接受同类型的指针表达式的值,void*类型的指针可以接受任何类型的指针表达式的值。
    例如:
char ch='d',*cp;
cp=&ch;
  1. 取地址(&)
    &操作符可取变量地址,指针变量用于存放地址。
    例如:
int x=30,*xp=&x;
printf("%d%p\\n",x,xp);
  1. 间接访问()
    操作符
    可取指针变量所指单元内容,称为间接引用指针。
    *(取内容)和&(取地址)为互逆操作。
#include<stdio.h>
void main()
	int x=10,y=20;
	int * xp=&x,*yp=&y;
	int z=*xp+*yp;
	printf("%d%d\\n",*xp,*yp);
	*xp+=5;
	printf("%d%d%d\\n",*xp,*yp,z);

  1. 增1(++)和减1(–)
    指针向后(高地址)移动1个单位:指针变量++或++指针变量。指针向前(低地址)移动1个单位:指针变量–或--指针变量。
    例如:
int a[4]=10,25,36,48;
int * p=a;
printf("%d",*p);
p++;
printf("%d",*p++);
printf("%d\\n",*++p)

;
4. 增1(++)和减1(–)
例如:

char b[10]="abcdef";
char * p=b;
printf("%c",*p++);
p++;p++;
printf("%c",*p--);
printf("%c\\n",*--p);
  1. 加(+)和减(-)
    指针向后(高地址)移动n个单位:指针表达式+n。
    指针向前(低地址)移动n个单位:指针表达式-n。
double a[10]=0;
double * p1=a,* p2=p1+8;
p1++;--p2;
printf("%d%d\\n",p2-p1,p1-p2);

例如:
#include<stdio.h>
void main()

	char a[10]="ABCDEF";
	int b[6]=1,3,5,7,9,11;
	char * p1=a,* p2;
	int * q1=b,* q2;
	p2=p1+4;q2=q1+2;
	printf("%c%c%c\\n",*p1,*p2,*(p2-1));
	printf("%d%d%d\\n",*q1,*q2,*(q2+3));

  1. 加赋值(+=)和减赋值(-=)
    指针向后(高地址)移动n个单位:指针表达式+=n。
    指针向前(低地址)移动n个单位:指针表达式-=n。
    例如:
int a[5]=2,4,6,8,10;
int * p1=a+1,* p2=a+4;
printf("%d%d\\n",*p1,*p2);
p1+=2;p2-=3;
printf("%d%d\\n",*p1,*p2);
  1. 比较(==,!=,<,<=,>,>=)
    判断一指针是否为空指针(==或!=)。
例:如果p是空指针,则…
      if(p==0)if(p==Null)if(!p)…
例:如果p不是空指针,则…
      if(p!=0)if(p!=Null)if(p)

通过指针引用数组

一个变量有一个地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素(把某一个元素的地址放在一个指针变量中)所谓数组的指针是指数组的起始地址,数组元素的指针是数组元素的地址。

可以用一个指针变量指向一个数组元素,例如:

 int a[10] = 1, 3, 5, 7, 9, 11, 13, 15, 17, 19;
  int *p;
   p = &a[0];


C语言规定,数组名代表数组的首地址,也就是第0号元素的地址。因此,下面两个语句等价:

p=&a[0];
p=a;

在定义指针变量时可以赋给初值:

int *p=&a[0];

它等效于:

int *p; 
p=&a[0];

当然定义时也可以写成:

int *p=a;

从图中我们可以看出有以下关系:
p,a,&a[0]均指向同一单元,它们是数组a的首地址,也是0 号元素a[0]的首地址。应该说明的是p是变量,而a,&a[0]都是常量。在编程时应予以注意。
数组指针变量说明的一般形式为:
类型说明符 *指针变量名;
其中类型说明符表示所指数组的类型。从一般形式可以看出指向数组的指针变量和指向普通变量的指针变量的说明是相同的。

通过指针引用数组元素

C语言规定:如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素。

引入指针变量后,就可以用两种方法来访问数组元素了。
如果p的初值为&a[0],则:

  1. ) p+i和a+i就是a[i]的地址,或者说它们指向a数组的第i个元素。
  2. ) (p+i)或(a+i)就是p+i或a+i所指向的数组元素,即a[i]。例如,(p+5)或(a+5)就是a[5]。
  3. ) 指向数组的指针变量也可以带下标,如p[i]与*(p+i)等价。

根据以上叙述,引用一个数组元素可以用:
4. ) 下标法,即用a[i]形式访问数组元素。在前面介绍数组时都是采用这种方法。
5. ) 指针法,即采用*(a+i)或*(p+i)形式,用间接访问的方法来访问数组元素,其中a是数组名,p是指向数组的指针变量,其处值p=a。

【例1】输出数组中的全部元素。(下标法)

#include <stdio.h>
int main(void)

    int a[10], i;
    for (i = 0; i < 10; i++)
        a[i] = i;
    for (i = 0; i < 5; i++)
        printf("a[%d]=%d\\n", i, a[i]);

【例2】输出数组中的全部元素。(通过数组名计算元素的地址,找出元素的值)

#include <stdio.h>
int main(void)

   
    int a[10], i;
    for (i = 0; i < 10; i++)
        *(a + i) = i;
    for (i = 0; i < 10; i++)
        printf("a[%d]=%d\\n", i, *(a + i)以上是关于C语言-指针的主要内容,如果未能解决你的问题,请参考以下文章

C语言中关于结构体指针为啥不能在函数内赋初值的问题?

C语言进阶——指针进阶(字符指针指针数组数组指针)

C语言字符串指针

C语言字符串初值对函数说明

C语言 二维数组有没有默认初值?多少?

如何封装C语言的字符串相关功能