char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173] ?

[标题] char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173] ?!

今天又捧起久违的K&R C拜读了一遍。其实有点东西在6年前就想写,借着今天这个机会,终于把它写出来了。

初看一眼标题中的变量定义感觉是不是很抓狂?:)一直以来,C语言中关于指针、数据和函数的复合定义都是一个难点,其实,理解它也是有规律可循的。然而,即便是国内在讲解指针方面久负盛名的“谭本”也没有将这一规律说清楚,K&R C虽然提到了一点,却始终没有捅破这层窗户纸,也许是K&R觉得以“直观方式”解释太阳春白雪了点吧:)在Blog上面说说这种不值一提的dd倒正合适。

其实,理解C语言中复合定义的关键在于对变量声明语句中各修饰符结合律的把握,我们可以将它们的结合规律简单归纳如下:

(1) 函数修饰符 ( ) 从左至右
(2) 数组修饰符 [ ] 从左至右
(3) 指针修饰符 * 从右至左

其中,(1)与(2)的修饰优先级是相同的,而(3)比前两者的优先级都低,而且是写在左边的。下面我们给出3个直观的例子来说明如何借助结合律来理解复合变量声明,为了简单点,函数修饰符一律使用无形参的签名形式。

示例1. char (*(*x[3])())[5]
这是什么意思?别急,跟着走一遭咱就知道是什么了。根据结合律,我们可以依次写出与x结合的修饰符:

x -1-> [3] -2-> * -3-> () -4-> * -5-> [5] -6-> char

然后我们再来从左至右地对上述过程进行解释:

1说明:x是一个一维数组,数组中元素个数为3
2说明:上述数组中每一个元素都是一个指针
3说明:这些指针都是函数的指针,该函数的签名为( )
4说明:上面的函数所返回的值是一个指针
5说明:上面的指针所指向的是一个一维数组,其元素个数为5
6说明:上面的数组中的每一个元素均是一个字符

不知大家在上面的规范化步骤描述中看出端倪来了没有?:)这个声明的含义是:x是一个由3个指向函数A的指针所组成的一维数组,函数A返回指向一个元素个数为5的字符数组的指针。其实,以结合律来解析复合声明的方式是一种“由近及远”的方式:首先尝试着去说清楚离变量“近”的修饰符的含义,然后再对“远处” 的修饰符进行依次说明,从抽象到具体,从顶到底,层层细化。

实际上,我比较反感这种一步到位的复合方式,它不仅把变量定义和类型声明混为一谈,而且也不能直观地体现出类型的含义,更糟糕的是,这不符合典型的“积木化”的程序思维,我更倾向于采用typedef,以一种“由远及近”的方式来逐步定义变量的形态,即先定义若干基本类型,然后再在其基础上将其扩充成复杂类型,最后利用复杂类型定义变量。例如,上述的例子,如果要我来定义,我觉得如此定义比较恰当:

typedef char ArrayOfChar[5];
typedef ArrayOfChar* PointerOfArrayOfChar;
typedef PointerOfArrayOfChar (*PointerOfFunc)()
typedef PointerOfFunc ArrayOfPointerOfFunc[3]
ArrayOfPointerOfFunc pfa;


这种“堆积木”的方式实际上和那个复合声明是等价的,其看似繁冗,但对于程序员而言却很直观,所以平心而论,我比较推荐这种积木化声明方式,而不推荐以复合声明直接一步到位。

示例2. char (**x[3])()[5]
根据结合律,将上述声明改写如下:

x -1-> [3] -2-> * -3-> * -4-> () -5-> [5] -6-> char

1说明:x是一个数组,这个数组包括3个元素
2说明:每个元素均为一个指针
3说明:上面的指针又指向另一个指针
4说明:上面的第二个指针是一个函数的指针
5说明:上面的函数返回的是一个数组,这个数组包括5个元素?? (错误!)

从上述推导过程可以发现,当我们到达第5步时,其语义提到了“一个函数返回了一个数组”,这在C语言中实际上是错误的定义,即,( )与[ ]相邻是非法的,因此,编译器将拒绝接受这一关于x变量的声明。同样的,在推导过程中[ ]与( )相邻也是不合法的,什么叫做“一个数组,这个数组里面的每一个元素都是一个函数(而不是一个指针)”?在这种情况下,编译器也会100%报错。

示例3. char p[5][7]、char (*q)[7]、char *r[5] 和 char **s
不知p、q、r、s这四个变量类型是否兼容?根据结合律,有:

p -> [5] -> ([7] -> char) const
q -> * -> ([7] -> char)
r -> [5] -> { * -> char} const
s -> * -> { * -> char}

不难发现,无需经过类型强制转换即可将p赋值给q、将r赋给s,而其他的赋值方式均是错误的。为什么?首先,p和r是两个数组,不是指针,因此不能修改其值;其次,不妨让我们来对p与q(或者r与s)在其括号内的类型部分分别进行sizeof运算,可以发现,二者的结果是一样的,即:p、q(或者r、s)指针变量具备一致的增量寻址行为,所以二者才兼容。

看完了上述解释,想必最唬人的指针复合定义恐怕也难不倒你了。试试下面的挑战如何?

1. 解释一下x变量的含义:char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173];
2. 在32位环境下,假设void* p=(void *)(x+1),x=0x1234;则p的16进制值为多少?sizeof(x)等于多少?

;-)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 将一个 `char*` 类型的字符串转换为 `float` 类型,可以使用 `atof()` 函数。`atof()` 函数的功能是将一个字符串转换为浮点数。 下面是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> int main() { char str[] = "3.14"; float f = atof(str); printf("f = %f\n", f); return 0; } ``` 在这个例子中,我们首先定义一个 `char` 类型的数组 `str`,其中存储了一个浮点数的字符串表示形式。然后使用 `atof()` 函数将其转换为 `float` 类型的变量 `f`。最后,使用 `printf()` 函数将 `f` 的值输出到控制台。 需要注意的是,如果字符串无法转换为浮点数,`atof()` 函数将返回 0。因此在使用 `atof()` 函数时需要确保输入的字符串是有效的浮点数表示形式。 ### 回答2: 结构体char *迁至转换为float可以通过以下步骤实现: 首先,我们需要创建一个结构体,其中包含一个char类型的指针变量和一个float类型的变量。 ```c struct MyStruct { char* char_ptr; float float_num; }; ``` 然后,我们可以根据需要给结构体的char*成员赋值,这个char*指针可以指向一个字符串。 ```c struct MyStruct my_struct; char my_string[] = "3.14"; // 这里假设我们有一个表示浮点数的字符串 my_struct.char_ptr = my_string; ``` 接下来,我们可以使用合适的函数将字符串转换为浮点数。在C语言中,有一个名为`atof()`的函数可以实现这个功能。 ```c my_struct.float_num = atof(my_struct.char_ptr); ``` 最后,我们可以打印出转换后的浮点数值,以验证转换是否成功。 ```c printf("转换后的浮点数为: %f\n", my_struct.float_num); ``` 整个过程就是这样完成的。希望这个回答能够对你有所帮助! ### 回答3: 将结构体char *转换为float的过程主要涉及两个步骤:首先是将char *类型的指针转换为合适的字符串,然后使用相应的函数将字符串转换为float。 在进行指针转换的过程中,我们需要确保字符指针所指向的内存包含一个有效的浮点数字符串。如果字符串格式正确,那么可以通过调用函数strtod()或sscanf()将其转换为float类型。 例如,假设有一个结构体如下所示: ``` struct MyStruct { char *str_number; }; ``` 接下来,我们需要将结构体中的char *成员转换为float: ``` struct MyStruct myStruct; myStruct.str_number = "3.1415"; ``` 可以使用以下代码将字符串转换为float类型: ``` float number = strtod(myStruct.str_number, NULL); ``` 或者使用sscanf()函数: ``` float number; sscanf(myStruct.str_number, "%f", &number) ``` 以上两种方法都可以将字符串"3.1415"转换为3.1415的浮点数值,并将其存储在变量number中。 需要注意的是,进行类型转换时要保证内存中的字符串格式正确,否则将导致转换失败或结果不准确。另外,还需要注意转换后的float类型是否可以表示所需的精度和范围。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值