想了解Golang:不同结构类型之间是否可以转换?的新动态吗?本文将为您提供详细的信息,我们还将为您解答关于golang结构体转换的相关问题,此外,我们还将为您介绍关于2022-06-18:golan
想了解Golang:不同结构类型之间是否可以转换?的新动态吗?本文将为您提供详细的信息,我们还将为您解答关于golang结构体转换的相关问题,此外,我们还将为您介绍关于2022-06-18:golang与 C++数据结构类型对应关系是怎样的?、3-2.结构类型之数组类型、c – 如何通过非缩小转换来编写类型特征以检查类型是否可以转换为另一种类型?、c++ 于编译期检测两个类型之间是否可以转化的新知识。
本文目录一览:- Golang:不同结构类型之间是否可以转换?(golang结构体转换)
- 2022-06-18:golang与 C++数据结构类型对应关系是怎样的?
- 3-2.结构类型之数组类型
- c – 如何通过非缩小转换来编写类型特征以检查类型是否可以转换为另一种类型?
- c++ 于编译期检测两个类型之间是否可以转化
Golang:不同结构类型之间是否可以转换?(golang结构体转换)
假设我有两种类似的类型设置为:
type type1 []struct { Field1 string Field2 int}type type2 []struct { Field1 string Field2 int}
知道它们具有相同的字段,是否有直接的方法将值从类型1写入类型2?(除了编写将所有字段从源复制到目标的循环外)
谢谢。
答案1
小编典典对于您的特定示例,您可以轻松地将其转换playground
:
t1 := type1{{"A", 1}, {"B", 2}}t2 := type2(t1)fmt.Println(t2)
2022-06-18:golang与 C++数据结构类型对应关系是怎样的?
2022-06-18:golang与 C++数据结构类型对应关系是怎样的?
答案2022-06-18:
uintptr和unsafe.Pointer相当于c++的void*,也就是任意指针。 uintptr可以参与指针运算,unsafe.Pointer不能参与指针运算。
c++和golang对照表如下:
c++类型 | golang类型 | 备注 |
---|---|---|
char | byte | |
char * | string | 函数入参或出参 |
char * | uintptr或者*byte | 结构体成员或者回调函数参数 ,不能用string |
byte | byte | |
byte * | *byte | |
int16 | int16 | |
int16 * | *int16 | |
uint16 | uint16 | |
uint16 * | *uint16 | |
int32 | int32 | |
int32 * | *int32 | |
uint32 | uint32 | |
uint32 * | *uint32 | |
int64 | int64 | |
int64 * | *int64 | |
uint64 | uint64 | |
uint64 * | *uint64 | |
float | float32 | |
float* | *float32 | |
double | float64 | |
double* | *float64 | |
bool | bool | |
void * | uintptr | |
结构体 | 结构体 | |
结构体* | *结构体 | |
枚举 | int32 | |
typedef 返回类型 (*函数名)(参数列表) | type 函数名=func(参数列表)uintptr | 返回类型占用的内存小于uintptr,需要指定为uintptr |
结构体中的函数指针 | uintptr |
go类型和uintptr的相互转换如下:
go源类型 | go目标类型 | 转换方法 |
---|---|---|
int | uintptr | uintptr(a)。byte,int16,int32,int64都可以 |
uintptr | int | int(a)。byte,int16,int32,int64都可以 |
*int | uintptr | unsafe.Pointer(a)。所有指针都是这种转换方式,函数指针例外 |
uintptr | *int | (*int)(unsafe.Pointer(a))。所有指针都是这种转换方式,函数指针例外 |
float32 | uintptr | unsafe.Pointer(&a)。注意:跟int的不一样 |
uintptr | float32 | *(*float32)(unsafe.Pointer(&a))。注意:跟int的不一样 |
float64 | uintptr | unsafe.Pointer(&a)。注意:跟int的不一样 |
uintptr | float64 | *(*float64)(unsafe.Pointer(&a))。注意:跟int的不一样 |
结构体 | uintptr | unsafe.Pointer(&a) |
uintptr | 结构体 | *(*结构体)(unsafe.Pointer(&a)) |
string | uintptr | 见代码UintPtrFromString |
uintptr | string | 见代码StringFromPtr |
bool | uintptr | 见代码CBool |
uintptr | bool | 见代码GoBool |
func | uintptr | syscall.NewCallback(a)或者syscall.NewCallbackCDecl(a)。这是函数指针 |
uintptr | func | 不知道怎么转换,待定 |
func bytePtrFromString(str string) (res *byte) {
res, _ = syscall.BytePtrFromString(str)
return
}
// string → uintptr
func UintPtrFromString(str string) uintptr {
return uintptr(unsafe.Pointer(bytePtrFromString(str)))
}
// uintptr → string
func StringFromPtr(sptr uintptr) (res string) {
if sptr <= 0 {
return
}
buf := make([]byte, 0)
for i := 0; *(*byte)(unsafe.Pointer(sptr + uintptr(i))) != 0; i++ {
buf = append(buf, *(*byte)(unsafe.Pointer(sptr + uintptr(i))))
}
res = string(buf)
return
}
// uintptr → bool
func GoBool(val uintptr) bool {
if val != 0 {
return true
}
return false
}
// bool → uintptr
func CBool(val bool) uintptr {
if val {
return 1
}
return 0
}
// func转uintptr
func NewCallback(fn interface{}) uintptr {
//return syscall.NewCallbackCDecl(fn)
return syscall.NewCallback(fn)
}
3-2.结构类型之数组类型
3-2.数组类型(array)
转自delphii2010语法基础
数组是一种数据类型数据的有序集合,是代表一定数量具有相同类型变量的一种数据类型。Object Pascal数组可与任何简单数据类型或字符串类型等一起使用。数组可用于声明一个简单变量或作为一个记录类型定义的组成部分。
(1)数组的定义
声明一个数组变量,要求你提供一个标识符,使用array[əˈreɪ]保留词,在方括号中指定数组的界限,并指定编译器数组将用于存储什么类型,例如:
Var Check:array[1..100] of Integer;
范围标点‘..’用于表示Check是一个有100个整数的数组,这些整数从1到100编号。范围编号是一个子界类型,可以是0,也可以是正数或负数,或者字符,或其它有序类型。
下面是声明数组类型及数组类型变量的举例:
Type
TCheck = array [1..100] of Integer;
Var
CheckingAccount:TCheck;
上面是先定义数组类型,然后定义数组变量。其实上,也可以同时定义类型、变量,例如:
var
Kelvin:array[0..1000] of Temperatures;
TwentiethCentury: array[1901..2000] of Events;
LessThanzeroo: array[-999..-400] of Shortint;
DigiTValues:array [''0''..''9''] of Byte;
SecretCode:array[''A''..''Z''] of char;
访问数组中的元素很简单,只要在标识符后面的方括号中给出指定的元素的索引号即可。例如:
Check[5]:=12;
J:=9;
Check[J]:=24;
要访问数组中的所有元素,可以使用循环语句。例如 :
For J:=1 to 10 do
Check[J]:=0;
(2)多维数组
上面介绍的是一维数组。实际上,数组可以是多维的。例如,如果你想编写一个数组来容纳一张电子表格中的值,那么就可以使用2维数组。下面的例子说明如何使用2维数组定义一个有20行和20列的表格:
Type Ttable = array[1..20,1..20] of Double;
Var
BigTable:Ttable;
要将2维数组中的所有数据初始化,可以使用如下语句:
var Col,Row:Intger;
for Col:=1 to 20 do
for Row:=1 to 20 do
BigTable[Col,Row]:=0.0;
使用多维数组时,要记住的一件事是数组为每维所占据的RAM数都呈幂级数增加。例如:
Aline:Array[1..10] of byte;占用10个字节
AnArea:Array[1..10,1..10] of byte;占用10*10=100个字节
Avloume:Array[1..10,1..10,1..10] of byte;占用10*10*10=1000个字节
(3)字符数组
前面介绍的字符串,实际上就是一个1维字符数组,只不过Pascal对字符串类型作了特殊的准许,你可以把它看作一个整体。字符串类型本质上等同于下列类型:
type StringType:array[0..255] of char;
虽然你可以当一个字符串看待,但它仍然保持其数组的特性。例如在定义一个字符串类型变量时,你可以说明字符串的大小,就像你定义字符数组的大小一样。下面是几个字符串类型定义:
type MyString:string[15];
BigString:string;
LittleString:string[1];
上面语句定义MyString类型包含15个字符,LittleString包含1个字符,BigString没有说明大小,就取字符串包含字符的最大个数255。然后你可以定义这些类型的变量,就像使用其它类型一样:
Var MyName:MyString;
Letter,Digit:LittleString;
你可以对字符串变量进行赋值:
MyName:=''Frank P.BorLand'';
因为MyName长度为15,因此只能容纳15个字符。如果执行下面语句:
MyName:=''Frank P.Borland and ...'';
则MyName变量中只存有FranK.P.Borlan其余部分被舍弃。
为了取得字符串中的一个字符,可以按如下方法进行:
AChar:=MyName[2];
但是,如果索引大于字符串变量的长度,则结果不可知。例如:
AChar:=MyName[16];
则AChar将被设置为某个不确定的字符,换句话说,就是废字符。
在字符串类型的数组中,字符串的第一个位置[0]包含有字符串的长度,因此数组的实际长度比该字符串长度大1个字节。你可以使用Length函数或下面的代码来得到一个字符串的长度:
L:=Ord(String[0]);
(4)数组类型常量
数组类型常量的每个字段都是类型常量,一个数组类型常量由括号括起来的类型常量组成,不同类型常量用逗号隔开。像简单类型常量一样,数组类型常量用来定义一个数组常量,下面是一个例子。
type TStatus = (Active, Passive, Waiting);
TStatusMap = array[TStatus] of string;
const StatStr: TStatusMap = (''Active'', ''Passive'', ''Waiting'');
上面的例子首先定义一个数组TStatusMAp,然后定义一个数组常量StatStr。该数组常量的目的是把TStatus类型的值转化为对应的字符串。下面是数组常量StatStr元素的值:
StatStr[Active] = ''Active''
StatStr[Passive] = ''Passive''
StatStr[Waiting] = ''Waiting''
数组常量的元素类型可以是除文件类型以外的任何类型。字符数组类型常量既可以是字符也可以是字符串,例如:
const Digits: array[0..9] of Char = (''0'', ''1'', ''2'', ''3'', ''4'', ''5'',''6'', ''7'', ''8'', ''9'');
该数组常量也可以表示为:
const Digits: array[0..9] of Char = ''0123456789'';
初始化字符数组类型常量的字符串长度可以小于数组类型的定义长度,例如:
var FileName: array[0..79] of Char = ''TEST.PAS'';
这时数组余下的字符空间自定置NULL(#0),因此数组也变成了一个以NULL结尾的字符串。
多维数组类型常量的定义采用括号的形式,每一维用括号括起,不同维及不同元素常量之间用逗号隔开。最里面的常量对应最右面的维数。例如:
type TCube = array[0..1, 0..1, 0..1] of Integer;
const Maze: TCube = (((0, 1), (2, 3)), ((4, 5), (6, 7)));
Maze常量数组各元素的值为:
1
2
3
4
5
6
7
8
|
Maze[
0
,
0
,
0
] =
0
Maze[
0
,
0
,
1
] =
1
Maze[
0
,
1
,
0
] =
2
Maze[
0
,
1
,
1
] =
3
Maze[
1
,
0
,
0
] =
4
Maze[
1
,
0
,
1
] =
5
Maze[
1
,
1
,
0
] =
6
Maze[
1
,
1
,
1
] =
7
|
(5)开放式数组
所谓开放式数组,是指数组作为形参传递给过程或函数时其长度是可变的,这样在调用过程或函数时,可以传递不同长度的数组作为实际参数。开放式数组在过程或函数中作为形参可以定义为:
array of T
这里T是数组的元素类型标识符,实际参数必须是T类型的变量或元素类型为T的数组变量。在过程或函数内形参的作用可看作为下面的数组:
array[0..N - 1] of T
这里N是实参中元素的个数。实际上实参的上下界被映射到0到 N-1。如果实参是类型T的简单变量,则它被看成为只有类型T元素的数组。
开放数组只能以开放数组参数或一个未定义变量参数的形式传递到过程或函数。开放数组可以作为数值参数、常数参数或变量参数,并与这些参数具有同样的语法规则。作为形式参数的开放数组不允许整体赋值,只能访问它的元素。并且对元素的赋值不影响实参。当开放式数组作为数值参数时,编译器将在内存中开辟一块区域存放实参的拷贝,等过程或函数退出后再释放这块区域,这样当实参是个很大的数组时,可能会发生栈溢出的问题。在使用开放数组参数时,可以使用Low函数获得当前最小下标(不过总是为0),使用High函数获得当前最大下标,使用SizeOF函数获得当前数组大小。下面是一个例子,演示了开放式数组的使用。

Var
X1:array[1..10] of Double;
X2:array[1..30] of Double;
{Clear过程对一个Double数组的各元素清0,SUM过程计算一个Double数组的各元素之和。两个过程的参数都是开放式数组。}
procedure Clear(var A: array of Double);
var I: Integer;
begin
for I := 0 to High(A) do
A[I] := 0;
end;
function Sum(const A: array of Double): Double;
var
I: Integer;
S: Double;
begin
S := 0;
for I := 0 to High(A) do
S := S + A[I];
Sum := S;
end;
begin
Clear(X1);
Clear(X2);
Sum(X1);
Sum(X2);
end.

当开放式数组的元素类型为Char时,实参可以是一个字符串常数。例如:

procedure PrintStr(const S: array of Char);
var I: Integer;
begin
for I := 0 to High(S) do
if S[I] <> #0 then
Write(S[I])
else
Break;
end.

下面是合法的过程调用语句:
PrintStr(''Hello world'');
PrintStr(''A'');
(6)动态数组
在Delphi中,除了定义静态数组外,还可以定义动态数组。动态数组只需说明数组的类型信息(包括数组的维数和数组元数的类型),但不需要定义元素的个数。例如:
A: array[1..100] of string;//静态数组
B: array of integer//动态数组
C: array of array of string;//动态数组
这里A是静态数组,B是一维的整数动态数组,C是二维的字符串动态数组。动态数组没有固定的长度。相反,当为动态数组赋值或使用SetLength过程时,动态数组的内存空间将重新分配。动态数组的定义形式是:
array of baseType
例如:
var MyFlexibleArray: array of Real;
定义了一个类型为实数型的一维动态数组。注意,声明语句并没有为MyFlexibleArray分配内存。要为动态数组分配内存,需要调用SetLength过程。例如:
SetLength(MyFlexibleArray, 20);
上面语句分配20个实数,标号从0到19。
动态数组的标号是整数类型,标号总是从0开始。使用Length,High和Low函数可以取得有关动态数组的特性。Length函数返回数组中元素的个数。High函数返回数组的最大标号,Low返回0。
c – 如何通过非缩小转换来编写类型特征以检查类型是否可以转换为另一种类型?
不幸的是,出于向后兼容性的原因,C 03,GCC自4.7以来只给出了这些警告.
GCC的documentation表明此扩展不适用于SFINAE环境,但似乎是错误的:
#include <type_traits> #include <utility> template <typename From,typename To> class is_list_convertible_helper { template <typename To2> static void requires_conversion(To2 t); template <typename From2,typename To2,typename = decltype(requires_conversion<To2>({std::declval<From2>()}))> // ^ Braced initializer static std::true_type helper(int); template <typename From2,typename To2> static std::false_type helper(...); public: using type = decltype(helper<From,To>(0)); }; template <typename From,typename To> class is_list_convertible : public is_list_convertible_helper<From,To>::type { }; static_assert(!is_list_convertible<double,int>::value,"double -> int is narrowing!");
GCC 4.9.1给出了这个输出
$g++ -std=c++11 foo.cpp foo.cpp: In substitution of ‘template<class From2,class To2,class> static std::true_type is_list_convertible_helper<From,To>::helper(int) [with From2 = double; To2 = int; <template-parameter-1-3> = <missing>]’: foo.cpp:18:31: required from ‘class is_list_convertible_helper<double,int>’ foo.cpp:22:7: required from ‘class is_list_convertible<double,int>’ foo.cpp:26:48: required from here foo.cpp:10:46: warning: narrowing conversion of ‘std::declval<double>()’ from ‘double’ to ‘int’ inside { } [-Wnarrowing] typename = decltype(requires_conversion<To2>({std::declval<From2>()}))> ^ foo.cpp:26:1: error: static assertion Failed: double -> int is narrowing! static_assert(!is_list_convertible<double,^
如果没有为每个缩小转换添加特化,有没有办法让这个工作?
解决方法
c++ 于编译期检测两个类型之间是否可以转化
在学习《modern c++ design》的时候,学习到了如何在 compile-time 检测两个类型之间是否可以转化.这里的转换,既包括 int,long,double 这些数据类型之间的转换,也包括基类和子类之间的转换(也就是两个类之前是否存在 class A 继承自 class B)。但是这篇文章最核心点在于 compile-time 这个限定词。因为这些检测是在 compile-time 做出的,所以不存在任何 runtime 开销。
书中的解法是使用了 sizeof 这个关键字。提到 sizeof,忍不住想要先提一下这个关键字的妙用:你可以对任意复杂的表达式使用 sizeof 关键字,最关键的一点是,sizeof 后面的表达式并不会 runtime 被执行。也就是说 sizeof(expr) 的值,在 compile-time 就已经确定下来。下面有代码佐证:
#include <stdio.h>
int test();
int test2() {
printf(__func__);
return 1;
}
int main() {
void(sizeof(test()));
void(sizeof(test2()));
}
上面代码可以正常的通过编译,运行后并没有任何输出,也就是没有任何函数调用。可以看到,test 函数甚至没有定义,这个性质在之后的实现中会被使用到。sizeof 的故事到此为止,下面让咱们说回正题。实现检测两个类型是否可转换使用的主要用到的技术是函数的重载和函数匹配时的类型之间的转换。其利用的主要是这样的一个事实:对于函数
int f(Base); char f(...);
f(...) 代表最低等级的转换等级,其函数匹配的优先级最低,如果 class A 可以转化为 class Base,那么
f(A());
应该是调用第一个版本,否则则为第二个版本。细心的朋友想必已经注意到,这两个函数的重载,不但重载了参数,还重载了返回类型,正是使用不同的返回类型,搭配上前面的 sizeof 关键字,为我们这篇文章提供了一个完美的解决方案。
下面贴出代码
template<typename T, typename U>
class Conversion {
private:
static char Test(U);
static int Test(...);
static T MakeT();
public:
enum { exists = sizeof(Test(MakeT())) == sizeof(char) };
};
上面的代码有很多熟悉的面孔,包括最主要的函数重载和 sizeof 的使用。一个新面孔是 MakeT() 函数。这个是用来干嘛的呢?其实这个函数主要是为了兼容对于 T 的构造函数是 non-public 的情况。试想,如果将 MakeT() 调用替换为简单的 T(),那么上面的代码在遇到 T 类型的构造函数为 non-public 时,就无法奏效了,但是显然,我们并不关心这个类型的构造函数可不可以由客户端直接调用。引入了一个返回类型为 T 的函数以后,就巧妙的解决了这一点。
短短不到10行代码,我们便实现了在 compile-time 检测两个类型之间是否可以转化,而这个检测的开销基本为0,你不需要做任何函数的调用,没有任何try...catch,没有任何对象的拷贝析构。c++ 模板编程的妙用,真的让人赞叹。下面是最后的测试代码:
#include "conversion.h"
#include <iostream>
struct B { };
struct D : public B { };
struct T { };
int main() {
using namespace std;
cout << Conversion<int, double>::exists << endl;
cout << Conversion<int, int>::exists << endl;
cout << Conversion<int, B>::exists << endl;
cout << Conversion<D, B>::exists << endl;
cout << Conversion<T, B>::exists << endl;
}
输出为:
1
1
0
1
0
与我们的期望完全一致。这样的技巧在 STL、boost 中都有使用。鉴于自身水平有限,文章中有难免有些错误,欢迎大家指出。也希望大家可以积极留言,与笔者一起讨论编程的那些事。
今天的关于Golang:不同结构类型之间是否可以转换?和golang结构体转换的分享已经结束,谢谢您的关注,如果想了解更多关于2022-06-18:golang与 C++数据结构类型对应关系是怎样的?、3-2.结构类型之数组类型、c – 如何通过非缩小转换来编写类型特征以检查类型是否可以转换为另一种类型?、c++ 于编译期检测两个类型之间是否可以转化的相关知识,请在本站进行查询。
本文标签: