2021-09-25 C语言 指针专题(一)

本文详细介绍了C语言中的指针概念,包括指针在内存中的作用,指针变量的声明和初始化,以及指针的运算如算术运算和关系运算。此外,还探讨了指针与数组的关系,特别是指针如何用于遍历和操作一维及二维数组。

Day6指针专题(一)

指针
1.C程序设计中使用指针可以:
(1)使程序简洁、紧凑、高效
(2)有效的表示复杂的数据结构
(3)动态分配内存
(4)得到多于一个的函数返回值
2.在计算机内存中,每个字节单元(1Byte=8Bits)都有一个编号称为地址;在C语言中,内存单元的地址称为指针,专门用来存放地址的变量称为指针变量。在不影响理解的情况下,有时对地址、指针和指针变量不区分统称指针。
3.指针变量的说明:
(1)一般形式:
<存储类型> <数据类型> * <指针变量名>
例:

char * pName ;

注:
① 指针的存储类型是指指针变量本身的数据类型
② 指针说明时指定的数据类型不是指针变量本身的数据类型,而是指针目标的数据类型。简称指针的数据类型。
(2)指针在说明时也可以被赋予初值,称为指着的初始化
一般形式:
<存储类型> <数据类型> * <指针变量名> = <地址量>
例:

int a = 10 ;
int a , *pa = &a ; //int *pa ; pa = &a ;
//在上面的语句中,把变量a的地址作为初值赋予了刚说明的int型指针pa

例:
地址	存储数据	描述&a:0xbf98c768	00000000	存储数据10,即a、*pa、*(&a)0xbf98c769	00000000	0xbf98c76a	00000000	0xbf98c76b	00010010	……	……	……&pa:0xbf3be5c	10111111(bf)	存储a的地址0xbf98c768,即pa0xbf3be5d	10011000(98)	0xbf3be5e	11000111(c7)	0xbf3be5f	01101000(68)

4.指针变量的使用
(1)指针指向的内存区域中的数据称为指针的目标,如果它指向的区域是程序中的一个变量的内存空间,则这个变量称为指针的目标变量,简称指针的目标(指针指向的地址空间里存放的数据)
*pa = *(&a) = a (通过“*”号可以找到指针的目标,即指针目标区域里存放的数据)

引用指针要注意程序中的pa、*pa、&pa三种表示方法的不同意义。设pa为一个指针则:
①pa——指针变量,它的内容是地址
②*pa——指针所指向的对象,它的内容是指针目标的数据
③&pa——指针变量占用的存储区域的地址,即存储指针变量的地址,是个常量

(2)指针的赋值运算是指通过赋值运算符向指针变量赋予的一个地址值,向一个指针变量赋值时,赋予的值必须是地址常量或者指针变量,不能是普通的整数(零除外)。

指针赋值运算,常见的形式有以下几种:
①把一个普通变量的地址赋给一个具有相同数据类型的指针

double x = 15.0 , *px ;
px = &x ;

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

float a , *px , *py ; 
px = &a ;
py = px ;

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

int a[20] ,*pa ;
pa = a ; //等价于pa = &a[0] ;

例:

#include <stdio.h>

int main(int argc, char *argv[])
{
        int a = 10;
        int * p;
        int * q;

        p = &a;
        //q = &a;
        q = p;

        printf("&p:%p %d\n", &p, sizeof(p));
        printf("%p %p\n", p, &a);
        printf("%d %d\n", a, *p);

        printf("\n\n&q:%p %d\n", &q, sizeof(q));
        printf("%p %d\n", q, *q);

        return 0;
}

指针运算
指针运算是以指针变量所存放的地址量作为运算量而进行的运算。
指针运算实质就是地址的运算。
指针运算的种类是有限的,它只能进行赋值运算、算术运算和关系运算。
1.指针的算术运算
在这里插入图片描述

注:
① 不同数据类型的两个指针实行加减整数运算是无意义的
② px+n表示实际位置的地址量是:(px) + sizeof(px的数据类型) *n ;px-n表示实际位置的地址量是:(px) - sizeof(px的数据类型) *n
③ 两指针相减运算,px-py运算的结果是两指针指向地址位置之间相隔数据的个数,因此,两指针相减不是两指针持有地址量相减的结果。即两指针相减的结果值不是地址量,而是一个整数值,表示两指针之间相隔数据个数。
例:

#include <stdio.h>

int main(int argc, char *argv[])
{
        int a[5] = {4, 8, 1, 2, 7};
        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);//3

        return 0;
}

#include <stdio.h>

int main(int argc, char *argv[])
{
        int a[5] = {4, 8, 1, 2, 7};
        int *p = NULL, *q;

        printf("%d %p\n", p, p);
        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;
}

2.指针的关系运算符
在这里插入图片描述

注:
① 两指针之间的关系运算表示它们指向地址位置之间的关系。指向地址大的指针大于指向地址小的指针。
② 指针与一般整数变量之间的关系运算没有意义,但可以和零进行等于或不等于的关系运算判断指针是否为空。

3.程序举例
(1)

int main(){
int a[] = {5,8,7,6,2,7,3} ;
int y , *p = &a[1] ;

y = (*--p)++ ;

printf(%d_”,y) ;
printf(%d\n”,a[0]) ;

return 0 ;
}
//结果:5_6

(2)

#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 = 0; i < N; i++) 
                scanf("%d", p++);//&a[i],warning!!!

        p = a;
        for (i = 0; i < N; i++)  {
                printf("%d ", *p++);
        }
        puts("");


        return 0;
}

问:指针加n移动多少个字节?
答:移动sizeof(指针数据类型) *n个字节

指针与数组
在C语言中,数组的指针是指数组在内存中的起始地址,数组元素的地址是指数组元素在内存中的起始地址。
1.一维数组
一维数组的数组名为一维数组的指针(起始地址)
例:

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("");

        p++;

        printf("%d\n", p[1]);//*(p+1)

        return 0;
}

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

x++ ; //错误
px++ ; //正确

例:

int a[] = {1,2,3,4,5,6,7,8,9} , *p = a , i ;
/*
数组元素地址的正确表示是:
① &(a+1) //错,表示a[1]的值
② a++ //错,数组名为地址常量不能自增
③ &p //错,表示指针变量的地址
④ &p[i] //对,表示a[i]的地址
 数组名是地址变量:
① p++,p-- //对
② a++,a-- //错
③ a+1,*(a+2) //对
*/

2.程序举例
编写一个函数将整型数组中n个数按反序存放。

#include <stdio.h>

int main(int argc, char *argv[])
{
        int a[] = {1, 6, 9, 12, 61, 12, 21};
        int *p, *q, n, t;

        n = sizeof(a) / sizeof(int);

        p = a;
        q = &a[n-1];

        while (p < q) {
                t = *p;
                *p = *q;
                *q = t;
                p++;
                q--;
        }
        
        for (t = 0; t < n; t++)
                printf("%d ", a[t]);
        puts("");


        return 0;
}

指针与二维数组
1.多维数组就是具有两个或两个以上下标的数组。在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];
//p = a ;指针类型不兼容
        printf("%p %p\n", p, p+1);
        printf("%p %p\n", a, a+1);

        for (i = 0; i < n; i++)
                printf("%d ", *(p+i));
        puts("");


        return 0;
}

2.可把二维数组看作由多个一维数组组成。
比如 int a[2][3] ,内含三个元素(一维数组)a[0]、a[1]、a[2](元素a[0]、a[1]、a[2]都是一维数组名)。二维数组名代表数组的起始地址,数组名加1就是移动1行元素。因此二维数组名也称作行地址。

a == a[0] == *(a + 0) == &a[0][0]
a + 1 == a[1] == *(a + 1) == &a[1][0]
a + 2== a[2] == *(a + 2) == &a[2][0]
a[0] + 1 == *(a + 0) + 1 == &a[0][1]
a[1] + 1 == *(a + 1) + 1 == &a[1][1]
a[2] + 1 == *(a + 2) + 1 == &a[2][1]
//“*”作用:改变指针的性质,将行指针降级为一级指针。

例:

#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);

        //a[1]++;
        //printf("%d\n", sizeof(a[1]));

        p = a[0]; //&a[0][0];
        printf("%p %p\n", p, p+1);
        printf("%p %p\n", a, a+1);

        for (i = 0; i < n; i++)
                printf("%d ", *(p+i));
        puts("");


        return 0;
}

3.行指针(数组指针)
存储行地址的指针变量,叫做行指针变量。形式如下:
<存储类型> <数据类型> (*<指针变量名>) [表达式]
例:

int a[2][3] ;
int (*p)[3] ; // 声明一个指针p,p+1表示移动3个地址(一行)

方括号中的常量表达式表示指针加1,移动n个数据。当用行指针操作二维数组时,表达式一般写成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], i, j;

        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));
        for (i = 0; i < 3; i++) {
                for (j = 0; j < 2; j++)
                        printf("%d, %d, %d, %d ", a[i][j], p[i][j], *(*(a + i)+j), *(*(p + i) + j));
                puts("");
        }


        return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值