欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言学习——指针详解

程序员文章站 2022-05-08 14:33:05
...

C语言学习——指针详解

1 指针和指针变量

1.1存储单元

内存: 内部存储器是由线性连续的存储单元组成
的。存储单元的最小单位是字节。

1.2 地址

为了访问内存中的某个存储单元,我们要为它编号,这种编号称为地址。通过地址我们就能够访问该地址标识存储单元所存储的数据。
C语言学习——指针详解

1.3 变量的地址

变量在内存中总占用几个连续的字节,开始字节的地址,就是变量的地址。当没有赋值时,变量的值是不确定的。

1.4 指针

一个变量的地址称为该变量的指针。

1.5 指针变量

若一个变量专用于存放另一个变量的地址(指针),则该变量称为指针变量。

C语言学习——指针详解

2 指针变量的定义和使用

2.1指针变量的定义

语法: 类型说明符 *变量名 1,*变量名 2……;
例: int *p1, *p2;
说明:

  1. 指针变量是一个存放另一个变量地址的变量,所占内存由编译系统决定。
  2. 指针变量的存储类型、作用域、可见性、寿命同普通变量;
  3. 指针变量的数据类型指明了该指针指向的内存空间所存储数据的数据类型。
  4. 在引用指针变量前必须首先让它指向一个变量。

2.2 指针变量的赋值

语法:类型说明符 *指针名=初始地址值;
例: int i; int *p=&i;
说明:
1. 用变量地址初始化指针时,该变量必须预先定义
2. 变量和指针的type应一致
3. 空指针不表示任何指向,而是一种状态标志
int *px=0; char *py=“”,*py=NULL;

2.3 指针的存取运算

“&” (地址运算符) :取变量的存储地址
“*” (引用运算符) :取指针所指向单元的内容

i ------普通变量,它的内容是10
&i------指针,它的内容是地址:2000      =p 
p-------指针变量,它的内容是地址:2000   
*p------指针所指向单元的内容--数据:10 =i
&p----指针变量占用内存的地址:二级指针

3 指针与数组

指针和数组的关系十分密切。大多数指针运算都要求指针指向计算机内存中连续存储的数据;而数组正好在计算机内存中申请了一段连续的存储空间,数组名代表了数组起始元素的地址;所有通过数组完成的操作都可以通过指针来完成,指针和数组的表现方式完全等价。

注意: 数组名这个指针是一个地址常量,不能像指针变量那样自加、自减、改变其值;用指针变量编写的程序执行速度快,理解起来稍微困难一些

3.1

设: int Num[5]={2, 4, 6, 8, 10}, *Ptr=Num;

下标法:①地址&Num[i] ②元素Num[i]
指针法:①地址Num+I ②元素(Num+i)
Ptr+i 等于Num+i等于 &Num[i] 等于 &Ptr[i]
*(Ptr+i) 等于 *(Num+i) 等于Num[i] 等于 Ptr[i]

设:int a[10],*p ; p=a ;
C语言学习——指针详解
说明:
⑴ 数组名代表数组的首地址(起始地址),也就是第一个元素的地址;指针变量使用前必须赋值。
⑵ 指针变量是地址变量,数组名是地址常量:指针变量的值可以改变;而数组名一旦定义就不能变了。

3.2 指针运算

指针变量如果只进行&取地址运算、*引用运算,则用途受限。当指针指向计算机内存中连续存储的数据区域时,使用就更加灵活,可以进一步参加算术运算、关系运算,可以更有效方便地使用数组、字符串。

3.2.1 pointer±n

意义:指向该指针下移或上移n个数据之后的内存地址,即(pointer)±n*sizeof(type)
short a[]={10,20,30,…};
short *pa=a;
&a[0]a+0 pa+0
&a[2]a+2 pa+2C语言学习——指针详解
注意:
(1)参加pointer ± n运算的指针必须指向在内存中连续存放的数据区域,如数组;
(2)pointer ± n指向原数据前后的第n 个数据,但pointer指针本身的指向并未改变;
(3)pointer±=n同时改变了指针的指向,表达式和运算结束后的pointer均指向原数据前后的第n 个数据。

3.2.2 pointer++/--

意义:指向该指针下移或上移1个数据之后的内存地址,即(pointer)±sizeof(type)

注意:

 *ptr++ =*(ptr++)(*ptr)++
 *++ptr =*(++ptr)++(*ptr)
 ++*优先级相同,结合性自右向左:
#include <stdio.h>   //利用指针自增、自减访问数组
void main( void )               	
{
    int Num[5], i, *Ptr=Num;
    printf("顺序输入5个整数:"); 
    for(i=0; i<5; i++, Ptr++) 	// for(i=0; i<5; i++) 
        scanf("%d", Ptr );	//      scanf("%d", Ptr++);

    printf("逆序打印Num:"); 
    for(i=0; Ptr--, i<5; i++)	 // for(i=0; i<5; i++) 
         printf(" %d", *Ptr); 	//      printf(" %d", *--Ptr); 
    printf("\n"); 
}

3.2.3 指针相减

两个指向同一数组不同元素的指针相减,其结果为两个指针之间的数据的个数:
ptr1-ptr2=(ptr1的值- ptr2的值) /sizeof(type)

 /* Description:  指针法求字符串长度.     */

#include <stdio.h>
void main( void )
{
   char str[20], *ptr;
   printf("请输入一字符串:");
   gets(str);
   ptr=str;
   while(*ptr++);     //循环体为空,
   printf("字符串长度为%d\n", ptr-str-1);
}

3.2.4 指针比较

两个指向同一数组不同元素的指针进行比较运算——比较两个指针的位置,结果是逻辑值。

p1<p2    表示p1指的元素在前
p1>p2    表示p1指的元素在后
p1==p2  表示p1与p2指向同一元素
特例:指针变量还可以与空指针 NULL0 比较。 
ptr1==0  表明 ptr1 是空指针,它不指向任何变量。 
ptr1!=0   表示 ptr1 不是空指针。 

3.2.5 指针赋值运算

⑴取地址运算——把变量地址值赋值给指针变量:
如:int a, *pa ;   pa=&a ; 
⑵指针间赋值——把指针变量的值赋给另一个指针变量: 
如:int a,*pa,*pb;   pa=&a ;  pb=pa;
⑶数组名赋值(数组名是指针常量) 
如:int a[10],*pa;   pa=a;
⑷算术赋值运算:
如:int a[10],*pa;   pa=a+3; pa+=2;

3.3 指针与二维数组

3.3.1 二维数组的行地址和列地址

定义:short a[3][4];

⑴ a是数组名,包含三个元素 a[0],a[1],a[2]

⑵ 每个元素a[i]又是一个一维 数组,包含4个元素

a+i等价于&a[i]:第i行首地址,指向行
a[i]等价于*(a+i) 等价于&a[i][0]:表示第i行第0列元素地址,指向列
a[i]+j等价于*(a+i)+j 等价于 & a[i][j]
a[i][j] 等价于 ((a+i)+j) 等价于 (a[i]+j) 等价于((a+i))[j]

C语言学习——指针详解
C语言学习——指针详解

⑶ 通过一级指针引用二维数组元素:

#include <stdio.h>
void main( void )
{
    int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
    int *p;
    for(p=a[0];p<a[0]+12;p++) 
   {   if((p-a[0])%4==0)   
                printf("\n");
        printf("%4d  ",*p);
    }
}

3.3.2 数组指针

(1)二级指针:
类型说明符 **二级指针变量名;
int num=100;
int * prt1=#
int **prt2=&ptr1;
*ptr2等于prt1, **ptr2 等于num, 即 100。
二级指针要进行自加、自减等各种指针运算,就必须具备类似行地址、列地址的结构才行。
注:如果将一个指向基本数据的指针变量地址赋给二级指针变量,这个二级指针变量不能进行各种指针运算,最多能做 *、**引用运算

#include <stdio.h> 
void main( void ) 
{ 
short a[ ]={1,3,5,7,9}, *p, **k; 
p=a;k=&p; 
printf("%d, ", *(p++)); 
printf("%d, ", * *k); 
} 
结果1,3

(2)数组指针:指向一维数组的指针变量,又称行指针

类型说明符 (*变量名)[一维数组长度];
如: short (*P)[3];
short A[][3]={{2,4,6},{8,10,12},{14,16,18}};

让 P指向 A[0]的方法如下:
P=A; 或 P=&A[0];

利用 P访问 A[i][i]的方法如下:
P[i][j] ⇔A[i][j]
((P +i)+j) ⇔*((A +i)+j)
(P[i]+j) ⇔( A[i]+j)
(
(P +i))[j] ⇔ (*(A +i))[j]

4 指针和字符串

利用字符数组存放和处理一个或多个字符串的方法,都是借助数组下标访问字符串或字符串中字符。现在,还可以定义字符型指针变量,通过初始化或赋值的方式将其指向待处理的字符串,然后可以使用该字符型指针变量来处理字符串或字符串中字符。

4.1 一级字符型指针和单个字符串处理

定义一个一级字符型指针来处理单个字符串。一级字符型指针的定义方法:
char *一级字符指针变量名;
和整型指针变量一样,字符指针变量可以在定义时初始化,也可以通过赋值方式将其指向待处理的字符串,例如:
char *S1=“C program”, *S2;
char string[]=“China”;
S2=string;
虽然 S1、 S2 在使用中和 string 差不多, 但字符指针变量和字符数组还是有所不同:
(1)存储内容不同。字符数组可以直接存储字符串,字符指针变量里存储的是地址。

(2)存储空间长度不同。字符数组一旦定义,所指向的存储空间的大小就已经确定下来,虽然今后存储的字符串可能没有那么长,尾部空间会闲置;而字符指针变量占用的字节数是地址所需的长度,与字符串无关。

(3)赋值方式不同:字符数组定义时可以直接初始化,也可以利用字符串赋值函数赋值:
char string[20]=“programming”;
或: strcpy( string, “programming”);
但不能对字符数组直接赋值:string=“Microsoft”; (X)
(4)接受输入:字符数组可以接受输入的字符串(长度不能超出) :
char string[20];
gets( string );
字符指针变量不能直接接受输入的字符串:
char *cp;
scanf("%s", cp); (X)
需要事先开辟内存:
char *cp, str[10];
cp=str;
scanf("%s",cp);

(5)运算不同。字符数组名是地址常量,不能改变,字符指针变量可以参加指针运算,地址可变。
设:char *cp; char str[20];
str由若干元素组成,每个元素放一个字符;而cp中存放字符串首地址
char str[20]; str=“C program”; (×)
char *cp; cp=“C program”; (√)
str是地址常量;cp是地址变量
cp接受键入字符串时,必须先开辟存储空间

4.2 指针数组处理多个字符串

如果数组的每个元素都是同种类型的指针,该数组便称为指针数组。指针数组中的每一个元素都是指针变量,只能存放地址。
定义指针数组:
语法:type *pointer[size];
功能:开辟一片内存存放size个type类型指针
说明:指针数组遵循数组的所有语法约定。
注意:区别 int *p[2] (指针数组)、int (*p)[2] (数组指针)的含意。
当指针数组中的每一个元素都指向一个一维数组时,指针数组就相当于二维数组的多个行地址(数组指针) ,指针数组名就相当于二维数组名。

指针数组和二维数组的区别:
指针数组中的每一个元素所指向的一维数组长度可以不一,一维数组之间也不需要在内存中连续存放,指针数组中的每一个元素均为指针变量,地址可变。

指针数组特别适合于处理若干个长度不等的字符串。指针数组使得字符串处理更加简单方便,不浪费内存空间。

// Description: 利用字符指针数组将五门课程名按字典顺序输出 
#include <stdio.h> 
#include <string.h> 
void main(void) 
{ 
    char *tmp,*str[5]={"VB","FORTRAN","VC++","Authorware","Java"}; 
    short i, j, k, n=5; 
    for(i=0;i<n-1;i++) 
    { 
        k=i; 
        for(j=i+1;j<n;j++) 
            if(strcmp(str[k], str[j])>0)   
               k=j; 
         if(k!=i) 
            tmp=str[i], str[i]=str[k], str[k]=tmp; 
    }         //选择法排序
    for(i=0;i<n;i++) 
          puts(str[i]); 
}
 /*输入2个字符串,将二者连接后的结果输出(用指针完成)。*/
#include <stdio.h>
#include <string.h>
void main( void )
{
   char str1[20], str2[20], *ptr1, *ptr2;
   printf("请输入2个字符串:");
   gets(str1); gets(str2);
   ptr1=str1;  ptr2=str2;
   while(*ptr1)    
       ptr1++; 
   while(*ptr1=*ptr2)  
       ptr1++, ptr2++; 
   printf("合并字符串为%s\n",  str1);
}

5 指针小结

C语言学习——指针详解
使用指针时的常见错误:
(1)试图修改地址常量:如对数组名进行自加、自减、将一个字符串赋值给它等。
(2)使用未初始化的野指针,导致系统混乱。
(3)对指一个基本类型数据的一级指针变量加减。
(4)对两个未指向同一数组元素的指针进行比较、相减
(5)指针访问越界:比如将一个长字符串复制到短字符串所在空间。
(6)对通过一级指针间接指向一个基本类型数据的二级指针变量进行复杂的指针运算。