C语言指针应用

C语言指针应用


指针介绍

指针含义介绍的文章很多,这里不多做赘叙,一言而概之就是存储一个地址的变量,存储的地址所指向的内存内可以存储任何信息,如一个整数(整数指针)、一个函数(函数指针)或者另一个指针(多重指针)。
话不多说,直接上干货。


指针的常见使用场景

下面将通过实际的代码来说明一些指针的常用场景。

1.函数内部需要修改参数的值

大家都应该知道函数传递的参数是一个拷贝,并不是其本身,如果在函数内部想要修改函数外部的变量值,就需要用到指针;这也是指针最常用的使用方法,函数内部需要修改外部值时需要传递外部参数的指针给到函数,最常见的例子为C标准库的scanf函数:

int scanf(const char* format, ...);
#include <stdio.h>

int mian(int argc, char* argv[])
{
    int number;
    printf("Plase enter a nember:\n");

    if(scanf("%d", &number) == 1){
        printf("The number is %d\n", number);
    }
    else{
        printf("Enter warning\n");
    }
    return 0;
}

2.函数形参传递大结构体数据

形参传递时,会在栈空间里分配一段空间给形参,对传递的参数进行一次拷贝,如果我们需要传递的参数是一个占用空间较大的结构体时,如果直接传递值,就会比较耗费时间。这时通过指针,将我们要传递结构体参数的指针传递给被调函数,则整个过程只需要拷贝一个指针变量,函数内部就可以访问数据。为了和1区分功能,且防止参数内容被错误的篡改,可以使用const关键字标识:

struct t{
	int a;
	int buf[10];
	struct{
		int buf[20];
		int b;
	}str_info;
};

int process_t(const struct* t);

3. 快速寻址结构深度较深的结构体成员

对于结构深度较深且使用比较频繁的结构体成员,可以对于的指针变量直接指向该成员,一定程度上可以优化程序效率,但更重要的是减少代码冗余,毕竟程序是给人看的,机器码才是给电脑看的。

struct t{
	int a;
	int buf[10];
	struct{
		int buf2[20];
		int b;
	}str_info;
};

 void print_buf2(const struct t* pt)
 {
 	const int* buf = pt->str_info.buf2;
 	for(int i = 0; i < sizeof(pt->str_info.buf2) / sizeof(int); ++i)
	{
		printf("%d ", buf[i]);
	}
}

4. 数据转换

完成一些用联合体处理的事情,将一个数据转换成其他类型的数据进行访问。

#include <stdio.h>
#include <stdint.h>

void print_hex(const void* buf, size_t len)
{
    uint8_t* data = (uint8_t*)buf;
    for(int i = 0; i < len; ++i)
    {
        printf(" %02x", data[i]);
    }
}

struct data_item_t{
    uint16_t len;
    uint8_t data[1]
};

int main(int argc, char* argv[])
{
    char* a = "abcdefg";
    uint32_t test = 0x12345678;
    uint8_t data_buf[128];
    struct data_item_t* data_itme = (struct data_item_t*)data_buf;
    data_itme->len = sizeof(data_buf) - sizeof(uint16_t);
    memset(data_itme->data, 0xFF, data_itme->len);

    print_hex(a, strlen(a));
    print_hex(&test, sizeof(test));
    print_hex(data_itme, data_itme->len + sizeof(uint16_t));
}

5. 优化迭代性能

有过库函数源码阅读经验的人,应该都可以发现很多库函数在迭代的时候都会使用指针来进行迭代,而不是使用一个index来进行处理。这样处理每次循环至少都会减少一次变址处理,有利于函数效益的提升。这里同样按照指针的方式来进行迭代重写了上面的print_hex函数。

void print_hex(const void* buf, size_t len)
{
    const uint8_t* start = (const uint8_t*)buf, *end = start + len;

    for(; start != end; ++start)
    {
        printf(" %02x", *start);
    }
}

6. 链表

基操,通过指针将数据连接成链状结构,对不连续的数据进行链状的访问。这里就不再添加代码段说明了。

7. 函数指针做调用表

使用函数指针,对相同接口类型进行访问。

struct cmd_fuc{
    const char* pair_str;
    tBool (*handler)(const char *at_cmd, char *cmd_line);
};

static tBool AT_GetVer_Patch(const char *at_cmd, char *cmd_line);

const struct cmd_fuc g_cmd_list[] = 
{
    {"GVER",        AT_GetVer_Patch},
};

void AT_proces(char* cmd_line)
{
    const struct cmd_fuc *cmd_func = g_cmd_list, *end_func = g_cmd_list + sizeof(g_cmd_list)/sizeof(struct cmd_fuc);

    for(; cmd_func != end_func; ++cmd_func){
        if(memcmp(cmd_line, cmd_func->pair_str, strlen(cmd_func->pair_str)) == 0){
            tBool ret = cmd_func->handler(cmd_func->pair_str, cmd_line + strlen(cmd_func->pair_str));
            printf(ret ? "OK\r\n" : "ERROR\r\n");
            break;
        }
    }
    
    if(cmd_func == end_func){
        printf("ERROR\r\n");
    }
}

8. 函数指针实现多态

利用函数指针可以修改的特性,对于一类数据,设计好接口。然后根据实际实现来为接口的函数指针赋值,一套代码,多处使用。

struct{
    uint32_t (*data_store)(uint32_t offset, const uint8_t* data, uint32_t len);
    uint32_t (*data_load)(uint32_t offset, uint8_t* data, uint32_t len);
}data_store_interface;

enum STORE_TYPE{
    TYPE_STORE_FLASH_IN = 1,
    TYPE_STORE_FLASH_EX,
    TYPE_STORE_EEPROM
};

uint32_t flash_data_store_in(uint32_t offset, const uint8_t* data, uint32_t len);
uint32_t flash_data_load_in(uint32_t offset, uint8_t* data, uint32_t len);

uint32_t flash_data_store_ex(uint32_t offset, const uint8_t* data, uint32_t len);
uint32_t flash_data_load_ex(uint32_t offset, uint8_t* data, uint32_t len);

uint32_t eeprom__data_store(uint32_t offset, const uint8_t* data, uint32_t len);
uint32_t eeprom_data_load(uint32_t offset, uint8_t* data, uint32_t len);

void data_store_interface_init(enum STORE_TYPE type){
    switch(type){
    case TYPE_STORE_FLASH_IN:
        data_store_interface.data_store = flash_data_store_in;
        data_store_interface.data_store = flash_data_load_in;
        break;

    case TYPE_STORE_FLASH_EX:
        data_store_interface.data_store = flash_data_store_ex;
        data_store_interface.data_store = flash_data_load_ex;
        break;

    case TYPE_STORE_EEPROM:
        data_store_interface.data_store = eeprom_store;
        data_store_interface.data_store = eeprom_data_load;
        break;
        
    default:
        data_store_interface.data_store = NULL;
        data_store_interface.data_store = NULL;
        break; 
    }
}

9. 函数指针传递回调函数

将自身设计的接口传递通过函数指针的方式给一个模块,改模块在特定时候(完成某一个动作)会通过函数指针调用该函数,对应用模块进行通知。在异步架构中经常用到。

10. 空指针传递任意自定义参数

如软件定时器等,设计之初都会在接口中添加一个空指针作为创建定时器的一个参数,用于传递一些用户自定义的参数,回调的时候通过参数再将该指针传递给到回调函数。该策略可以很大程度上减少全局变量的使用。如如下的设计:

typedef void (*simple_timer_cb_t)(uint16_t id, void* arg);

uint16_t simple_timer_set(uint32_t, simple_timer_cb_t, void*);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值