17944|47

7815

帖子

57

TA的资源

裸片初长成(中级)

楼主
 

不得不说~~关于 *(unsigned long *) 和 (unsigned long) [复制链接]

 
然而我很悲伤地再次郑重声明:我木有错!具体请看我回复 水果君 那难得一见的长回帖的回帖!!



这事情居然还可以从遥远的 2月2号 刚下班放假那天开始。
那天,水群里,水果君弄了个论坛的帖子,说让我去回帖,我看了一下,看到一个挺新鲜的东西,觉得有点意思,但看到下面的讨论我就觉得很晕。
当时稀里糊涂在群里和他们胡说八道了一番,废了很多劲才发现问题的关键。

那个帖子,链接如下:
https://bbs.eeworld.com.cn/thread-483541-1-1.html
帖子很短,基本都是代码,我烤过来........


  1. void GPIO_DeInit(GPIO_TypeDef* GPIOx)
  2. {
  3.   /* Check the parameters */
  4.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  5.   
  6.   switch (*(uint32_t*)&GPIOx)
  7.   {
  8.     case GPIOA_BASE:
  9.       RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA, ENABLE);
  10.       RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA, DISABLE);
  11.       break;.....
  12. //不明白switch行中GPIOx为什么要取址,GPIOx本来不就是地址么?
  13. #define GPIOA    ((GPIO_TypeDef*)GPIOA_BASE)
  14. //求解释
复制代码
对于水果君和我,尽管我们的意见不一致,但其实可以说,我和他,都知道是怎么回事。
只不过,他的逻辑是,反正是一回事,能写多简单是多简单,而我的观点是:不要为了偷懒少敲那几个字母,让别人误会。
尽管我们都很清楚他其实是怎么回事。

没想到这事余孽未消,刚刚突然水群里又来一个类似的问题,这次问得更加没头没脑,直接把我这容易犯懵的大脑弄糊涂了。

绕了一圈睡了一觉后,我想了想,决定好好写个帖子,好好说说这事情



此帖出自编程基础论坛

最新回复

  详情 回复 发表于 2023-5-17 14:03
点赞 关注(4)
个人签名

强者为尊,弱者,死无葬身之地

 

回复
举报

7815

帖子

57

TA的资源

裸片初长成(中级)

沙发
 
首先,从 GPIOx 说起

这是ST库里的经典映射手法。
具体就是

和PC上的程序不一样,在STM32上,可以通过

*(int *)0x08001000 = 34;

在地址为 0x80010000上写入 34 这个内容。
——这里不考虑什么 FLASH RAM之类的问题。

所以,ST库映射寄存器的典型手法就是

比如说 GPIOA的寄存器地址,如果是从 0x20001000开始存放什么 ODR IDR之类的。
因为 结构体成员在内存上也是按序排放的,所以,它就把

ODR IDR等等 寄存器 按顺序 定义成 GPIO 这个结构体。

形如

typedef GPIO
{
    odr;
    idr;
};

这一部分具体可以去看 stm32fxxx.h,我就不多说了。

最后,GPIOA GPIOB GPIOC都会有一个 GPIOA_Base GPIOB_Base
这个基地址,指的就是 每个port端口 寄存器的 起始地址。ABCDEFGI口各自按顺序排好。
所以只要找到头,再借助这个结构体,就可以直接通过

GPIOA->ODR这样的写法非常简单直观的 寻知道 GPIOA的ODR地址。

非常形象,非常生动。
而且很简洁,完全的利用了C语法本身的特性。

是以,从我个人的角度看,这是一个非常不错的 映射手法。

这基本也是那天晚上我语无伦次 发语音说的重点。

此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

板凳
 
现在,先来回答原来那个帖子的问题。

*(uint32_t*)&GPIOx

这句话到底是什么意思?

其实问题还是在上一个帖子里提到的 GPIOx的定义里

#define GPIOA    ((GPIO_TypeDef*)GPIOA_BASE)

其实我为什么第一反应会觉得这个东西写的挺新鲜,因为以前我曾小小纠结过如何通过一个函数,让函数自己区分 GPIOA GPIOB这个问题。
而这里提供了一个 非常直接简单的方法:

*(uint32_t*)&GPIOx

对这种较复杂的表达式或者宏,解决的思路很简单,就是一步一步展开。但这个过于简单,而且这个话题也太口水了,我就直接带过去不罗嗦了,罗嗦了你们还以为是我无知大惊小怪......(多怨啊我,我只是一个喜欢 详细解释的好版主)

GPIOx 是传递进来的形参,它的可能值就是 GPIOA GPIOB之类的
那也就是

((GPIO_TypeDef*)GPIOA_BASE)
GPIOA_BASE是一个数值,代表的是 GPIOA的寄存器的起始地址。

*(uint32_t*)&GPIOx
这个操作,等于,把 GPIOA_BASE 这个最初宏定义的数值,就是说,这是一个常数。

所以这个时候,就可以很方便的使用 switch-case结构了
因为case后面跟的只能是常数,而不能是变量或者其他任何数值。

这就是这个问题的所有答案
此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

4
 
本帖最后由 辛昕 于 2016-2-12 16:56 编辑

水果君在那个帖子里认为,其实这个地方是多此一举,可以直接写成

(uint32)GPIOx

说实在的,这个,和 那个显得很麻烦的写法

*(uint32_t*)&GPIOx

效果确实是一样的。

只是,我强调 后面这种写法,我的理由在于:

可读性。

看到前者,你不会联想到 GPIOx是一个地址,而看到后者,稍微有点经验的C程序员都马上会领悟到这一点。
这就很重要。
为什么,因为,在类似的环境下,我就会搞懵。
比如说,最开始主楼贴 的那个图。

那是 人民币君发的。

我一开始因为直接联想到 这个放假前的讨论,因此我想都没想,就直接说,这两个效果是一样的。

然而,果真是一样吗?呵呵,那还真不是。
比如说



写到这里,我就懵逼了,看出问题了没?
一个是那个数其实是地址值,要去操作那个地址上的内容
另一个,压根就只是一个常数。

这两个操作的出来的结果和影响完全不一样。

虽然这可以解释为看走眼,糊涂,当然你也可以认为我经常犯懵,但是我个人的经验是——
不要盲目自信你不会犯懵。

因为一旦犯懵的时候,你可能要付出两三天或者两三个加班的夜晚去解决问题。
试问值得不值得?

而这也是我和水果君争论的核心所在:
虽然我的写法复杂一点,但是,我觉得我可以很明确的告诉别人这就是一个地址。

正如我发帖子或者在q群里讨论,总是不厌其烦,说的大白菜一样。
而不是假设你和我一样知道一些东西。

这样虽然我可以省事很多,少说很多话,打少很多字,但是却极有可能产生沟通的误解,因为双方很可能
不是建立在相同的已知上。

这也是我为什么非常厌恶和吐槽一些半导体厂商提供的文档的理由。
因为他们说的根本不是人话,不是站在开发者使用者的角度去说事。

似乎他们很希望和你分享他们设计芯片和外设原理一样,却不知道这样把我们搞的是一塌糊涂。
我们只不过想学会如何配置和使用,它们却罗罗嗦嗦说另一个方面的问题。

好了,就此结束。


此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

1372

帖子

2

TA的资源

五彩晶圆(初级)

5
 
本帖最后由 cruelfox 于 2016-2-12 18:48 编辑

我差点被你绕晕了。

(uint32) GPIOx  还是写  *(uint32_t*)&GPIOx

我倾向于前者,或者C++写 static_cast(GPIOx)
因为 GPIOx 已经是一个地址了,地址值的比较么,直接当作32位整型(这里隐含32-bit平台前提)比较就是了。写成后面那种形式,这是何苦呢,呃,还要取一个地址……如果参数是寄存器传递的话,地址是哪儿呢?

就好比说有有符号的数要转换成无符号的
int x=-1;
unsigned int y;
是写成 y=x; 呢,还是 y=(unsigned int)x; 呢,还是 y=*(unsigned int*)&x; 呢?
除了第一个写法会有编译警告,都是一样的效果。
此帖出自编程基础论坛
 
 
 

回复

1158

帖子

2

TA的资源

版主

6
 
感谢版主非常详细的解释了这个问题,以前看32的时候还真没有认真注意这个细节的地方,有时候人真的会“自以为是”,很多东西都觉得懂了,其实还是似懂非懂,在往里问一点就搞不清楚。也确实说明我们应该认真细心的学,脚踏实地一个一个搞清楚才是正确的学习方法。
此帖出自编程基础论坛
 
 
 

回复

6040

帖子

204

TA的资源

版主

7
 
我来对号入座了,我就是文中提到的水果君。
首先来讨论一个我认为很有意思的问题,就是这两个强制类型转换:
*(uint32_t*)&AAA 是否等价于 (uint32_t)AAA ?(假设我们不知道AAA的类型,变量还是常量)
为了便于讨论,我们假设变量uint32_t X=*(uint32_t*)&AAA, Y=(uint32_t)AAA;
乍一看,好像是有点等价的意思,但是仔细想想,又不是那么回事,这还取决于AAA的类型。
(1).现在假设AAA是2字节short型=0x1234;
那么X的结果是强制从AAA的地址中取走4字节,其中2字节未知:
X=0xXXXX1234 (小端情况下)
Y的结果就比较确定,编译器帮他把高位填0, Y=0x00001234;
(2).假设AAA是64位long long类型=0x1234;前面的0我就不写了。
那么X还是取走4个字节,根据大小端而异,可能是高4字节,也可能是低四字节。
Y只是简单的舍弃了高四字节,结果比较确定。
(3). 假设AAA是个数组,这个情况比较特殊,数组名本身就是数组的首地址,取地址后还是相同的值:
因此X会取出数组中的元素
而Y却还是一个地址值。
(4). 一个不靠谱的假设AAA是个结构体
那么X可以取到结构体的成员
而Y的写法就直接报错了,不允许强制类型转换。
(5). AAA是uint32_t类型,那么没什么问题,X与Y等价。

由此可见:
X写法确实是无条件强制转换,基本是无所不能转,但是如果类型不匹配就很容出错了。
Y写法是有限制的编译器参与的半智能转换,因为编译器知道源类型与目标类型,会帮忙参与转换,或者类型转换不太靠谱的话,直接报错。

由此可以得出结论,在使用强制类型转换的时候,必须具有可行性,同时也必须清楚转换后的结果,也就是说,程序员(写程序的人)必须清清楚楚地知道源类型和目标类型到底是什么,否则还是去读书深造的好。

现在我来说说为什么我觉得uint32_t X=*(uint32_t*)&GPIOx有脱裤子放屁的嫌疑(从阅读程序的角度来说)。
首先有个变量GPIOx,是个指针,也就是个地址1,此地址上面存放的类型是GPIO_TypeDef。
然后对这个地址1取了个地址2,那么这个地址2的类型是GPIO_TypeDef **
现在对地址2进行强制类型转换,转成uint32_t*,也就是间接说明,不再把地址1当做地址(指针)看待,而是作为一个uint32_t类型。
然后在地址2中的uint32_t数据取出来,完毕。牛逼的程序员也看出来了,这就是拐外抹角的把GPIOx转成uint32_t类型。
一般刚入门的程序员看了会不会很懵逼?

好,说的有点乱,我们按教主的思路重新捋一下:
我们假设不知道GPIOx到底是个什么东西,就当做是一个不知道类型的普通变量。
引用:“——————————————————————————————————
水果君在那个帖子里认为,其实这个地方是多此一举,可以直接写成
(uint32)GPIOx
说实在的,这个,和 那个显得很麻烦的写法
*(uint32_t*)&GPIOx
效果确实是一样的。
只是,我强调 后面这种写法,我的理由在于:
可读性。
看到前者,你不会联想到 GPIOx是一个地址,而看到后者,稍微有点经验的C程序员都马上会领悟到这一点。
————————————————————————————————引用结束”
看看我对脱裤子的理解:
首先一个变量GPIOx,不知道其类型
然后对此变量取了个地址,此地址类型也未知。
然后对这个地址进行强制转换成uint32_t类型的一个地址(此处影射GPIOx是个uint32_t类型)
最后,从这个地址中取出了一个uint32_t类型的变量,完成了最终这个语句的使命。
这样理解,也根本看不出GPIOx有地址的意思(只是明确了变量的地址是个具体类型的地址)。只是拐了个弯,把GPIOx强制转成uint32_t类型而已。

然而,把一个地址(指针)转成一个整形数,就很常见不过了。uint32_t Y=(uint32_t)GPIOx。

由此,可以看到两个转换的最终区别:脱裤子那种,是强制转换的变量地址的类型,间接对数据类型进行转换,而直接转换就是直接对变量进行转换。
此帖出自编程基础论坛

赞赏

1

查看全部赞赏

 
 
 

回复

613

帖子

2

TA的资源

纯净的硅(初级)

8
 

小虾米来此一游,哈哈,两只大虾的贴子我来看看噢
此帖出自编程基础论坛
 
个人签名我是一头搞电子的猪,猪是一种好色的动物,猪八戒就是代表.       
 
 

回复

6423

帖子

17

TA的资源

版主

9
 
听君一席话胜读十年书
此帖出自编程基础论坛
 
个人签名training
 
 

回复

89

帖子

0

TA的资源

一粒金砂(中级)

10
 
此帖出自编程基础论坛
 
 
 

回复

4177

帖子

9

TA的资源

五彩晶圆(高级)

11
 
辛昕 发表于 2016-2-12 16:49
水果君在那个帖子里认为,其实这个地方是多此一举,可以直接写成

(uint32)GPIOx

说实在的,这个, ...

辛辛,不好意思,今天才看到你们在这里讨论的帖子。回想起我回复的那个帖子,我感觉我的想法太过于稚嫩了,看到你们的回复后又理解了一些。
此帖出自编程基础论坛
 
 
 

回复

4177

帖子

9

TA的资源

五彩晶圆(高级)

12
 
lcofjp 发表于 2016-2-12 19:17
我来对号入座了,我就是文中提到的水果君。
首先来讨论一个我认为很有意思的问题,就是这两个强制类型转换 ...

如果没有记错的话,您应是Super哥(老是在水军群改名字,我也挤不太清楚了)。看到你们的回复,我对我之前在那个帖子中稚嫩的回复感到惭愧。你们的回复,又让我理解深入了。感谢。
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

13
 
huaiqiao 发表于 2016-2-17 10:15
如果没有记错的话,您应是Super哥(老是在水军群改名字,我也挤不太清楚了)。看到你们的回复,我对我之 ...

然而我不得不悲伤地说,其实我是对的,水果君是错的。
请稍等,我更新主题帖
此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

14
 
lcofjp 发表于 2016-2-12 19:17
我来对号入座了,我就是文中提到的水果君。
首先来讨论一个我认为很有意思的问题,就是这两个强制类型转换 ...

看到你回这么长的贴,我也是挺感动的,然而很不好意思的告诉你。
虽然你没错,但我还是要比你更正确......

很简单,看好了。

在32位机器下,你是对的,你还是更简洁的。
然而如果这种写法,在16位机或者64位机 等非32位机下就会出错。
why?
很简单,,非32位机的 地址非4字节,而是 16位机的2字节,64位机的8字节。
这种情况下,对应的指针字长也就成了 2字节 8字节。

于是结果已经很明显。
你每次都uint32去强转地址,问题是,你强转的是一个地址.......那也就是说。
对16位机,你多转了后面未知的2字节,对64位机,你少转了后面需要的4字节。
所以,必然是错的。

而原来那种看起来复杂的写法呢?
木有错,为毛?
因为,,uint32_t * 也是一个指针,或者地址(指针或地址随你叫吧)
因为在同一机器下,任何类型指针的字长都是一样的。
所以这种情况下,我读到的地址值永远不会少或者多。

这个问题意味着。
在你的写法里,你需要去假设指针字长,比如uint32,但这永远只能对一种机器字长适应。
而那个复杂的写法,则无此需求,不需要作任何假设,也就不会受限于任何机器字长的限制。

事实上,我写成

*(uint8_t *)
*(uint16_t *)
.....
都木有任何关系
此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

130

帖子

1

TA的资源

一粒金砂(高级)

15
 
(uint32) GPIOx  还是  *(uint32_t*)&GPIOx
如果GPIOx是形参,无论哪种写法,得出的汇编都一样,都是直接取R0,

如果GPIOx 是全局变量,汇编结果也是一样,都是取GPIOx变量的内容
如果GPIOx变量存放到0x10001000,直接取0x10001000里面的内容

编译器非常聪明,认为对指针变量X取地址A再取A里面的内容和直接取X里面的内容是一样的!
此帖出自编程基础论坛
 
 
 

回复

6040

帖子

204

TA的资源

版主

16
 
samos2011 发表于 2016-2-17 11:57
(uint32) GPIOx  还是  *(uint32_t*)&GPIOx
如果GPIOx是形参,无论哪种写法,得出的汇编都一样,都是直接取R ...

虽然效果一样,但是明显给阅读程序带来了困难,一般人看到这个表达式都要思考一下才敢下决定。

如我在7楼所说,如果换一种类型,例如:
(uint16_t) GPIOx  和  *(uint16_t*)&GPIOx 未必一样,或许在小端机器上同样被优化,但是大端机器上肯定就不同了。
此帖出自编程基础论坛
 
 
 

回复

6040

帖子

204

TA的资源

版主

17
 
辛昕 发表于 2016-2-17 10:44
看到你回这么长的贴,我也是挺感动的,然而很不好意思的告诉你。
虽然你没错,但我还是要比你更正确.... ...

很显然,@辛昕(别名教主)对我的反驳是很无力的
在@freebsder叔叔的蛊惑下,一直在找我的漏洞,我也觉得代码这东西没有绝对的对与错,虽然那种写法看起来很复杂并且高大上,一个语句展现了好几个C语言的知识点,而且结果正确,一点毛病没有,只是让人读起来需要有个停顿思考的时间。

C语言在程序移植这里确实存在许多诟病,在不同硬件平台上,数据类型的长度并不统一。
例如通常int在16位机为2字节,32位机为4字节,指针也一样,根据机型有2字节,4字节或者8字节的长度,记得还有3字节的……因为硬件平台种类实在是太多了,五花八门。
为了应付这类问题,C99标准出台了更具体的类型,如int16_t,uint32_t这样的具体长度类型,使程序在不同硬件平台更容易移植。但是……。
遗憾的是,这些类型中没有指针,我觉得因为指针也没法具体化。因此,教主提出了他的问题:在不同平台上如何用整型来表示指针?
其实这问题的根源完全来自于switch语句,因为在switch中只能使用整型数据类型,不可以是指针,如果非要去比较指针,那么只能转成整型。因此,如果把指针转为整型成了讨论的重点,但是很明显,教主的结论是错误的,他的写法并不能实现他要的结果。
大家都应该知道,指针是有类型的,解引用的时候会得到相应的类型:
*(uint32_t*)xxx  结果将是uint32_t
*(int16_t*)xxx 结果就是int16_t
根本得不到他所想要的与平台相关的数据类型。
因此在C语言中,想要得到指针所对应的整型类型,只能通过手动指定,例如微软就是这么干的
#ifdef _WIN64
    typedef __int64          intptr_t;
#else
    typedef int              intptr_t;
#endif
这样intptr_t就可以确保能保存指针类型。
但是可惜这只是某些厂家这么干,ST的库中并没有这样的类型,否则这事就好办了(当然了,ST目前可能也没考虑推出64位的单片机)
我不知道*(uint32_t*)&GPIOx这样的代码是否是出自ST的标准库,我没有去考证,如果真这样写的话他们自己可能也看着别扭,所以我看到st的某个版本库里面看到的是直接比较指针,当然了,就不能使用switch语句了,而是if语句,像这样:
if (GPIOx == GPIOA) xxx_statement 。反正我觉得这样写是最直观最易读的了,给他们点个赞!

c语言的争议太多了,就像#define与typedef之争,#define与const之争,程序员的理论就是运行结果没错那就都不是大问题,不说了,累,继续搬砖了。
此帖出自编程基础论坛
 
 
 

回复

6040

帖子

204

TA的资源

版主

18
 
忘了总结了:
虽然看似老婆都是别人家的好,但是代码并不是别人写的就一定好,即使你是一个新手,也不要随便拿来主义随便借鉴,包括我写的代码,很多都比较烂,写的并不严谨,只是为了应付能用就行(能保证在我的应用环境中不出错就行),因为把代码写完美确实是一件很不容易的事,很费时间和精力。
此帖出自编程基础论坛
 
 
 

回复

130

帖子

1

TA的资源

一粒金砂(高级)

19
 
本帖最后由 samos2011 于 2016-2-17 14:40 编辑

有以下代码:
  1. char xx;
  2. void GPIO_DeInit1(GPIO4_TypeDef* GPIOx) {
  3.         xx = (char)GPIOx;
  4. }
  5. void GPIO_DeInit2(GPIO4_TypeDef* GPIOx) {
  6.         xx = *(char*)&GPIOx;
  7. }
复制代码


在32位机测试结果如下:(ARMCC编译器,优化级别-O0,-O1,-O2,-O3都一样)

  1. xx = (char)GPIOx;
  2. 0x10000558 4919      LDR      r1,[pc,#100]  ; @0x100005C0
  3. 0x1000055A 7008      STRB     r0,[r1,#0x00]
复制代码

前者虽然效果非常好,但编译器给出警告: warning:  #767-D: conversion from pointer to smaller integer

  1. xx = *(char*)&GPIOx;
  2. 0x1000055E B501      PUSH     {r0,lr}
  3. 0x10000560 4668      MOV      r0,sp
  4. 0x10000562 4917      LDR      r1,[pc,#92]  ; @0x100005C0
  5. 0x10000564 7800      LDRB     r0,[r0,#0x00]
  6. 0x10000566 7008      STRB     r0,[r1,#0x00]
  7. 0x10000568 BD08      POP      {r3,pc}
复制代码


最终结果还是一样

当把char修改为int型,编译器才能对这句xx = *(char*)&GPIOx优化
  1. xx = (int)GPIOx;
  2. 0x10000516 4919      LDR      r1,[pc,#100]  ; @0x1000057C
  3. 0x10000518 6048      STR      r0,[r1,#0x04]
复制代码

  1. xx = *(int*)&GPIOx;
  2. 0x1000051C 4917      LDR      r1,[pc,#92]  ; @0x1000057C
  3. 0x1000051E 6048      STR      r0,[r1,#0x04]
复制代码

对于这个编译器警告,我猜想,编译器认为不应该把指针变量(即内存地址)强制转换为小于这个地址宽度的值,地址就应该当地址用,不要破坏地址

通过你们的争论,让我对这两个写法产生了好奇,至于用那种,没有警告没有错误就行!


此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

20
 
lcofjp 发表于 2016-2-17 13:35
很显然,@辛昕(别名教主)对我的反驳是很无力的
在@freebsder叔叔的蛊惑下,一直在找我的漏洞,我也觉 ...

别闹了,跟我回家。
完全暴露出和我半斤八两的水平,咱跑吧,别在这丢人了。
这帖子接下来的时间交给 free叔和samos
我下次一定好好学反汇编..........
此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表