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

1.指针和函数

(1)传递指针给函数

当函数的形参类型是指针类型时,使用该函数时,需要传递指针、地址或者数组给该形参。

代码示例:传递指针或地址给函数。

#include <stdio.h>
//函数原型声明
void fun(int *);
int main()
{
    //定义变量
    int num=100;
    //定义指针
    int *ptr=&num;
    //传递地址给函数
    fun(&num);
    printf("%d\n",num);
    //传递指针
    fun(ptr);
    printf("%d",*ptr);
    return 0;
}
void fun(int *p)
{
  *p+=1;
}

(2)传数组给函数

数组名本身就代表数组首地址,因此传数组的本质就是传地址。

C语言中没有指针,数组传递不了给函数。

#include <stdio.h>
int fun(int *, int );
int main()
{
    int arr[5] = {11, 23, 44, 34, 45};
    int *ptr=&arr[0];
    int length = 5;
    printf("%d\n", fun(ptr, 5));
    return 0;
}
int  fun(int *ary, int length)
{
    for (int i = 0; i < length; i++)
    {
        
        printf("%d\n",*(ary+i));//printf("%d\n",ary[i])       
    }
    return *ary;
}

#include <stdio.h>
int fun(int *, int );
int main()
{
    int arr[5] = {11, 23, 44, 34, 45};
    int *ptr=&arr[0];
    int length = 5;
    fun(arr,5);
    printf("%d\n", arr[1]);
    return 0;
}
int  fun(int *ary, int length)
{
    ary[1]=24;//在函数中通过指针修改数组的值,在main函数中也会修改。
              //因为fun函数中的指针,指向的就是main函数中的arr数组

    return *ary;
}

(3)指针函数(返回指针的函数)

如果函数的返回值是一个指针,这样的函数称为指针函数。

语法规则:返回类型 *指针函数名(参数列表)

<1>请编写一个函数 strlong(),返回两个字符串中较长的一个.

#include <stdio.h>
#include <string.h>
char *strlong(char *str1, char *str2)
{
    return strlen(str1) > strlen(str2) ? str1 : str2;//返回的是指针
}
int main()
{
    char str1[100];
    char str2[100];
    char *ptr = NULL;
    printf("请输入字符串:");
    scanf("%s", &str1);
    printf("请输入字符串:");
    scanf("%s", &str2);
    ptr = strlong(str1, str2);
    printf("%s", ptr);
    return 0;
}

<2>返回值不能指向局部变量

函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、局部数组和形式参数,所以函数返回的指针不能指向这些数据。

如果确实有这样的需求,需要定义为静态局部变量。

#include <stdio.h>
int *fun()
{
    static int num=100;//延长周期
    return &num;
}
int main()
{
    int *ptr=fun();
    int n=*ptr;
    printf("%d",n);
    return 0;
}

输出100

如果不用static,局部变量无法在主函数区域内声明。

<3>编写一个函数,它会生成10个随机数,并使用数组名作为返回值。

说明:生成随机数可以使用 rand() 函数,由C语言标准库 <stdlib.h> 提供,可以返回一个随机整数。

示例:

#include <stdio.h>
#include <stdlib.h>

// 函数返回一个数组
int *fn()
{
static int arr[10]; //  必须加上static ,让arr 的空间在静态数据区分配

 for (int i = 0; i < 10; i++)
 {
 arr[i] = rand();
 }

 return arr;
}

int main()
{
 int *ptr = fn(); // p 指向是在 f1 生成的数组的首地址(即第一个元素的地址)  
 for (int i = 0; i < 10; i++)
 {
 printf("第%d号元素%d\n",i,ptr[i]);
 }

 return 0;
}

我的:

#include <stdio.h>
#include <stdlib.h>
int *fn()
{
    // 定义数组
    static int arr[10]; // 便于主函数内调用,加静态static延长生命周期
    for (int i = 0; i < 10; i++)
    {
        arr[i] = rand(); // 循环遍历随机整数
    }
     return arr;      // 返回值不是数组,应是数组首元素的地址,故函数定义为指针类型
}
int main()
{
    int *vay = fn(); // 返回值是数组首元素地址,需要以指针接收
    for (int i = 0; i < 10; i++)
    {
        printf("第%d个元素输出整数 %d\n",i,vay[i]); // array[i]或者*(array++)
    }
    return 0;
}

(4)函数指针

一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常类似。

把函数的这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数,这种指针就是函数指针。

语法规则:

返回类型 (*函数指针名)(参数列表);  int (*ptr)(int )

注意:()的优先级高于*,第一个括号不能省略,如果省略,就成了指针函数。

示例代码:

用函数指针来实现对函数的调用,返回两个整数中的最大值。

#include <stdio.h>
//定义函数
int max(int a,int b)
{ 
    return a>b?a:b;

}
int main()
{
    //函数指针还是指针,存的是函数的地址,听过地址找到并调用函数
    int (*ptr)(int a,int b)=max;
    printf("%d",(*ptr)(11,34));
    return 0;
}

函数的指针,对嵌入式开发来说,实际用处不大,以上例子完全可以直接调用函数即可。

(5)回调函数

函数指针可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。

简单的讲,回调函数是由别的函数执行时调用你传入的函数。

示例代码:

使用回调函数的方式,给一个整型数组int arr[10] 赋10个随机数。

#include <stdio.h>
#include <stdlib.h>
int *fun(int *ptr,int (*sn)())
{
    for(int i=0;i<10;i++)
    {
        ptr[i]=(*sn)();
    }
    return ptr;
}
int main()
{
    //初始化一个数组
    int arr[10];
    //函数
    fun(arr,rand);//传rand函数,用到函数指针。
    for(int i=0;i<10;i++)
    {
        printf("元素:%d  值是:%d\n",i,arr[i]);
    }
    return 0;
}

明悟:数组和函数要想传参到定义函数,需要用到指针。指针函数能够通过返回指针或地址来赋值数组的值,函数指针则能通过指针寻找到函数。

2.多级指针

(1)介绍

指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。

指针也是有地址的,并且和指向数值的地址不同。

(2)多级指针的定义与使用

声明多级指针时,需要使用多个星号来表示指针的级别。

int *ptr;       // 一级指针

int **pptr;     // 二级指针

int ***ppptr;   // 三级指针

初始化多级指针时,你需要逐级给指针赋值,确保每个级别的指针指向正确的目标。

int var;

int *ptr = &var;        // 一级指针指向 var

int **pptr = &ptr;      // 二级指针指向 ptr

int ***ppptr = &pptr;   // 三级指针指向 pptr

解引用多级指针时,需要根据指针的级别使用适当数量的星号解引用操作。

printf("Value of var: %d\n", var);

printf("Value of ptr: %d\n", *ptr);        // 解引用一次

printf("Value of pptr: %d\n", **pptr);     // 解引用两次

printf("Value of ppptr: %d\n", ***ppptr);  // 解引用三次

(3)案例演示:

#include <stdio.h>

int main()
{
    int a=100;
    int *p1=&a;//一级指针,存a的地址
    int **p2=&p1;//二级指针,存指针p1的地址
    int ***p3=&p2;//三级指针,存二级指针p2的地址
    printf("%d\n",*p1);
    printf("%d",**p2);//虽然指向的地址不同,但值都是一样的
    return 0;
}

3.空指针

赋为NULL 值的指针被称为空指针,NULL 是一个定义在标准库 <stdio.h>中的值为零的宏常量。

声明指针变量的时候,如果没有确切的地址赋值,为指针变量赋一个NULL 值是好的编程习惯。

int *ptr=NULL;

4.野指针

野指针就是指针指向的位置是不可知(随机性,不正确,没有明确限制的)

(1)野指针的成因:

1.指针使用前未初始化

指针变量在定义时如果未初始化,其值是随机的,此时操作指针就是去访问一个不确定的地址,所以结果是不可知的。此时p就为野指针。

int *p;

printf("%d\n", *p);

在没有给指针变量显式初始化的情况下,一系列的操作(包括修改指向内存的数据的值)也是错误的。

int *p;

*p = 10;

2.指针越界访问

int arr[4] = {10,20,30,40};

int *p = arr;

p += 4;

printf("%d", *p);   // 此时 *p 即为越界访问

当 p += 4之后,此时 *p 访问的内存空间不在数组有效范围内,此时 *p 就属于非法访问内存空间,p为野指针。

3.指针指向已释放的空间

#include <stdio.h>

int *test()

{

    int a = 10;

    return &a; //&a=0x0012ff40

}

int main()

{

    int *p = test();

    printf("%d", *p);

    return 0;

}

调用test()函数将返回值赋给p,test函数的返回值是局部变量a的地址,函数调用结束局部变量会被销毁,其所占用的内存空间会被释放,p 指向的是已经释放的内存空间,所以 p 是野指针。

(2)避免野指针

  1. 指针初始化如果没有确切的地址赋值,为指针变量赋一个 NULL 值是好的编程习惯。
  2. 小心指针越界。
  3. 避免返回指向局部变量的指针。
  4. 指针使用之前检查指针是否为 NULL。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值