深刻理解指针的应用

一、指针的基本用法


1、c程序设计中使用指针的优点:

①可以使程序简洁、紧凑、高效。

②有效的表示复杂的数据结构。

③动态分配内存。

④得到多于一个的返回值。


2、指针变量的说明

在计算机内存中,每一个字节单元,都有一个编号,称为地址。

1 Byte=8 bit 1字节等于8位

在C语言中,内存单元的地址称为指针,专门用来存放地址的变量,称为指针变量。在不影响理解的情况下,有时对地址、指针、和指针变量不区分,统称指针。

指针变量的一般形式:<存储类型> <数据类型> *<变量指针>;

例如:char *pName;

指针的存储类型指针变量本身的数据类型。

指针说明时指定的数据类型不是指针变量本身的数据类型,而是指针目标的数据类型。简称为指针的数据类型。


3、指针的初始化

指针在说明的同时,也可以赋予初值,称为指针的初始化。指针的赋值运算指的是通过赋值运算符向指针变量送一个地址值送的值必须是地址常量或指针变量,不能是普通的整数(0除外).

一般形式为:<存储类型> <数据类型> *<变量指针>=<地址量>;

指针赋值的常见形式:

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

例如:int a=3; int *pa=&a;

int a,*pa; pa=&a;

②把一个已有地址值的指针变量赋给具有相同数据类型的另一个指针变量。

例如:float a,*px,*py;

px=&a;

py=px;

③把一个数组的地址赋给具有相同数据类型的指针。

例如:int a[20],*pa;

pa=a; 或 pa=&a[0];

指针指向的内存区域中的数据称为指针的目标。如果它指向的区域是程序中的一个变量的内存空间,则这个变量称为指针的目标变量。简称为指针的目标


设px为一个指针,则:

px——指针变量,他的内容是地址量

*p——指针所指的对象,它的内容是数据

&px——指针变量占用的存储区域的地址,是个常量

二、指针的运算

1、定义及运算符的使用

指针运算是以指针变量所存放的地址量作为运算量而进行的运算。

指针运算的实质就是地址的计算。

指针运算的种类是有限的,它只能进行赋值运算、算术运算和关系运算。

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

运算符

计算形式

意义

+

PX+n

指针向地址大的方向移动n个数据

-

PX-n

指针向地址小的方向移动n个数据

++

PX++或px++

指针向地址大的方向移动1个数据

--

PX--或px--

指针向地址小的方向移动1个数据

-

PX-PY

结果是两个指针之间相隔数据元素的个数,不是地址量

px+n表示的实际位置的地址量是:(px) + sizeof(px的类型)*n

px-n表示的实际位置的地址量是:(px) - sizeof(px的类型)*n

2、练习一:p-q示例

#include <stdio. h>
int main(int argc, char *argv[])
{
int a[5]={4,8,1,2,7};        //p-q示例
int *P, * q;
p = a; //&a[0]; 
q = &a[3];
printf("%p %p\n", p, q);
printf("%d %d\n",*p,*q);
printf("%d\n", q-p);
return 0;
}
打印结果:
 0xbff5b7ac  oxbffb7b8
 4  2
 3

3、练习二:指针自增自减示例

#include <stdio.h>
int main(int argc,char *argv[]){
int a[5]= {4,8,1,2,7};
int *p,*q;
p= a; //&a[0];
printf("%p %d\n",p,*p);       //指针自增自减示例
q=p++;
printf("%p %d\n",p,*p);
printf("%p %d\n",q,*q);
return 0;
}
打印结果:
0xbfa66e4c   4
0xbfa66e50   8
0xbfa66e4c   4

3、练习三:利用数组输入并打印

两指针之间的关系运算表示它们指向的地址位置之间的关系。指向地址大的指针大于指向地址小的指针。

指针与一般整数变量之间的关系运算没有意义。但可以和零进行等于或不等于的关系运算,判断指针是否为空。

#include <stdio . h>
#define N 5
int main(int argc, char *argv[])
int a[N];
int *p, i;
p=a;//int*p=a;
for(i=6;i<N;i++)
scanf("%d" , p++);//&a[i]
p=a;
for(i=0;i<N;i++){
printf(" %d",*p++);  //++的优先级高,先p++,但是赋值还是之前的赋值给*p
}
puts(" " );
return 0;
}

三、指针与数组

1、定义与使用

在C语言中,数组的指针是指数组在内存中的起始地址,数组元素的地址是指数组元素在内存中的起始地址。

一维数组的数组名为一维数组的指针(起始地址)。

例如:double x[8]; 因此,x为x数组的起始地址。


设指针变量px的地址值等于数组指针x (即指针变量px指向数组的首元数), 则:

x[i]、*(px+i)、 *(x+i) 和px[i]具有完全相同的功能:访问数组第i+ 1个数组元素。

注意:指针变量和数组在访问数组中元素时,一定条件 下其使用方法具有相同的形式,因为指针变量和数组名都是地址量.但指针变量和数组的指针(或叫数组名)在本质上不同,指针变量是地址变量)而数组的指针是地址常量。

#include <stdio. h>
int main(int argc, char *argv[])
int a[]={1,6,9,12,61,12,21};
int *p,i, n;
p=a;
n = sizeof(a) / sizeof(int);
printf("%p %p %p\n", a, a+1, a+2);
for(i=0;i<n;i++)
printf("%d %d %d %d\n", a[i], *(p+i), *(a+i), p[i]);
puts(" ");
return 0;
}

2、练习一:编一个程序,实现数组中n个数的翻转。

#include <stdio.h>
int main(int argc, const char *argv[ ] )
{
int a[]={1,2,3,4,5,6};
int *p,*q,n,i, temp;
n=sizeof(a)/sizeof(int);
p=a;
q=&a[n-1];
while(p<q){
temp=*p;
*p=*q;
*q=temp;
p++ ;
q-- ;
}
for (i=0;i<n;i++){
printf( " %d\n",a[i]);
return 0;
}

四、指针与二维数组

多维数组就是具有两个或两个以上下标的数组。

在C语言中,二维数组的元素连续存储,按行优先存。

练习:编程实现,使用一级指针遍历二维数组。

#include <stdio. h>
{
int main(int argc, char *argv[])
int a[3][2] = {
    
    {1, 6}, {9, 12}, {61,12}};
int *p, i, n;
n = sizeof(a) / sizeof(int);
p =a[0]; //&a[0][0];
printf("%p %p\n", p, p+1);      一个一个加
printf("%p %p\n", a, a+1);      一行一行加
for(i=8;i<n;i++)
printf("%d,*(p+i));
puts("") ;
return 0;
}
输出结果:
0xbffeb188 0xbffeb18c
0xbffeb188 0xbffeb190
1 6 9 12 61 12

二维数组名代表数组的起始地址,数组名加1,是移动一行元素。因此,二维数组名常被称为行地址。

行指针(数组指针)

存储行地址的指针变量,叫做行指针变量。形式如下:

●<存储类型> < 数据类型> (* <指针变量名>)[表达式] ;

例如, int a[2][3]; int (*p)[3];

方括号中的常量表达式表示指针加1 , 移动几个数据。

当用行指针操作二维数组时,表达式般写成1行的元素个数,即列数。


1、练习1:编程实现使用行指针表示二维数组int a[3][2]的元素是 a[1][1]

#include <stdio . h>
int main(int argc,char *argv[])
{
int a[3][2] = {
    
    {1, 6},{9, 12},{61,12}};
int (*p)[2];
p=a;
printf("%p %p\n", a, a+1);
printf("%p %p\n", p, p+1);
printf( "%d, %d, %d, %d\n”, a[1][1], p[1][1], *(*(a + 1)+1), *(*(p + 1) +1);
return 0;      //a+1数组先移动一行,加*从行指针变为一级指针,+1再移动一列,加*再取值
}                               
输出结果:
0xbfb52e58 0xbfb52e60
0xbfb52e58 0xbfb52e60 
12,12,12,12

2、练习2:编程实现使用行指针遍历打印出二维数组int a[3][2]的元素

#include <stdio . h>
int main(int argc,char *argv[])
{
int a[3][2] = {
    
    {1, 6},{9, 12},{61,12}};
int (*p)[2];
p=a;
printf("%p %p\n", a, a+1);
printf("%p %p\n", p, p+1);
for(i=0;i<3;i++){
for(j=0;j<2;j++)
printf( "%d, %d, %d, %d\n”, a[i][j], p[i][j], *(*(a + i)+j), *(*(p + i) +j);
}
return 0;      //a+1数组先移动一行,加*从行指针变为一级指针,+1再移动一列,加*再取值
}                               
输出结果:

0xbfb52e58 0xbfb52e60
0xbfb52e58 0xbfb52e60 
1,1,1,1,6,6,6,6
9,9,9,9,12,12,12,12,
61,61,61,61,12,12,12,12

五、字符指针与字符串

1、定义与使用

C语言通过使用字符数组来处理字符串。通常,我们把char数据类型的指针变量称为字符指针变量。字符指针变量与字符数组有着密切关系,它也被用来处理字符事。

#include <stdio.h>
int main(int argc, char *argv[])
{
char ch='A';
char * p;
p = &ch;
printf("%C %c\n", ch, *p);
return 0;
}
输出结果:
A A

初始化字符指针是把内存中字符串的首地址赋予指针,并不是把该字符串复制到指针中

char str[] = "Hello World" ;

char *p =str;

在C编程中,当一个字符指针指向一个字符串常量时,不能修改指针指向的对象的值

例如:char *p="Hello World" ;

*p ='h' ;//错误,字符串常量不能修改

全局变量、static变量、字符串常量放在静态区,程序结束才会结束。

2、练习、不利用任何字符串函数,编程实现字符串连接函数的功能。

int main(int argc, char *argv[])
{
char ch[108] = "welcome" ;
char * p = "hello world!", * q;
int i=0;
q=p;              //保留一下p
while (*(ch+1) != '\0 ) 
i++;
while (*p != '\0') {
(ch+i) = *p;
i++;
p++;
}
*(ch+i) = *p;   //赋以'\0'
p=q;           //保留p
puts(ch);
puts(p);
return 0;
}
输出结果:
welcomehello World!
hello World!

六、指针数组

1、定义与使用

所谓指针数组是指由若干个具有相同存储类型和数据类型的指针变量构成的集合。

指针数组的一般说明形式:

<存储类型> <数据类型> *<指针数组名>[<大小>];

指针数组名表示该指针数组的起始地址.

#include <stdio.>
int main(int argc, char *argv[])
{
int * p[3];//int *....p[0] p[1] p[2]
int a[]={3,6,1,9,18};
p[0] = a;
p[1]=a+1;
p[2]=a+3;
printf( "%d %d %d\n", a[0], a[1], a[3]);
printf"%d %d %d\n",*(p[0]), *(p[1]), *(p[2]);  //[]的优先级大于*,所以()可省略
return 0;
}
输出结果:
3 6 9
3 6 9
#include <stdi0.h>
int main(int argc, char *argv[]) 
int a[2][3] = {
    
    {1, 4, 6},{12, 9, 7}};
int *p[2];
p[0]= a[0];//&a[0][0] 
p[1]= a[1];//&a[1][0]
printf("%d\n", a[0][1]);
printf( %d\n", *(a[0]+1));
printf( %d\n", *(p[0]+1));
return 0;
输出结果:
4
4
4

2、编程:利用指针数组处理一个二维数组 ,要求遍历二维数组所有元素。

#include <stdi0.h>
int main(int argc, char *argv[])
{
int a[2][3] = {
    
    {2, 4, 6},{12, 9, 7};//----a[0] a[1]
p[2] = {a[0], a[1]};
int 1, j;
//printf("%p %p\n", p[0],a[0]); 
//printf("%p %p\n", p[1],a[1]);
for(i=0;i<2;i++){
for(j=0;j<3;j++)
//printf("%d %d ",(a[i) + j),*(p[i]+j));
printf( "%d %d, *(*(a+i) + j),*(*(p+i)+j));
puts(" " ) ;
}
return0;
}

3、编程:利用指针数组处理一个二维数组 ,要求求出二维数组所有元素的和。

#include <stdi0.h>
int main(int argc, char *argv[])
{
int a[2][3] = {
    
    {2, 4, 6},{12, 9, 7};//----a[0] a[1]
p[2] = {a[0], a[1]};
int 1, j,sum=0;
//printf("%p %p\n", p[0],a[0]); 
//printf("%sp %p\n", p[1],a[1]);
for(i=0;i<2;i++){
for(j=0;j<3;j++)
//printf("%d %d ",(a[i) + j),*(p[i]+j));
sum+=*(p[i]+j);
printf( "sum=%d , sum);
puts(" " ) ;
}
return 0;
}
输出结果:sum=39

七、多级指针

1、定义与使用

把一个指向指针变量的指针变量,称为多级指针变量。

对于指向处理数据的指针变量称为一级指针变量,简称一级指针。

而把指向一级指针变量的指针变量称为二级指针变量,简称二级指针。

二级指针变量的说明形式如下

<存储类型> <数据类型> **<指针名> ;

#include <stdio. h>
int main(int argc, char *argv[])
{
int m=10;
int * p; 
int* * q;
p=&m;
q=&p;
printf("%p %p\n", p,&m);
printf("%p %p\n",q,&p);
printf("%d %d %d\n", m, *p, **q);
return 0;
}
输出结果:
0xbfde5884 0xbfde5884
0xbfde5888 0xbfde5888
10 10 10
#include <stdio. h>
int main(int argc, char *argv[])
{
int a[]={3,6,9);
int
p[2] = {&a[0], &a[1]};int ** q;
q = &p[0];//p 
q=p;
printf("%d %d\n", a[0], a[1]);
printf("%d %d\n", *p[0], *p[1]);
printf("%d %d %d\n", a[0], *p[0], **q);
printf("%d %d %d\n", a[1],*p[1], **(q+1));
return 0;
}

2、练习:指针数组的打印

#include <std1o. h>
int main(int argc, char *argv[])
{
//char s1 . "apple";
//char s2 = "pear";
char * s[] = {"apple", "pear”, "potato" }
int i, n;
i=0;
n = sizeof(s) / sizeof(char *);
p=&s[0];  //p=s;
while (i < n){
printf("%s %s\n", s[i],p[i]);
}
return 0;
}

七、void 指针和const修饰符

1、定义与使用

void指针是一种不确定 数据类型的指针变量,它可以通过强制类型转换让该变量指向任何数据类型的变量。一般形式为:void *<指针变量名称> ;

对于void指针,在没有强制类型转换之前,不能进行任何指针的算术运算

#include <stdio . h>
int main(int argc,char *argv[])
{
int m=10;
double n = 3.14;
void *p,*q;
p=&m;//p = (void * )&m;
printf("%d %d\n", m, *(int *)p);
q=&n;//q = (void *)&n;
printf("%.2lf %.2lf\n", n, *(double *)q);
return 0;
}
输出结果:  10  10
          3.14    3.14

2、练习:用void 指针遍历一维数组。

#include <stdio . h>        //方法一
{
int main(int argc, char *argv[])
inta[]={5,9,1,6,9,10};
int i, n;
void * p;
p=a;
n = sizeof(a) / sizeof(int);
for(1=0;1<n;1++)
printf("%d, *((int *)p + i);
puts("");
return 0 ;
}
#include <stdio . h>            //方法二
{
int main(int argc, char *argv[])
{
inta[]={5,9,1,6,9,10};
int i, n;
void *q;
int *p;
q= a;
p = (int *)q;
n = sizeof(a) / sizeof(int);
for(i=0;i<n;i++)
printf("%d”, *(p + i));
puts("");
return 0 ;
}

3、const

const变量:常量化变量的值

const <数据类型>变量名= [<表达式>];

常量化变量是为了使得变量的值不能修改。

变量有const修饰时,若想用指针间接访问变量,指针也要有const修饰。

常量化指针目标表达式

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

常量化指针目标是限制通过指针改变其目标的数值, 但<指针变量>存储的地址值可以修改。

常量化指针变量

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

使得<指针变量>存储的地址值不能修改。但可以通过* <指针变量名称>修改指针所指向变量的数值。

常量化指针变量及其目标表达式

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

常量化指针变量及其目标表达式,使得既不可以修改<指针变量>的地址,也不可以通过* <指针变量名称>修改指针所指向变量的值。

猜你喜欢

转载自blog.csdn.net/qq_52049228/article/details/129646483