范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

C语言指针经典知识汇总

  指针在C语言中是一块很重要的内容,也是比较难理解的一块内容,我们需要反复理解反复巩固才可以对其有所了解。之前也分享过指针相关的笔记,但是都比较杂,本篇笔记汇总一下指针相关的内容,包含了挺多指针相关的基础知识点。这篇笔记有点长,可以收藏下来慢慢阅读。复杂类型说明
  以下这部分内容主要来自《让你不再害怕指针》:
  要了解指针,多多少少会出现一些比较复杂的类型,所以,先介绍一下如何完全理解一个复杂类型,要理解复杂类型其实很简单。
  一个类型里会出现很多运算符,他们也像普通的表达式一样,有优先级,其优先级和运算优先级一样,所以我总结了一下其原则: 从变量名处起,根据运算符优先级结合,一步一步分析。
  下面让我们先从简单的类型开始慢慢分析吧:int p;  
  这是一个普通的整型变量 。
  int *p;  
  首先从 P处开始,先与*结合,所以说明 P 是一个指针,然后再与 int 结合,说明指针所指向的内容的类型为 int 型。所以 P 是一个返回整型数据的指针。
  int p[3];  
  首先从 P 处开始,先与[]结合,说明 P 是一个数组,然后与 int 结合,说明数组里的元素是整型的,所以 P 是一个由整型数据组成的数组。
  int *p[3];
  首先从 P 处开始,先与[]结合,因为其优先级比 * 高,所以 P 是一个数组,然后再与 * 结合,说明数组里的元素是指针类型,然后再与 int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组 。
  int (*p)[3];
  首先从 P 处开始,先与 * 结合,说明 P 是一个指针然后再与[]结合与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与 int 结合,说明数组里的元素是整型的。所以 P 是一个指向由整型数据组成的数组的指针。
  int **p;
  首先从 P 开始,先与后再与 * 结合,说明指针所指向的元素是指针,然后再与 int 结合,说明该指针所指向的元素是整型数据。由于二级以上的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针。
  int p(int);  
  从 P 处起,先与()结合,说明 P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数然后再与外面的 int 结合,说明函数的返回值是一个整型数据。
  int (*p)(int);
  从 P 处开始,先与指针结合,说明 P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个 int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以 P 是一个指向有一个整型参数且返回类型为整型的函数的指针。
  说到这里也就差不多了,我们的任务也就这么多,理解了这几个类型,其它的类型对我们来说也是小菜了。不过我们一般不会用太复杂的类型,那样会大大减小程序的可读性,请慎用,这上面的几种类型已经足够我们用了。
  分析指针的方法
  指针是一个特殊的变量, 它里面存储的数值被解释成为内存里的一个地址。要搞清一个指针需要搞清指针的四方面的内容: 指针的类型、 指针所指向的类型、 指针的值(指针所指向的内存区)、 指针本身所占据的内存区。 让我们分别说明。
  先声明几个指针放着做例子:(1)int *ptr; (2)char*ptr; (3)int **ptr; (4)int (*ptr)[3]; (5)int *(*ptr)[4];
  1、指针的类型
  从语法的角度看, 你只要把指针声明语句里的指针名字去掉, 剩下的部分就是这个指针的类型。 这是指针本身所具有的类型。 让我们看看例一中各个指针的类型:(1)int*ptr;//指针的类型是 int* (2)char*ptr;//指针的类型是 char* (3)int**ptr;//指针的类型是 int** (4)int(*ptr)[3];//指针的类型是 int(*)[3] (5)int*(*ptr)[4];//指针的类型是 int*(*)[4]
  2、指针所指向的类型
  当你通过指针来访问指针所指向的内存区时, 指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。
  从语法上看, 你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉, 剩下的就是指针所指向的类型。例如:(1)int*ptr; //指针所指向的类型是 int (2)char*ptr; //指针所指向的的类型是 char (3)int**ptr; //指针所指向的的类型是 int* (4)int(*ptr)[3]; //指针所指向的的类型是 int()[3] (5)int*(*ptr)[4]; //指针所指向的的类型是 int*()[4]
  在指针的算术运算中, 指针所指向的类型有很大的作用。
  3、指针的值
  指针的值是指针本身存储的数值, 这个值将被编译器当作一个地址, 而不是一个一般的数值。 在 32 位程序里, 所有类型的指针的值都是一个 32 位 整数, 因为 32 位程序里内存地址全都是 32 位长。
  指针所指向的内存区就是从指针的值所代表的那个内存地址开始, 长度为 sizeof(指针所指向的类型)的一片内存区。
  以后, 我们说一个指针的值是 XX, 就相当于说该指针指向了以 XX 为首地址的一片内存区域; 我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。
  指针所指向的内存区和指针所指向的类型是两个完全不同的概念。 在例一中, 指针所指向的类型已经有了, 但由于指针还未初始化, 所以它所指向的内存区是不存在的, 或者说是无意义的。
  以后, 每遇到一个指针, 都应该问问: 这个指针的类型是什么? 指针指向的类型是什么? 该指针指向了哪里? (重点注意) 。
  4、指针本身所占据的内存区
  指针本身占了多大的内存? 你只要用函数 sizeof(指针的类型)测一下就知道了。 在 32 位平台里, 指针本身占据了 4 个字节的长度。指针本身占据的内存这个概念在判断一个指针表达式(后面会解释) 是否是左值时很有用。
  指针的算术运算
  指针可以加上或减去一个整数。 指针的这种运算的意义和通常的数值的加减运算的意义是不一样的, 以单元为单位。
  这在内存上体现为:相对这个指针向后偏移多少个单位或向前偏移了多少个单位,这里的单位与指针变量的类型有关。在32bit环境下,int类型占4个字节,float占4字节,double类型占8字节,char占1字节。
  【注意】一些处理整数的操作不能用来处理指针。例如,可以把两个整数相乘,但是不能把两个指针相乘。示例程序#include   int main(void) { 	int    a = 10, *pa = &a; 	float  b = 6.6, *pb = &b; 	char   c = "a", *pc = &c; 	double d = 2.14e9, *pd = &d;  	//最初的值 	printf("pa0=%d, pb0=%d, pc0=%d, pd0=%d ", pa, pb, pc, pd); 	//加法运算 	pa += 2;  	pb += 2;  	pc += 2; 	pd += 2; 	printf("pa1=%d, pb1=%d, pc1=%d, pd1=%d ", pa, pb, pc, pd); 	//减法运算 	pa -= 1;  	pb -= 1;  	pc -= 1; 	pd -= 1; 	printf("pa2=%d, pb2=%d, pc2=%d, pd2=%d ", pa, pb, pc, pd);  	return 0; }
  运行结果为:pa0=6422268, pb0=6422264, pc0=6422263, pd0=6422248 pa1=6422276, pb1=6422272, pc1=6422265, pd1=6422264 pa2=6422272, pb2=6422268, pc2=6422264, pd2=6422256
  解析:
  举例说明pa0→pa1→pa2的过程,其他类似。pa0+2*sizeof(int)=pa1,pa1-1*sizeof(int)=pa2。因为pa为int类型的指针,所以加减运算是以4字节(即sizeof(int))为单位地址向前向后偏移的。看下图:
  如图:pa1所指向的地址在pa0所指向地址往后8字节处,pa2指向地址在pa1指向地址往前4字节处。
  从本示例程序中,还可以看出:连续定义的变量在内存的存储有可能是紧挨着的,有可能是分散着的。
  数组和指针的联系
  数组与指针有很密切的联系,常见的结合情况有以下三种:数组指针指针数组二维数组指针
  1、数组指针
  数组指针:指向数组的指针。如:int arr[] = {0,1,2,3,4}; int *p = arr; //也可写作int *p=&arr[0]
  也就是说,p,arr,&arr[0]都是指向数组的开头,即第0个元素的地址。
  如果一个指针p指向一个数组arr[]的开头,那么p+i为数组第i个元素的地址,即&arr[i],那么*(p+i)为数组第i个元素的值,即arr[i]。
  同理,若指针p指向数组的第n个元素,那么p+i为第n+1个元素的地址;不管 p 指向了数组的第几个元素,p+1 总是指向下一个元素,p-1 也总是指向上一个元素。
  下面示例证实了这一点:#include   int main(void) {    int arr[] = {0, 1, 2, 3, 4};    int *p = &arr[3];  //也可以写作 int *p = arr + 3;     printf("%d, %d, %d, %d, %d ",     *(p-3), *(p-2), *(p-1), *(p), *(p+1) );    return 0; }
  运行结果为:0, 1, 2, 3, 4
  2、指针数组
  指针数组:数组中每个元素都是指针。如:int a=1,b=2,c=3; int *arr[3] = {&a,&b,&c};
  示例程序:#include  int main(void) {    int a = 1, b = 2, c = 3;    //定义一个指针数组    int *arr[3] = {&a, &b, &c};//也可以不指定长度,直接写作 int *parr[]    //定义一个指向指针数组的指针    int **parr = arr;    printf("%d, %d, %d ", *arr[0], *arr[1], *arr[2]);    printf("%d, %d, %d ", **(parr+0), **(parr+1), **(parr+2));     return 0; }
  第一个 printf() 语句中,arr[i] 表示获取第 i 个元素的值,该元素是一个指针,还需要在前面增加一个 * 才能取得它指向的数据,也即 *arr[i] 的形式。
  第二个 printf() 语句中,parr+i 表示第 i 个元素的地址,*(parr+i) 表示获取第 i 个元素的值(该元素是一个指针),**(parr+i) 表示获取第 i 个元素指向的数据。
  指针数组还可以和字符串数组结合使用,请看下面的例子:#include  int main(void) {     char *str[3] =      {         "hello C",         "hello C++",         "hello Java"     };     printf("%s %s %s ", str[0], str[1], str[2]);     return 0; }
  运行结果为:hello C hello C++ hello Java
  3、二维数组指针
  二维数组指针:指向二维数组的指针。如:int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} }; int (*p)[4] = a;
  a [3] [4]表示一个3行4列的二维数组,其所有元素在内存中是连续存储的。
  请看如下程序:#include  int main(void) {     int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };     int i,j;     for( i = 0; i < 3; i++ )     {         for( j = 0; j < 4; j++ )         {             printf("a[%d][%d]=%d ", i, j, &a[i][j]);         }     }      return 0; }
  运行结果为:a[0][0]=6422216 a[0][1]=6422220 a[0][2]=6422224 a[0][3]=6422228 a[1][0]=6422232 a[1][1]=6422236 a[1][2]=6422240 a[1][3]=6422244 a[2][0]=6422248 a[2][1]=6422252 a[2][2]=6422256 a[2][3]=6422260
  可见,每个元素的地址都是相差4个字节,即每个连续在内存中是连续存储的。
  按照以上定义可归纳出如下4个结论:
  (1)p指向数组a的开头,也即第1行;p+1前进一行,指向第2行。
  (2)*(p+1)表示取第2行元素(一整行元素)。
  (3)*(p+1)+1表示第2行第2个元素的地址。
  (4)((p+1)+1)表示第2行第2个元素的值。
  综上4点,可得出如下结论:a+i == p+i  *(a+i) == *(p+i) a[i][j] == p[i][j] == *(a[i]+j) == *(p[i]+j) == *(*(a+i)+j)== *(*(p+i)+j)
  以上就是数组与指针常用的三种结合形式。
  指针与数组的区别
  数组与指针在多数情况是可以等价的,比如:int array[10]={0,1,2,3,4,5,6,7,8,9},value; value=array[0]; //也可写成: value=*array; value=array[3]; //也可写成: value=*(array+3); value=array[4]; //也可写成: value=*(array+4)
  但也有不等价的时候,比如如下三种情况:数组名不可以改变,而指向数组的指针是可以改变的。字符串指针指向的字符串中的字符是不能改变的,而字符数组中的字符是可以改变的。求数组长度时,借用数组名可求得数组长度,而借用指针却得不到数组长度。
  1、区别一
  数组名的指向不可以改变,而指向数组的指针是可以改变的。
  请看如下代码:#include   int main(void) {     int a[5] = {0, 1, 2, 3, 4}, *p = a;     char i;      // 数组遍历方式一     for ( i = 0; i < 5; i++ )     {         printf("a[%d] = %d ", i, *p++);     }      // 数组遍历方式二     for ( i = 0; i < 5; i++ )     {         printf("a[%d] = %d ", i, *a++);     }      return 0; }
  数组遍历方式一:使用指针遍历数组元素,* p++等价于*(p++),即指针指向的地址每次后移一个单位,然后再取地址上的值。这里的一个单位是sizeof(int)个字节。
  数组遍历方式二:使用数组名自增遍历数组元素,编译出错,错误如下:error: value required as increment operand
  因为数组名的指向是不可以改变的,使用自增运算符自增就会改变其指向,这是不对的,数组名只能指向数组的开头。但是可以改为如下遍历方式:for ( i = 0; i < 5; i++ ) {     printf("a[%d] = %d ", i, *(a+i)); }
  这可以正确遍历数组元素。因为*(a+i)与a[i]是等价的。
  2、区别二
  字符串指针指向的字符串中的字符是不能改变的,而字符数组中的字符是可以改变的。
  请看如下代码://字符串定义方式一 char str[] = "happy";  //字符串定义方式二 char *str = "happy";
  字符串定义方式一:字符串中的字符是可以改变的。如可以使用类似str[3]="q"这样的语句来改变其中的字符。原因就是:这种方式定义的字符串保存在全局数据区或栈区,是可读写的。
  字符串定义方式二:字符串中的字符是不可以改变的。原因就是:这种方式定义的字符串保存在常量区,是不可修改的。
  2、区别三
  求数组长度时,借用数组名可求得数组长度,而借用指针却得不到数组长度。
  请看如下代码:#include   int main(void) {     int a[] = {0, 1, 2, 3, 4}, *p = a;     char len = 0;      // 求数组长度方式一     printf("方式一:len=%d ",sizeof(a)/sizeof(int));      // 求数组长度方式二     printf("方式二:len=%d ",sizeof(p)/sizeof(int));      return 0; }
  运行结果方式一:len=5 方式二:len=1
  求数组长度方式一:借用数组名来求数组长度,可求得数组有5个元素,正确。
  求数组长度方式二:借用指针求数组长度,求得长度为1,错误。原因是:
  p只是一个指向int类型的指针,编译器不知道其指向的是一个整数还是指向一个数组。sizeof(p)求得的是p这个指针变量本身所占用的字节数,而不是整个数组占用的字节数。
  下面还需要注意数组名的一个问题: 声明了一个数组 TYPE array[n] , 则数组名是一个常量指针, 该指针的值是不能修改的, 即类似 array++的表达式是错误的。
  指针函数与函数指针
  函数、指针这两个词结合的顺序不同其意义也不同,即指针函数与函数指针的意义不同。
  1、指针函数
  指针函数的本质是一个函数,其返回值是一个指针。示例如下:int *pfun(int, int);
  由于"*"的优先级低于"()"的优先级,因而pfun首先和后面的"()"结合,也就意味着,pfun是一个函数。即:int *(pfun(int, int));
  接着再和前面的"*"结合,说明这个函数的返回值是一个指针。由于前面还有一个int,也就是说,pfun是一个返回值为整型指针的函数。
  指针函数示例程序如下:#include  //这是一个指针函数的声明 int *pfun(int *arr, int n);  int main(void) {     int array[] = {0, 1, 2, 3, 4};     int len = sizeof(array)/sizeof(array[0]);     int *p;     int i;      //指针函数的调用     p = pfun(array, len);      for (i = 0; i < len; i++)     {         printf("array[%d] = %d ", i, *(p+i));     }      return 0; }  //这是一个指针函数,其返回值为指向整形的指针 int *pfun(int *arr, int n) {     int *p = arr;      return p; }
  程序运行结果如下:
  主函数中,把一个数组的首地址与数组长度作为实参传入指针函数pfun里,把指针函数的返回值(即指向数组的指针)赋给整形指针p。最后使用指针p来遍历数组元素并打印输出。
  2、函数指针
  函数指针其本质是一个指针变量,该指针变量指向一个函数。C程序在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。函数指针示例:/*声明一个函数指针 */ int (*fptr) (int, int);  /* 函数指针指向函数func */ fptr = func;  // 或者fptr = &func;
  func是一个函数名,那么func与&func都表示的是函数的入口地址。同样的,在函数的调用中可以使用:方式一:func(),也可以使用方式二:(*fun)()。这两种调用方式是等价的,只是我们平时大多都习惯用方式一的调用方法。
  至于为什么func与&func的含义相同,《嵌入式Linux上的C语言编程实践》这本书中有如下解释:
  对于函数func来说,函数的名称就是函数代码区的常量,对它取地址(&func)可以得到函数代码区的地址,同时,func本身也可以视为函数代码区的地址。因此,函数名称和对其取地址其含义是相同的。
  函数指针示例程序如下:#include   int add(int a, int b);  int main(void) {     int (*fptr)(int, int); //定义一个函数指针     int res;     fptr = add;  //函数指针fptr指向函数add      /* 通过函数指针调用函数 */     res = (*fptr)(1,2); //等价于res = fptr(1,2);     printf("a + b = %d ", res);      return 0; }  int add(int a, int b) {     return a + b; }
  程序运行结果如下:
  以上就是关于指针函数与函数指针的简单区分。其中,函数指针广泛应用于嵌入式软件开发中,其常用的两个用途:调用函数和做函数的参数。
  以上就是本次的分享,如有错误,欢迎指出!谢谢

面试被问到零拷贝!你要怎么回答?前言IO概念1。缓冲区2。虚拟内存3。mmapwrite方式4。sendfile方式Java零拷贝1。MappedByteBuffer2。DirectByteBuffer3。Cha基于SpringCloud的分布式微服务架构权限管理系统源码免费分享平台简介采用前后端分离的模式,微服务版本前端(基于RuoYiVue)。后端采用SpringBootSpringCloudAlibaba。注册中心配置中心选型Nacos,权限认证使用SpringCloud和AntDesignPro前后端管理平台一站式脚手架分享今天小编要推荐的这个项目,在Git上收获了1。7Kstar,是一个管理后台脚手架SpringCoud微服务SpringSecuritySSOJWTToken自定义登陆页面前后端分离微服务实战文档分享,阿里内部的Springcloud微服务精髓都在里面前几天,看到一条消息。在互联网职场论坛有个阿里巴巴员工说自己毕业了两年,结果发现工资居然比不上应届毕业研究生的起薪,感觉受到了公司的侮辱!其实,资历浅的新员工工资比老员工工资高的情快速创建MySQL五百万级数据,愉快的学习各种优化技巧如果你打算好好学习一下MySQL,性能优化肯定是绕不过去一个问题。当你撸起袖子准备开始的时候,突然发现一个问题摆在眼前,本地数据库中没那么大的数据量啊,几条数据优化个毛线啊。生产库接私活神器前后端分离的Java快速开发平台源码分享先转发,然后加关注,私信源码即可获取源码项目说明是一个轻量级的在renrenfast基础上利用jfinal架构二次开发的一个极速二次开发直播,拼团商城框架,前后端分离的Java快速面试这么撩准拿offer,扰动函数负载因子扩容拆分,原理实践作者小傅哥博客bugstack。cn沉淀分享成长,让自己和他人都能有所收获!一前言得益于DougLea老爷子的操刀,让HashMap成为使用和面试最频繁的API,没办法设计的太优秀SSMShiroSwaggerEhcacheQuartzLayui快速开发脚手架源码先转发,再关注,私信回复源码即可免费获取到项目源码一款快速开发模块化脚手架,采用springboot2。0。1springSpringMvcmybatisshiroswaggere肝了一个半月的Java项目快速开发脚手架Chewing前言闲来无事,整一个Java项目快速开发脚手架。正文一简介Chewing是一个简单的Java项目快速开发脚手架。既适合需要开发小型项目的小伙伴使用,也适合刚入门的新手用来学习一些常基于java语言,打造比mybatis更实用的orm框架开源项目分享1。前言1。1sqltoyorm是什么sqltoyorm是比hibernatemyBatis(plus)更加贴合项目的orm框架(依赖spring),具有jpa式的对象CRUD的同写了个牛逼的日志切面,甩锅更方便了最近项目进入联调阶段,服务层的接口需要和协议层进行交互,协议层需要将入参json字符串组装成服务层所需的json字符串,组装的过程中很容易出错。入参出错导致接口调试失败问题在联调中
QI无线充电,雷柏VT350Q无线充电电竞鼠标,为竞技注入灵动能量。与光同往,与电同竞,正在读取游戏存档雷柏VT350Q欢迎进入无线竞技世界。专为右手玩家设计打造,人体工学设计线条,性能与实用立足玩家的需求偏好,为竞技注入灵动能量。支持适配Qi标准3万左右的价格,成老头乐的噩梦,五菱宏光MINIEV凭啥这么火?文车魔王原创其实,五菱这几年来一直试图在做两件事,一方面是摆脱自身长期以来工具车的形象。而另一方面,则是去听取更多人的意见,满足更精准的用车需求。前者的代表,自然是新宝骏品牌而五菱铁矿石价格上涨50是加剧通胀担忧的最新因素铁矿石突然从大宗商品市场的落后者一跃成为表现最佳的大宗商品,价格回升进一步加剧了通胀担忧,并波及全球。铁矿石期货价格在短短三周内上涨了50,铝价和能源价格也上涨,原因是需求上升供应欧洲本土化打法升级宁德时代手握锂电材料生命源为践行欧洲本土化战略,实现全球碳中和目标,近日,宁德时代与巴斯夫欧洲公司宣布在电池材料解决方案领域内建立战略合作伙伴关系,双方将聚焦正极活性材料和电池回收领域,开展密切合作。随着我动力电池原材料现涨价潮新能源车市能否顶住压力动力电池是否会涨价,牵动着行业内外的神经光。近来,在原材料涨声一片的背景下,新能源汽车锂电池涨价潮或有扩大之势。日前,鹏辉能源国轩高科等动力电池企业已经发出涨价函。所有订单将执行大电力成套设计流程,拿到系统图后如何列出材料清单,画出生产图纸一般成套设备厂拿到一份设计院的设计图纸后,都需要对图纸进行深化设计,然后生产,调试,运行!对于新手来说如何快速的入门呢?下面我就拿一份简单图纸,把成套厂的技术流程做一遍,仅供参考,免费试用思必驰会议魔方效果怎么样?安排打工人打工魂,打工都是人上人新时代的打工人偷偷修炼惊艳所有人思必驰会议神器助力打工人高效工作会议魔方,最懂打工人的神器会议魔方M1是一款针对会议办公场景的轻便级智能全向麦克风产品,三星GalaxyZFold3屏下前摄痕迹十分明显,像初代产品今晚三星将发布新一代的折叠屏手机zfold3和zflip2,zfold3是屏下摄像头版本,三星这个屏幕大厂的屏下技术相当令人期待,只不过前阵子有视频偷跑,zfold3的摄像头痕迹相海容材料完成数千万元PreA轮融资,加速建设全自动封装产线近日,深圳海容高新材料科技有限公司(以下简称海容材料)已完成数千万PreA轮融资,由中电光谷旗下投资机构零度资本领投,先风创投联合投资,上海国创等老股东持续加码。据悉,本轮融资资金3区域灯光,不足百元也能听声辨位,雷柏VH120RGB游戏耳机新上市听声辨位有多强?恰似有墙胜无墙!作为新武器,雷柏VH120RGB游戏耳机最近加入空投箱豪华套餐中网状RGB灯透露出质感与神秘竞技视觉科技感爆表,威力杠杠欢迎前往训练基地快乐刚枪稳就借助思必驰声纹识别,长虹CHiQ空调听声识人10月27日,长虹建业60周年战略转型暨产品发布会在绵阳举行,长虹本次推出的人工智能家电新品,涵长虹CHiQ电视CHiQ空调CHiQ冰箱等智能家居产品,并宣布力争2025年销售规模