打印

手动构造完全与`char *argv[]` 等价的参数

[复制链接]
345|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
keer_zu|  楼主 | 2023-3-27 13:54 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
场景
在main函数中的两个参数(int argc , char *argv[]),都是系统构造的。通常来说,我们只需要去解析即可,不需要去构造这样一个参数。

然而,今天写代码时却不得不构造这样一个参数。原因是使用了一个第三方的模块(准确的说是基于第三方模块),第三方模块初始化时需要接受(int argc , char ** &argv)这样的参数。注意到其中的引用符号!

通常来说,直接传递main函数的argc , argv是没有问题的。事实也确实如此,传递main函数接受的argv不会有编译错误。不过有些坑就是,这个模块自己参数解析写得太武断!就是不允许有多余的参数项!一旦有多余参数项就会导致其停止解析参数,最终导致其解析失败。然而我的程序除了给这个模块传参数,还需要额外的参数啊!

没有办法,我就得自己构造仅仅包含第三方模块需要的参数argv , 但是狗血的事情出现了!

怎么构造就是不行!!编译一直报错:
用类型为‘char**’的右值初始化类型为‘char**&’的非常量引用无效

经过一系列的修改,终于正确编译了,过程曲折,都是因为概念理解的不清晰。


使用特权

评论回复
沙发
keer_zu|  楼主 | 2023-3-27 13:55 | 只看该作者
解决方案

只考虑argv的构造:

int fack_main(int argc , char **&argv);

int fack_argc = 2 ;
char arg0[] = "program name" ;
char arg1[] = "arg1 for thirdparth" ;
char **fake_argv = new char *[fack_argc+1]{ arg0 , arg1 } ;

fack_main(fack_argc , fack_argv) ;

delete [] fack_argv ;

以上就是完整的构造过程。这样构造的fack_argv应该与main函数中的argv是完全等价的。


使用特权

评论回复
板凳
keer_zu|  楼主 | 2023-3-27 13:58 | 只看该作者
解决过程

解决过程就很曲折。

1. 尝试 raw

fack_argv[2][100] { "program name" , "arg1"  } ;

fack_main(fack_argc , fack_argv) ;

报错:

用类型为‘char (*)[100]’的右值初始化类型为‘char**&’的非常量引用无效

看到这个错误还是很好理解的。说明参数传递时fack_argv还是是一个指向char[100]的指针类型char (*)[100],不是char **& 。

想到的解决办法是static_cat



使用特权

评论回复
地板
keer_zu|  楼主 | 2023-3-27 16:56 | 只看该作者
2.static_cast
fack_argv[2][100] { "program name" , "arg1" } ;
fack_main(fack_argc , static_cast<char **>(fack_argv)) ;
结果:
从类型‘char [3][100]’到类型‘char**’中的 static_cast 无效

为何强制转换会失效呢?

难道只能一步步转换?

试了以下代码:


fack_main(argc , static_cast<char **>(static_cast<char(*)[100]>(f_argv))) ; 

试了以下代码:

fack_main(argc , static_cast<char **>(static_cast<char(*)[100]>(f_argv))) ; 

想要通过两步完成,但是依然报错:

错误:从类型‘char (*)[100]’到类型‘char**’中的 static_cast 无效

这说明从一维数组到指针是OK的,但是第二步就失败了。一想,第二步时,代码其实是在将一个指向数组的指针转为一个指向指针的指针。这或许是编译器不支持的。

失败之后,再一想,既然直接声明二维数组失败了,那么看来只有声明一个一维的数组,数组里放char *就好了。


使用特权

评论回复
5
keer_zu|  楼主 | 2023-3-27 16:58 | 只看该作者
3.指向指针(char*)的数组
char arg0[] = "program name" ;
char arg1[] = "arg1" ;
char *fake_argv[2] = { arg0 , arg1  } ;
结果还是报错:
错误:用类型为‘char**’的右值初始化类型为‘char**&’的非常量引用无效
这次需要注意了,前面已经对了——我们已经构造了char**,无奈目标是 char **& 。但是奇怪的是,传main的argv就不会报错,那说明这二者间还是有差别的!
需要继续改!

这次着眼于后边部分——”非常量引用”!

是不是说char **可以转换为常量引用啊?


使用特权

评论回复
6
keer_zu|  楼主 | 2023-3-27 16:59 | 只看该作者
测试:将char *[] 转为常量引用


终于弄明白常量引用如何声明:


char *fake_argv[2] = { arg0 , arg1  } ;
char ** const (&ra) = fake_argv ;

通过!

从上面可以看出,所谓常量引用,是要求一个底层const,就是说ra的值不能改变。

这个测试对问题而言帮助不大,但是隐式地说明——还是变量类型声明出了问题。

网上搜索!


使用特权

评论回复
7
keer_zu|  楼主 | 2023-3-27 17:00 | 只看该作者
5.网上搜索argv *[]

查看 Defining own main functions arguments argc and argv

发现了问题是相似的,但是提供的解决办法依然不能解决形参是car **&的问题。

不过收获到:argv最后要多一个NULL结束标志。


使用特权

评论回复
8
keer_zu|  楼主 | 2023-3-27 17:01 | 只看该作者
6.完全的类型一致!
既然是说char **到char **&错误,但是我的实参依然是个数组,这说明编译器已经做了隐式转换。

我记得在类的构造函数中说过,类的隐式转换最多一次。

有没有这种可能:

值参数到引用参数,其实也是一个隐式转换?

再加上前面的猜测——最多一次隐式转换,问题就明白了。一次隐式转换用来将char *argv[]变为char **argv,所以就导致了char **到char **&的失败。

书读得不多,我觉得应该会有相关介绍的。这里只是猜测。

这样,就有了最后的方案:
int fack_main(int argc , char **&argv);

int fack_argc = 2 ;
char arg0[] = "program name" ;
char arg1[] = "arg1 for thirdparth" ;
char **fake_argv = new char *[fack_argc+1]{ arg0 , arg1 } ;

fack_main(fack_argc , fack_argv) ;

delete [] fack_argv ;
编译通过了!

使用特权

评论回复
9
keer_zu|  楼主 | 2023-3-28 15:16 | 只看该作者
st_NgwClientCallbackParam nccp;
int32_t func_cb(NGW_CALLBACK_CMD_ENUM cmd, const void *pdata, uint32_t data_len)
{
        return 0;
}

使用特权

评论回复
10
keer_zu|  楼主 | 2023-3-28 15:17 | 只看该作者
nccp.m_cb = func_cb;
nccp.m_group_msg_ids.push_back(1);

使用特权

评论回复
11
csdnpurple| | 2023-4-6 11:07 | 只看该作者
根本原因是二维数组的数据结构和指针的链式结构在内存中的存储方式是不同,操作方式也不同

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:qq群:49734243 Email:zukeqiang@gmail.com

1352

主题

12436

帖子

53

粉丝