Go语言中的‘cannot take address of XXX‘

本文介绍了Go语言中'cannot take address of XXX'错误的原因,包括常量、map索引表达式和函数返回值不可寻址。常量和函数返回值因未分配特定内存而无法获取地址,而map值的地址可能导致指向无效地址。同时,复合文字可以通过&运算符获取其地址。

先上个结论:常量、map索引表达式和函数返回的值是不可寻址的。

来自某本书的摘抄-----

“For an operand x of type T, the address operation &x generates a pointer of type *T to x. The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array. As an exception to the addressability requirement, x may also be a (possibly parenthesized) composite literal. If the evaluation of x would cause a run-time panic, then the evaluation of &x does too.”

实际上,像 4、int64(4) 这样的常量或像 foo() 这样的函数返回值没有地址,因为没有为它们分配特定的内存(局部变量在函数被调用结束后内存会被回收,而常量由于本身特性是不可以被改变的,因此不能通过指针来进行修改);它们可以(maybe)在处理器寄存器中(基础类型可能在,但是复杂类型也许在栈中)。而对于map的元素num增加或减少的时候,则map将被重新分配。如果可以检索映射元素的地址,那么这样的指针可能会在重新分配之后指向无效地址。因此,不允许获取映射值的地址。

(可以将 & 视为获取某个现有变量的地址的运算符,但有一个例外:可以创建一个复合文字并使用 & 来获取其地址,例如 &T{}、&map[string ]int64{"a": 1} 或 &[]int{} 是有效的表达式。)

 

 

推荐阅读:3.2 计算机结构 · Go语言高级编程 (gitbooks.io) 

推荐阅读: Solve 'cannot take address of XXX' error in Go (Golang) (gosamples.dev)

在C++中,`void`类型是一个特殊的类型,它表示“无类型”。由于其语义上的特殊性,`void`类型有一些限制,其中之一是**无法对`void`类型的右值(rvalue)取地址**。这种限制通常表现为编译错误,例如: ``` error: cannot take the address of an rvalue of type void ``` ### 原因分析 1. **`void`类型的本质** `void`本质上不表示任何具体的数据结构或对象,因此它没有大小、没有内存布局,也无法被实例化为一个实际的变量。虽然可以声明一个`void`指针(如`void*`),但该指针仅能保存任意类型对象的地址,并不能直接操作`void`本身。 2. **右值与地址的关系** 右值(rvalue)指的是临时对象或字面量,它们通常是不可变的且生命周期短暂。在C++中,大多数右值是没有内存地址的,除非它们是表达式生成的临时对象(例如用户自定义类型的临时实例)。然而,对于`void`类型,甚至无法创建这样的临时对象[^4]。 3. **标准规则的约束** C++标准明确规定,只有左值(lvalue)才能使用`&`运算符取地址。对于右值,特别是像`void`这样无法绑定到引用或具有地址的对象,尝试对其取地址会导致编译失败。 ### 示例代码与错误说明 以下是一个典型的错误示例: ```cpp void foo() {} int main() { void (*funcPtr)() = &foo; // 正确:函数指针指向具名函数 void* ptr = &(void())(); // 错误:尝试对void类型的右值取地址 } ``` 在上述代码中,`void()`是一个默认初始化的`void`类型的临时对象,但由于其不具备实际的存储意义,编译器不允许对其取地址。 ### 解决方案 要解决这个问题,可以从以下几个方面入手: 1. **避免直接对`void`类型取地址** 如果目标是获取某个函数或表达式的地址,请确保该表达式的结果不是`void`类型。例如,在涉及函数指针时,应明确指定返回类型为非`void`类型。 2. **改用中间变量或包装机制** 若需要处理可能返回`void`的表达式,可以通过封装方式将其转换为某种可操作的类型。例如,使用`std::function`和`std::bind`进行函数对象包装: ```cpp #include <functional> #include <iostream> void foo() { std::cout << "Hello, World!" << std::endl; } int main() { std::function<void()> funcWrapper = foo; funcWrapper(); // 调用封装后的函数对象 } ``` 3. **使用`std::addressof`代替`&`运算符** 在某些极端情况下,如果确实需要获取对象地址,可以考虑使用`std::addressof`来规避重载的`operator&`导致的问题。不过,这仍然不能用于`void`类型的右值。 ```cpp #include <memory> // for std::addressof struct S {}; S makeS() { return S{}; } int main() { S s; S* p1 = std::addressof(s); // 合法 S* p2 = std::addressof(makeS()); // 不合法:右值仍然是临时对象,不能取地址 } ``` 4. **重新设计接口逻辑** 如果遇到必须对`void`类型右值取地址的需求,建议重新审视程序的设计逻辑。通常这意味着当前的设计存在不合理之处,应该通过重构函数签名或调整调用方式来规避这一问题。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值