C语言类型转换-自动类型转换、强制类型转换、指针类型转换

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注go)
img

正文

注意看第 6 行代码,total 变量被转换成了 int 类型才赋值给 total_int 变量,而这种转换并未影响 total 变量本身的类型和值。如果 total 的值变了,那么 total 的输出结果将变为 400.000000;如果 total 的类型变了,那么 unit 的输出结果将变为 80.000000。

自动类型转换 VS 强制类型转换

在C语言中,有些类型既可以自动转换,也可以强制转换,例如 int 到 double,float 到 int 等;而有些类型只能强制转换,不能自动转换,例如以后将要学到的 void * 到 int *,int 到 char * 等。

可以自动转换的类型一定能够强制转换,但是,需要强制转换的类型不一定能够自动转换。现在我们学到的数据类型,既可以自动转换,又可以强制转换,以后我们还会学到一些只能强制转换而不能自动转换的类型。

可以自动进行的类型转换一般风险较低,不会对程序带来严重的后果,例如,int 到 double 没有什么缺点,float 到 int 顶多是数值失真。只能强制进行的类型转换一般风险较高,或者行为匪夷所思,例如,char * 到 int * 就是很奇怪的一种转换,这会导致取得的值也很奇怪,再如,int 到 char * 就是风险极高的一种转换,一般会导致程序崩溃。

使用强制类型转换时,程序员自己要意识到潜在的风险。

指针类型之间的转换

指向值的一个类型的指针可以转换为指向另一类型的指针。 但是,由于对齐需求和存储中不同类型的大小,结果可能是未定义的。 指向对象的指针可转换为指向其类型要求小于或等于严格存储对齐的对象的指针,然后再次返回而不做更改。

指向 void 的指针可转换为/自指向任何类型的指针,且不受限制或不丢失信息。 如果结果转换回原始类型,则将恢复原始指针。

如果指针转换为另一个类型相同但具有不同的或其它限定符的指针,则新指针与旧指针相同(新限定符强加的限制除外)。

指针值也可以转换为整数值。 根据以下规则,转换路径取决于指针的大小和整型的大小:

  • 如果指针的大小大于或等于整型的大小,则指针的行为类似于转换中的无符号值,除非它无法转换为浮点值。
  • 如果指针小于整型,则指针首先转换为与整型大小相同的指针,然后转换为整型。

相反,整型可以基于以下规则转换为指针类型:

  • 如果整型与指针类型的大小相同,则转换只会促使整数值被视为指针(无符号整数)。
  • 如果整型类型的大小与指针类型的大小不同,则使用表从带符号整型类型转换从无符号整型类型转换中给定的转换路径,首先将整型转换为指针的大小。 然后将其视为一个指针值。

值为 0 的整型常量表达式或强制转换为类型 void* 的此类表达式可以通过类型强制转换、赋值或与任何类型的指针进行比较来进行转换。 这将产生与同一类型的另一个 null 指针相等的 null 指针,但此 null 指针与指向函数或对象的任何指针不相等。 常数 0 以外的整数可以转换为指针类型,但结果是不可移植的。

指针的本质是变量,指针就是指针变量。
一个指针涉及两个变量:一个是指针变量自己本身,一个是指针变量指向的那个变量。
int *p; :定义指针变量时,p是int *类型,*p(p指向的那个变量)是int类型的。int *说白了就是指针类型,只要是指针类型都是占4个字节,解析方式都是按照地址方式来解释(意思是里面存的23个二进制加起来表示一个内存地址)的。

结论:所有指针类型(int *,double *,char *…)解析方式是相同的,都是地址。

对于指针所指向的那个变量来说,指针类型就很重要。指针指向的变量类型要取决于指针类型。

指针类型主要是为了其指向的变量所标记的。

#include <stdio.h>
int main(void)
{
int a=5;
float *p;
p=&a;
printf(“*p=%f.\n”,*p);
}

#include <stdio.h>
int main(void)
{
int a=5;
int *p1=&a;
float *p;
p=(float *)p1; //强制类型转换
printf(“*p=%f.\n”,*p);
}

指针的强制类型转换是有风险的

#include <stdio.h>
int main(void)
{
int a=5;
char *p1=&a;
printf(“*p1=%d.\n”,*p1);
short *p2=&a;
printf(“*p2=%d.\n”,*p2);

int和char类型都是整形,是兼容的,强制类型转换时有时候对有时候出错。int有两个字节char只有一个,int能表示的范围比int大,超过范围后int朝char转会出错。char往int就就不会出错(127)。short也有两个字节范围比char大还是比int小(65535)。

#include <stdio.h>
int main(void)
{
int a[2]={0x11223344,ox55667788,0};
int *p1=a;
char *p2=(char *)a;
printf(“*p1=%x.\n”,*p1);
printf(“*p2=%x.\n”,*p2);
printf(“*p2=%x.\n”,*(p2+1));
printf(“*p2=%x.\n”,*(p2+2));
}

链接:https://www.jianshu.com/p/458928c7ec35

指针类型转换的应用

我们知道,所有指针都是指向一块内存的一个字节,然后根据指针的类型来对这个字节及其后面的字节进行解析。

因此我们可以利用这个特性来完成一些很方便的操作。

一、将结构体指针转换成数组指针,方便整体赋值操作

#include <string.h>
typedef struct
{
int a;
int b;
float c;
}ST;

int main(void) {
ST st;
memset((unsigned char*)&st, 0, sizeof(ST));
}

这样的好处是我们可以不用对结构体中的元素一个一个初始化,我们可以方便的操作结构体占用的内存。使用这种方法需要注意结构体中的内存对齐问题。

二、串口接收数据转换

问题背景可以参考这篇文章:https://mp.weixin.qq.com/s/cA4e9gvew5LYwZoVtdXZQw

三、函数、函数指针的参数和返回值

将函数参数设置成void *,可以让函数接收的数据类型更加灵活。

#include <stdio.h>
#define MSG_DRIVER_SIZE (10)
#define TRUE 1
#define FALSE 0

typedef struct msg_node{
void *parm;
void (*handler)(void *parm);
}msg_node_t; /* 消息数据结构 */

typedef struct msg_driver{
unsigned int in; //写入的位置
unsigned int out; //读出的位置
msg_node_t *buf[MSG_DRIVER_SIZE];
}msg_driver_t;

bool publish_msg(msg_driver_t *msg_buf, msg_node_t *msg)
{
msg_buf->buf[msg_buf->in] = msg;
msg_buf->in = (++msg_buf->in) % MSG_DRIVER_SIZE; //防止越界

return TRUE;
}

static msg_node_t *get_messge(msg_driver_t *msg_buf)
{
msg_node_t *msg = NULL;

msg = msg_buf->buf[msg_buf->out];
msg_buf->out = (++msg_buf->out) % MSG_DRIVER_SIZE; //防止越界

return msg;
}

void message_driver_handle(msg_driver_t *msg_buf)
{
msg_node_t *msg;
while( (msg = get_messge(msg_buf)) != NULL )
{
if (msg->handler != NULL)
msg->handler(msg->parm);
}
}

msg_driver_t msg_driver;

static void msg1_handle(void *parm)
{
printf(“gets msg1\r\n”);
}

static void msg2_handle(void *parm)
{
printf(“get msg2\r\n”);
}

static void msg3_handle(void *parm)
{
printf(“do msg3\r\n”);
}

msg_node_t msg1 = {
.parm = (void *)“I love u”,
.handler = msg1_handle
};

msg_node_t msg2 = {
.parm = (void *)“I hate u”,
.handler = msg2_handle
};

msg_node_t msg3 = {
.parm = NULL,
.handler = msg3_handle
};

int main(void)
{
publish_msg(&msg_driver, &msg1);
publish_msg(&msg_driver, &msg2);
publish_msg(&msg_driver, &msg3);

while(1)
{
message_driver_handle(&msg_driver);
}
}

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
er_handle(&msg_driver);
}
}

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-KvQC9gJy-1713295708272)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 14
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值