C语言 强制数字类型指针寻址

本文讨论了在C语言编程中,直接通过类型强制转换访问内存地址可能导致的问题,特别是在单片机平台上,由于内存地址和总线未对齐可能会导致程序死机或数据错误。为了解决这个问题,提出了使用联合结构体进行数据转换的方法,提供了一系列的转换函数,如TR_INT8, TR_UINT16等,以确保在不同芯片上正确地读取和处理数据。

在c语言开发中,有时候为了方便经常会这么访问一个地址 int a = *(int *)address。但这个方法在单片机平台中存在着隐患:

  • 表现:程序死机或者数据获取不对
  • 原因:取地址时内存地址和总线未对齐
  • 解决方法:使用联合结构体方法进行转换

测试代码:在不同的芯片中执行会有不同的情况,大部分能正确获得数据,但有些则是卡死或者数据获取错误。

#include <stdio.h>
#include <stdint.h>
#include "math.h"
#define M_PI 3.14159265358979323846
int main()
{
    int i;
    uint8_t data[10]={1,2,3,4,5,6,7,8,9,10};
    printf("8bit address: \n");
    for(i=0;i<10;i++){
        printf("%d:%lx\n",i,(uint32_t)&data[i]);
    }
    printf("16bit address: \n");
    for(i=0;i<10;i++){
        printf("%d:%lx\n",i,*(uint16_t*)&data[i]);
    }
    printf("32bit address: \n");
    for(i=0;i<10;i++){
        printf("%d:%lx\n",i,*(uint32_t*)&data[i]);
    }
    printf("done \n");
    return 0;
}

解决代码:

typedef union param_union {
    float param_float;
    int32_t param_int32;
    uint32_t param_uint32;
    int16_t param_int16;
    uint16_t param_uint16;
    int8_t param_int8;
    uint8_t param_uint8;
    uint8_t bytes[4];
}TYPE_UNION;

//常用的数据转换函数
int8_t TR_INT8(uint8_t *in){
    return *(int8_t*)in;
}
uint8_t TR_UINT8(uint8_t *in){
    return *in;
}
int16_t TR_INT16(uint8_t *in){
    TYPE_UNION changecore={0};
    memcpy(changecore.bytes,in,2);
    return changecore.param_int16;
}
uint16_t TR_UINT16(uint8_t *in){
    TYPE_UNION changecore={0};
    memcpy(changecore.bytes,in,2);
    return changecore.param_uint16;
}
int32_t TR_INT32(uint8_t *in){
    TYPE_UNION changecore={0};
    memcpy(changecore.bytes,in,4);
    return changecore.param_int32;
}
uint32_t TR_UINT32(uint8_t *in){
    TYPE_UNION changecore={0};
    memcpy(changecore.bytes,in,4);
    return changecore.param_uint32;
}
float TR_FLOAT(uint8_t *in){
    TYPE_UNION changecore={0};
    memcpy(changecore.bytes,in,4);
    return changecore.param_float;
}

### C语言中空类型指针 (void pointer) 的概念及用法 #### 什么是 `void` 指针? `void` 是一种特殊的数据类型,表示无类型。因此,`void*` 被称为通用指针或空类型指针,它可以存储任意类型的地址[^4]。 然而,由于 `void` 不代表具体类型,编译器无法推断它指向的对象的大小或其他属性。这意味着不能直接对 `void*` 进行解引用或执行算术运算。 --- #### 如何声明和初始化 `void*` 可以通过以下方式定义一个 `void*`: ```c void *ptr; ``` 要让 `void*` 指向某个变量的地址,可以这样赋值: ```c int a = 10; float b = 20.5; // 让 ptr 指向 int 类型变量 a ptr = &a; // 或者让 ptr 指向 float 类型变量 b ptr = &b; ``` 注意:在上述例子中,虽然 `ptr` 成功指向了不同类型的变量,但由于它是 `void*`,我们无法直接访问这些变量的内容。为此,需要将其强制转换为目标类型后再进行操作。 --- #### 对 `void*` 的使用限制 因为 `void*` 缺乏类型信息,所以在对其进行间接寻址之前必须先显式地转换成合适的类型。例如: ```c printf("%d\n", *(int*)ptr); // 将 void* 转换为 int* printf("%f\n", *(float*)ptr); // 将 void* 转换为 float* ``` 如果未正确转换就尝试读取数据,则可能导致不可预测的行为甚至程序崩溃。 另外需要注意的是,标准规定不允许对 `void*` 执行加减等算术运算,这是因为不知道它的步长是多少(即目标对象有多大)。只有当被重新解释为具有已知尺寸的另一种指针之后才能参与此类计算。 --- #### 实际应用场景举例 ##### 动态内存管理 最典型的用途之一是在动态分配内存时返回的结果是一个未经指定确切种类但能容纳任何实体位置的信息——这正是 malloc 函数所提供的服务: ```c #include <stdio.h> #include <stdlib.h> int main(){ int n=5; /* 请求一块连续空间用来存放五个整数 */ int *array=(int *)malloc(n*sizeof(int)); if(array==NULL){ fprintf(stderr,"Memory allocation failed!\n"); exit(EXIT_FAILURE); } array[0]=789; printf("The first element is %d.\n",array[0]); free(array); return EXIT_SUCCESS; } ``` 在这里可以看到,尽管最终还是得把得到的空间转回特定类别以便进一步处理,但在初始阶段却无需关心细节即可完成基本任务[^5]。 ##### 回调机制中的泛化接口设计 另一个常见场景涉及事件驱动编程模型下的回调注册过程。假定存在这样一个函数原型接受两个参数分别是另一段逻辑实现以及额外传递过去的上下文环境描述符;那么后者往往会被封装进一个统一形式下传输过去再由接收端自行解析还原原始状态: ```c typedef void(*CallbackFunc)(void*); void invoke_callback(CallbackFunc func,void *context){ (*func)(context); } struct MyData{ char name[32]; }; void my_handler(void *data){ struct MyData *md=data; puts(md->name); } int main(){ struct MyData md={"John Doe"}; CallbackFunc cb=my_handler; invoke_callback(cb,&md); return 0; } ``` 此模式允许使用者灵活定制各自专属的消息处理器而不受制于固定框架约束的同时还保持了一致性的外观表现。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值