brokkr

brokkr 查看完整档案

北京编辑  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

brokkr 回答了问题 · 2月17日

c++ 的dll 转golang可以调用

没明白为什么要用swig来封装?go 可以直接调用dll的,写成类似:

var (
    user32, _     = syscall.LoadLibrary("user32.dll")
    messageBox, _ = syscall.GetProcAddress(user32, "MessageBoxW")
)

这样的就可以直接调用了。
大概看了一下你说的“易盛期货”的dll,导出函数只有四个:

TAP_DLLEXPORT ITapTrade::ITapTradeAPI *TAP_CDECL CreateITapTradeAPI(const ITapTrade::TapAPIApplicationInfo *appInfo, ITapTrade::TAPIINT32 &iResult);
TAP_DLLEXPORT void TAP_CDECL FreeITapTradeAPI(ITapTrade::ITapTradeAPI *apiObj);
TAP_DLLEXPORT const ITapTrade::TAPICHAR *TAP_CDECL GetITapTradeAPIVersion();
TAP_DLLEXPORT const char  * TAP_CDECL GetITapErrorDescribe(ITapTrade::TAPIINT32 errorCode);

你按类似的方法来声名就好了,但在结构上Go与C++可能会有一定的区别,需要注意一下结构的差异性。

关注 3 回答 2

brokkr 回答了问题 · 2月17日

c++的 new int{10} 和 new int(10) 有什么区别?

对于 char, int, double 等基础类型来说,这两种初始化的方法是等同的。

class A
{
public:
    int a;
    // A(int _a) { a = _a; }
}

A* a1 = new A(10);
A* a2 = new A{10}

在 new 的对象是一个类的时候就有区别了,用(),如果没有构造函数会直接报错,用{}会直接按列表初始化来处理。如果有构造函数,这两种方式都可以正确的处理,{}也会去调用构造函数。所以C11之后,尽量用{}来做初始化构造。

关注 3 回答 2

brokkr 发布了文章 · 2020-12-30

C++ 变量零初始化需要注意的问题

在我们刚开始学习 C 语言的时候,就被教导过:“变量在使用前必须要初始化,很多bug都来自于未使用了未初始化的变量,因为某些编译器分配空间时,默认值并不为0。”
变量的零初始化是一个很普通的需求,但正是这个看起来很常见的需求,其实有很多需要注意的问题。

对于单一标准数据类型的零初始化,我们在定义变量的时候直接在后面加一句 = 0 就好了。但结构呢?每个结构变量逐个设置为0?数组呢?循环设置为0?
这些写法太啰嗦了,而且效率很低,我们需要一种更为简洁的方法。

memset

这是 C 语言时代就有的函数,但直至今日,仍然有大量的 C++ 程序员仍然使用 memset 来进行变量的零初始化工作。

这是用最原始的方法循环零初始化一个数组,显得非常的笨拙:

int a[10];

for (int i=0;i<10;I++)
    a[i] = 0;

而与之相对的, memset 只要一行就搞定了:

memset(a, 0, 10*sizeof(int));

甚至更进一步可以简化为:

memset(a, 0, sizeof(a));

编译器会帮我们计算 a 到底是多大。

memset 可以方便的清空一个结构类型的变量或数组,而让你不需要关心里面的细节,所以这种用法使用的非常普及。

坑1,对 const 数据执行 memset

void f1(char* a, size_t len)
{
    // ...
    memset(a, 0, len);
    // ...
}

int main()
{
    const char* a = "Hello World";
    f1((char*)a, strlen(a));

    return 0;
}

a 是一个 const char 类型的字符串指针,但 f1 函数需要的是一个 char 类型的指针,也许 f1 函数是一个第三方库里面的函数,
你为了让程序编译通过,调用 f1 时对 a 做了强制类型转换,但其实在 f1 函数内部对 a 执行了 memset,这时候程序会就会崩溃。

坑2,指定值初始化

如果我们希望数组 a 里面的初始化值不是 0 而是 1,下面的代码可以实现:

int main()
{
    char a[20];
    memset(a, 1, sizeof(a));

    return 0;
}

同理,你或许认为其它类型也可以这么做,譬如:

int main()
{
    int a[20];
    memset(a, 1, sizeof(a));

    return 0;
}

但你会惊奇的发现 a[0] = 0x01010101,a[1] = 0x01010101,...,这并不是你所希望的结果。
原因很简单,memset 处理的对象是字节,也就是说只有在 char 或 unsigned char 的时候,指定值初始化才符合你的预期。

坑3,对带有虚函数的类或结构做 memset

我们先看一个例子:

// 基类
class C0
{
public:
    virtual void f0() = 0;    // 这是一个纯虚函数
};

// 派生类
class C1 : public C0
{
public:
    C1()
    {
        memset(this, 0, sizeof(C1));
    }

    virtual void f0()
    {
        std::cout << "this f0" << std::endl;
    }
};

int main()
{
    C1 c1;
    C0* c0 = &c1;   // 
    c0->f0();        // 这里会崩,因为 f0 指针已经被清零了

    return 0;
}

对于 C++ 来说,类的虚表是算在类本身占用的空间中的,C1 的构造函数中的 memset 会把虚表的指针 B_vfptr 也一并清空,那么在调用派生类的 f0 的时候就会崩溃。

坑4,对带有 STL 元素的类或结构做 memset

struct ST
{
    string str1;
    string str2;
    char str3[8];
};

// memset 会破坏 string 的结构
int main()
{
    ST s;

    memset(&s, 0, sizeof(ST));
    s.str1 = "hello";
    s.str2 = "world";

    printf("%s %s %x,%x,%x,%x,%x,%x,%x,%x\n", s.str1.c_str(), s.str2.c_str(), s.str3[0], s.str3[1], s.str3[2], s.str3[3], s.str3[4], s.str3[5], s.str3[6], s.str3[7]);

    return 0;
}

这个程序在 Windows 用 VS2017 编译后的输出是:
?7籑 7籑 00,00,00,00,00,00,00,00

在 ubuntu 下用 g++ 7.5.0 编译后的输出是:
hello world 00,00,00,00,00,00,00,00
这个版本的结果看上去是正确的,但用 gcc 4.8.5 编译结果是会崩的

对带有 string, map, vector 等 STL 元素的类或结构使用 memset,会产生崩溃、内存泄漏...等一系列未知的问题,问题的严重性与编译器有关。
这个错误是很多程序员最容易犯的错误,而且不容易排查,所以要养成习惯:不对任何使用了 STL 容器的类或结构使用 memset 来做零初始化。

对 memset 的总结

那么我们什么时候可以使用 memset 做零初始化呢?这里有一个规则,当初始化的对象是一个POD类型(Plain Old Data)时是可以用 memset 做零初始化的。
POD用来表明C++中与C相兼容的数据类型,可以按照C的方式来处理(运算、拷贝等)。非POD数据类型与C不兼容,只能按照C++特有的方式进行初始化。
C 语言的标准数据类型:char、short、int、long、long long、float、double 这些都是 POD类型,都可以用 memset 做零初始化。
使用 C 语言的标准数据类型定义的数组也是 POD 类型,也可以用 memset 做零初始化。
对于类和结构来说,很多东西会破坏 POD 的约定,只使用了 C 语言的标准类型定义的类和结构还是 POD 的,前提是你没定义基类、没使用虚函数...,
具体到底哪些 C++ 规则会破坏 POD 约定请自行百度,但总的来说,不建议使用 memset 对类或结构进行零初始化工作。

C++ 的定义初始化

如果你不是在函数内部需要对外部传递进来的数据指针做零初始化工作的化(这种情况下还是需要使用memset),在 C++ 11 以后,在进行数据定义的同时做零初始化是更为优雅的一种方式。

int a[10] = { 0 };

这种定义的方式会让你直接获得一个已经清零的数组(注意并不是只有a[0]是0,而是全部都已清零)。这种方式看起来比 memset 还简单、便捷,似乎没有理由不去用它。

坑1,指定值初始化

上面这种写法,很容易让人写下下面的代码:

int a[10] = { 1 };

你是不是觉得自己会得到全部元素已被初始化成 1 的数组?但答案不是这样的。
上面的写法你得到的是 a[0] = 1, a[1] = 0, a[2] = 0, ... 也就是说除了 a[0] 以外,其它的数组元素被初始化为 0。
为什么是这样?因为有一条基本规则,数组初始化列表中的元素个数小于指定的数组长度时,不足的元素用默认值来初始化。
初始化列表里面只给了 1 个值,那么这个值被用来初始化 a[0],而 a[1] - a[9] 使用默认值 0 来做初始化。

坑2,对带有 STL 元素的类或结构做零初始化

我们去看看前面的例子,我们用定义初始化的方式来初始化带有 STL 元素的结构会怎么样

struct ST
{
    string str1;
    string str2;
    char str3[8];
};

int main()
{
    ST s = { 0 };

    s.str1 = "hello";
    s.str2 = "world";

    printf("%s %s %x,%x,%x,%x,%x,%x,%x,%x\n", s.str1.c_str(), s.str2.c_str(), s.str3[0], s.str3[1], s.str3[2], s.str3[3], s.str3[4], s.str3[5], s.str3[6], s.str3[7]);

    return 0;
}

你是不是觉得这样没问题?因为你用的是 C++ 的方法来做的初始化。但答案是程序可能在 ST s = { 0 }; 处崩溃,即使不崩溃,也不会输出期望的结果。
为什么会这样?仔细理解一下坑1里面讲的基本规则,ST s = { 0 }; 初始化列表里面只有一个 0 (int 类型),而结构的第一个元素是 string,它没有通过 int 来进行构造的方法。
这时候编译器就会把这个 string 类型当作是一个结构来处理,执行: memset(&str1, 0, sizeof(string)),也就是说编译器又去调用了 memset 来对 str1 执行初始化,不崩才怪。

如何进行正确的零初始化?

我们已经讨论了很多错误的零初始化方式,那么对于一个类或结构来说,什么才是正确的零初始化方式?
还是使用上面的例子:
我们把 ST s = { 0 }; 改为 ST s = { };
这下就 OK 了,因为你给了初始化列表,但初始化列表是空的,这时候编译器就会用 string 的默认值 "" 来初始化 str1, str2, 用 char 的默认值 0 来初始化数组,这正是我们想要的。

这个方法可以延展到所有的类型定义上:

int a{};    // 这种写法跟 a = {} 是等价的
float b = {};
int c[10] = {};

动态分配的数据的初始化工作

对于动态分配的数据,你也可以采用这种方法来做初始化工作。

int* a = new int[10] { };
string* b = new string[10] { };

我们看看如果不做初始化会怎么样:

int* a = new int[10];
string* b = new string[10];

这个时候 b 里面的 string 还都是 "",这是因为 string 的构造函数会默认初始化成 "",但 int 可没有构造函数,所以 a 里面的内容就是随机的。

还有坑?

看到这的时候,是不是觉得已经找到版本答案了?但生活中总是充满了惊(yi)喜(wai)。

struct ST
{
    string str1;
    string str2;
    char str3[8];

    ST() { }
};

int main()
{
    ST s = { };

    s.str1 = "hello";
    s.str2 = "world";

    printf("%s %s %x,%x,%x,%x,%x,%x,%x,%x\n", s.str1.c_str(), s.str2.c_str(), s.str3[0], s.str3[1], s.str3[2], s.str3[3], s.str3[4], s.str3[5], s.str3[6], s.str3[7]);

    return 0;
}

我们给 ST 添加了一个空的构造函数,现在得到的结果是:

hello world ffffffcc,ffffffcc,ffffffcc,ffffffcc,ffffffcc,ffffffcc,ffffffcc,ffffffcc

即使我写了 ST s = { }; str3 也没被初始化。也就是说,一个类或结构一旦你定义了自己的构造函数,编译器就不会再帮你去做初始化工作了。原因很简单,你自己的构造函数替代了编译器帮你默认生成的构造函数,而原来的初始化工作是这个默认的构造函数帮你完成的,一旦定义了自己的构造函数,那么初始化工作也就得自己来做了。

查看原文

赞 0 收藏 0 评论 0

brokkr 回答了问题 · 2020-07-20

为什么a:4 属于块级作用域

第一个 a = 1 使用的是循环外部定义的 a
function a 导致了局部变量的作用域覆盖了外部定义的 a
a = 4 修改的其实是 function a 定义的局部 a 定义
最后 } 导致局部作用域结束,外部作用域的 a 恢复。

关注 3 回答 2

brokkr 回答了问题 · 2020-07-19

解决内嵌汇编语言关于push的一个问题?

c++ 与汇编混合开发是微软专用的东西,linux 下只能用嵌入16进制代码的方式。
所以微软为啥要这么设计,就得问微软了。

push varstr
push [varstr]
push dword ptr varstr

微软都会翻译成

push dword ptr [varstr]

没啥道理可讲,知道就好,想办法规避吧。

关注 3 回答 1

brokkr 回答了问题 · 2020-07-19

解决make_pair为什么不支持传递左值参数?

你应该用

std::string s1 = "hello";
m.insert(std::pair<std::string, int>(s1, 10);

pair 构造是既支持左值又支持右值的

make_pair 的用法是不需要声明类型的时候,譬如:

m.insert(std::make_pair("hello", 10));

所以 make_pair 是强制右值的,你又用make_pair,又给出类型是一种非常规用法。

关注 2 回答 1

brokkr 回答了问题 · 2020-06-30

项目多客户问题,求解!

程序直连数据库服务器,一般只有在内网的时候才会这么做。外网的客户端应该连接的是服务端的程序,由服务端来访问数据库,所以你的问题就不会存在。

关注 5 回答 5

brokkr 回答了问题 · 2020-06-27

正则如何提取网址的主域名的首字母?

    let test_str = [
        'http://baidu.com',
        'https://baidu.com',
        'http://map.baidu.com',
        'https://map.baidu.com',
        'http://www.baidu.com',
        'https://www.baidu.com',
        'www.baidu.com',
        'baidu.com',
        'map.baidu.com',
        'pic.baidu.com',

        'http://baidu.com.cn',
        'https://baidu.com.cn',
        'http://map.baidu.com.cn',
        'https://map.baidu.com.cn',
        'http://www.baidu.com.cn',
        'https://www.baidu.com.cn',
        'www.baidu.com.cn',
        'baidu.com.cn',
        'map.baidu.com.cn',
        'pic.baidu.com.cn',


        'http://baidu.com/mm',
        'https://baidu.com/mm',
        'http://map.baidu.com/mm',
        'https://map.baidu.com/mm',
        'http://www.baidu.com/mm',
        'https://www.baidu.com/mm',
        'www.baidu.com/mm',
        'baidu.com/mm',
        'map.baidu.com/mm',
        'pic.baidu.com/mm',

        'http://baidu.com.cn/mm/c',
        'https://baidu.com.cn/mm/c',
        'http://map.baidu.com.cn/mm/c',
        'https://map.baidu.com.cn/mm/c',
        'http://www.baidu.com.cn/mm/c',
        'https://www.baidu.com.cn/mm/c',
        'www.baidu.com.cn/mm/c',
        'baidu.com.cn/mm/c',
        'map.baidu.com.cn/mm/c',
        'pic.baidu.com.cn/mm/c',   

        'http://www.baidu.co.jp',
        'http://www.baidu.net',
        'http://baidu.fun/mm'        
    ]

    let r1 = /([\w\-]+?\.[\w\-]+?\.[\w]{2}?$)/
    let r2 = /([\w\-]+?\.[\w\-]+?$)/
    let r3 = /([\w\-]+?\.[\w\-]+?\.[\w]{2}?\/)/
    let r4 = /([\w\-]+?\.[\w\-]+?\/)/
    for (let str of test_str) {
        if (str.match(r1)) {
            result = str.match(r1)
        } else if (str.match(r2)) {
            result = str.match(r2)
        } else if (str.match(r3)) {
            result = str.match(r3)
        } else if (str.match(r4)) {
            result = str.match(r4)
        } else {
            result = '-'
        }
        console.log(result[1][0])
    }

输出全是 b

关注 5 回答 3

brokkr 回答了问题 · 2020-06-27

解决try catch的执行顺序

if (promise === value)

会丢异常,走cache分支,返回true

if (value && (typeof value === 'function' || typeof value === 'object')) {
    var then = value.then;  // then should be retrived only once
    if (typeof then === 'function')
    ...

虽然上面的 if 判断了 object 但后面并没有进一步处理,所以只有 value 是 function 才会走里面的分支,返回 true。

除了这两种情况都会返回false

关注 3 回答 2

brokkr 回答了问题 · 2020-06-26

解决leetcode c++ 设计循环队列 出现AddressSanitizer错误

问题出在你的 new 语句上,我知道你想做的是

p = new int[k];

new 一个数组出来

但实际你写的

p = new int(k);

只会 new 出一个 int 出来,并给这个 int 赋初值 k

然后你按k个int 去用,就把堆栈搞崩了。

关注 2 回答 1

认证与成就

  • 获得 29 次点赞
  • 获得 6 枚徽章 获得 1 枚金徽章, 获得 1 枚银徽章, 获得 4 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2020-06-02
个人主页被 708 人浏览