嵌入式软件--C语言高级 DAY 9 指针

指针是C语言学习的难点所在,所以一定要按部就班跟着课程做笔记,细化分知识点,将基础打扎实,回过头来会发现难度会小很多。

1.指针的理解

(1)变量的访问方式

内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的 ,为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元通常占用1个字节。

变量在内存中分配空间,不同类型的变量占用不同大小的空间,那如何访问内存中的变量数据呢?有两种方式:

  1. 直接访问,直接使用变量名进行的访问,以前的程序中都是采用这种方式。
  2. 间接访问,通过指针来实现。

(2)内存地址

为了能够有效地访问到每个内存单元,就给内存单元进行了编号,这些编号被称为内存地址,因为每个内存单元都有地址,所以变量也是有地址的。

假设有 int 型变量 num,其在内存中会占用4个字节,也就是占用4个内存单元,第一个内存单元的地址即是变量 num 的地址。如图;

在32位系统中,内存地址通常是32位二进制数字,即4个字节,这允许寻址2^32(大约 4GB)个内存位置。

在64位系统中,内存地址通常是64位二进制数字,即8个字节,这允许寻址2^64个内存位置,这个数量相当大,远远超过了通常需要的内存大小。

(3)什么是指针

如果一个变量专门用来存放内存地址,则它称为指针变量,通常简称为指针。我们可以通过指针间接访问内存中另一个数据。

如图,指针里面存储的是变量 num 的地址,我们可以说该指针指向变量 num,通过该指针可以间接访问变量 num。

(4)指针的定义

数据类型 *指针变量名 [=初始地址值];

数据类型是指针所指向的地址处的数据类型,如 int、char、float 等。

符号 * 用于通知系统,这里定义的是一个指针变量,通常跟在类型关键字的后面,表示指针指向的是什么类型的值。比如,char  * 表示一个指向字符的指针,float * 表示一个指向浮点数的指针。

需要注意的是,以下三种定义指针的写法都是合法的:

int *ptr;

int* ptr;

int * ptr;  

(5)取址运算符和取值运算符

取址运算符,使用 & 符号表示,作用是取出变量的内存地址。如果要格式化输出地址,需使用格式占位符 %p。

取值运算符,使用 * 符号表示,作用是取出指针指向的内存地址处的数据值,也称为解引用运算符或间接引用运算符。

(6)指针应用案例

创建一个int类型的变量,使用取址运算符取出其地址,并将其地址赋值给一个指针,然后分别打印变量的值、变量的地址、指针的值、指针的地址、指针指向的值。 

#include <stdio.h>

int main()
{
    // 定义变量
    int num = 100;
    // 定义一个指针,指向变量的地址。取出变量的地址赋值给指针
    int *ptr = &num;
    //打印变量的值和地址
    printf("%d\n",num);
    printf("%p\n",&num);
    //打印ptr(存入的是地址)的值,地址和指向的值
    printf("%p\n",ptr);
    printf("%p\n",&ptr);
    printf("%d",*ptr);
    return 0;
}

(7)通过指针修改所指向的值

#include <stdio.h>

int main()
{
    int num = 100;
    // 定义一个指针
    int *ptr;
    ptr=&num;
    *ptr=999;//通过指针修改num的值
    printf("指针所指向的值:%d\n指针所存的地址:%p\n num的值:%d",*ptr,ptr,num);
    return 0;
}

2.指针运算

(1)指针加减整数

指针与整数的加减运算,表示指针所指向的内存地址的移动(加,向后移动;减,向前移动),指针移动多少,与指针指向的数据类型有关,数据类型占据多少个字节,每单位就移动多少个字节,比如一个 int 类型指针,+1 向后移动 4 个字节,-2 向前移动 8 个字节。

数组的元素在内存中连续存储的,我们通过数组元素来演示指针加减整数的情况。

#include <stdio.h>

int main()
 {
//     int num = 100;
//     // 定义一个指针
//     int *ptr;
//     ptr=&num;
//     *ptr=999;//通过指针修改num的值
//     printf("指针所指向的值:%d\n指针所存的地址:%p\nnum的值:%d",*ptr,ptr,num);
// 定义一个数组
int arr[] = {10, 20, 30, 40, 50};
// 定义指针
int *ptr = &arr[0];
printf("输出数组首元素的地址:%p\n", ptr);
int n = 0;
while (n < 5)
{
    printf("输出地址:%p\n所指向的值是:%d\n", ptr,*ptr);
    ptr++;
    n++;
}
return 0;
}

(2)指针自增、自减

指针自增、自减本质上就是指针加减整数,自增地址后移,自减地址前移。下面我们利用指针的自增自减实现数组的遍历,代码如下:

#include <stdio.h>

int main()
 {
//     int num = 100;
//     // 定义一个指针
//     int *ptr;
//     ptr=&num;
//     *ptr=999;//通过指针修改num的值
//     printf("指针所指向的值:%d\n指针所存的地址:%p\nnum的值:%d",*ptr,ptr,num);
// 定义一个数组
int arr[] = {10, 20, 30, 40, 50};
// 定义指针
int *ptr = &arr[0];
const int len=5;
for(int n=0;n<len;n++)
{
    printf("输出索引:%d 输出地址:%p 所指向的值是:%d\n",n,ptr,*ptr);
    ptr++;//指针自加

}
for(int m=len-1;m>=0;m--)
{
    ptr--;//指针自减
    printf("输出索引:%d 输出地址:%p 所指向的值是:%d\n",m,ptr,*ptr);
    

}
return 0;
}

从结果也可以看出,数组元素之间紧密相连,地址十六进制相差一个类型的字节长度。

(3)同类型指针相减

相同类型的指针可以进行减法运算,返回它们之间的距离,即相隔多少个数据单位。高位地址减去低位地址,返回的是正值;低位地址减去高位地址,返回的是负值。

同类型指针相减的结果是一个 ptrdiff_t 类型数据,ptrdiff_t 类型是一个带符号的整数,格式输出中对应的格式占位符是 %td

#include <stdio.h>
int main()
{
    //定义数组
    int arr[]={12,34,56,978,13};
    //定义指针
    int *ptr1=&arr[0];//指向第一个元素对应的地址
    int *ptr2=&arr[4];//指向第五个元素对应的地址
    printf("ptr2-ptr1=%td\n",ptr2-ptr1);//指针相减,输出差值,代表相隔多少个数据单位
    printf("ptr1-ptr2=%td\n",ptr1-ptr2);
    //定义两个double类型
    double a=1.2;
    double b=2.1;
    double c=3.1;
    double *p1=&a,*p2=&b,*p3=&c;
    printf("p2-p1:%td\n",p2-p1);
    printf("p1-p2:%td\n",p1-p2);
    printf("p2-p3:%td\n",p2-p3);
    printf("p1-p3:%td",p1-p3);
    return 0;
}

(4)指针的比较运算

指针之间可以进行比较运算,如 ==、<、 <= 、 >、 >=,比较的是各自指向的内存地址的大小,返回值是 int 类型整数 1 (true)或 0  (false)。案例演示如下:

#include <stdio.h>
int main()
{
    //定义数组
    int arr[]={12,34,56,978,13};
    //定义指针
    int *ptr1=&arr[0];//指向第一个元素对应的地址
    int *ptr2=&arr[4];//指向第五个元素对应的地址
    //定义两个double类型
    double a=1.2;
    double b=2.1;
    double c=3.1;
    double *p1=&a,*p2=&b,*p3=&c;
    printf("ptr1>ptr2=%d\n",ptr1>ptr2);//指针间的比较会输出0(flase)  1(true)
    printf("p1>ptr1=%d",p1>ptr1);//不同的类型进行比较,会警告
    return 0;
}

3.指针数组

(1)数组名

数组名在大多数情况下会被隐式地转换为指向数组第一个元素的指针,在特定情况下数组名可以被视为一个指针,具有一些指针的特性。

数组名可以被视为一个指针,但是它不是。指向的是数组中首个元素的地址。

但是数组名与真正的指针是不同的,主要有以下几点区别:

  1. 使用 sizeof 运算符,数组名得到的是整个数组的大小;指针得到的是本身的大小。
  2. 数组名不能进行自增、自减运算。
  3. 数组名的指向不可更改。
#include <stdio.h>

int main()
{
    int num[]={10,20,30,50,60};
    int *ptr=&num[0];
    printf("%p\n",ptr);
    printf("指向数组名的地址:%p  指向数组名的值%d",num,*num);//数组名的地址与首个元素的地址是一样的,指向的值也是首元素的值
    return 0;
}

(2)指针数组

指针数组(Pointer Array)是一个数组,其中的每个元素都是指针。

语法规则:数据类型 *指针数组名[长度];

#include <stdio.h>
int main()
{
    int num1=100,num2=200,num3=400;
    //定义指针数组
    int *arr[3];
    arr[0]=&num1;
    arr[1]=&num2;
    arr[2]=&num3;
    for(int i=0;i<3;i++)
    {
        printf("索引:%d 指针指向的地址%p 指针指向的值 %d\n",i,arr[i],*arr[i]);
    }
    return 0;
}

4.数组指针

数组指针(Array Pointer)是一个指针,它指向一个数组。注意,数组指针指向的是整个数组的地址而不是第一个元素的地址,虽然二者值是相同的,但在运算中会表现出不同。

语法规则:数据类型 (*数组指针名)[长度];

#include <stdio.h>

int main()
{
    //创建数组
    int arr[5]={12,24,45,676,56};
    //创建数组指针
    int (*ptr)[5]=&arr;//&arr是整个数组的地址
    printf("%p\n",arr);
    printf("%p\n",ptr);//两者的地址是一样的
    //数组指针+1会向后移动4*5=20个字节;数组+1会向后移动4个字节
    printf("arr+1=%p\n",arr+1);
    printf("ptr+1=%p\n",ptr+1);
    //使用数组指针遍历数组
    for(int i=0;i<5;i++)
    {
        printf("%d\n",(*ptr)[i]);
    }
    return 0;
}

数组指针和数组名的区别:

  1. 指向不同:数组名指向元素首地址,数组指针指向数组的地址。
  2. 类型不同:上面案例中,arr 的类型是 int[5],;ptr 的类型是 int(*)[5]。
  3. 可变性:数组名通常是不可变的;数组指针是可变的,你可以将它指向不同的数组。
  4. 初始化:数组名不需要显式初始化,它会自动指向数组的首元素;数组指针需要显式初始化,指定它将指向的数组。
  5. 访问元素:数组名访问数组元素不需要解引用;数组指针通常需要解引用才能访问数组元素。

5.字符指针

(1)基本概念

字符指针变量(简称字符指针)是C语言中的一种指针类型,它用于指向字符或字符串(字符数组),通常用于处理字符串(字符数组)。

字符指针指向一个字符串

char *pStr= "hello tom"; //指针指向字符串的第一个字符,也就是首个元素

pStr是指针,存储的是字符串首个元素“h"的地址。

C语言对字符串" hello tom"是按字符数组处理的,在内存中开辟了一个字符数组用来存放字符串,程序在定义字符串指针pStr时只是把字符串首地址(即存放字符串的字符数组的首地址)赋给pStr。

(2)字符数组名和字符指针表示字符串的区别

对字符数组只能对各个元素赋值,不能对字符数组名重新赋值。

char str[14];

str=" hello tom"; //错误

str[0] = 'i'; //ok

字符指针是可变的,允许使用下面方法重新赋值,指向新的字符串。

char *a = "hello tom";

a = "tom";

#include <stdio.h>

int main()
{
    //创建字符指针
    char *ptr="hello,world!";
    //通过指针修改字符串;
    ptr="good day!";
    printf("%c\n",*ptr);//ptr存储的是首元素g的地址,返回的值是g
    printf("%s",ptr);//%s进行位运算,寻找ptr指向g的地址,输出整个字符串
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值