从Entry Point到main函数调用(5):_cinit

C/C++启动过程解析

_cinit

在完成了_setargv() 以及_setenvp() 之后,进入到_cinit 函数。该函数的注释很短,就一句“do C data initialize”,让人完全摸不着头脑。不过不用着急,可以阅读_cinit 函数的实现来加以分析。

 

_cinit 函数很短,大致上分为三个步骤:

1. _fpmath() 或者 (*_FPinit)();

2. _initterm( __xi_a, __xi_z );

3. _initterm( __xc_a, __xc_z );

 

第一步 是可选的,_FPinit 主要用来初始化浮点运算。只有当用户写的代码中出现了浮点运算,_FPinit 才会被定义。关于_FPinit 由于MSDN 上没相关资料,在此不做深究。

 

第二步和第三步 是分别对C和C++程序做初始化。_initterm 接受两个指针作为参数,这两个指针中间的内存区域是一张函数指针表。_initterm 会从第一个指针开始,慢慢向后寻找,直到第二个指针结束,中间如果找到了一块内存表示一个函数指针,则执行该函数。

 

/*
 * pointers to initialization sections
 */

extern _PVFV __xi_a[], __xi_z[];    /* C initializers */
extern _PVFV __xc_a[], __xc_z[];    /* C++ initializers */

void _initterm ( _PVFV * pfbegin, _PVFV * pfend )
{
        /*
         * walk the table of function pointers from the bottom up, until
         * the end is encountered.  Do not skip the first entry.  The initial
         * value of pfbegin points to the first valid entry.  Do not try to
         * execute what pfend points to.  Only entries before pfend are valid.
         */
        while ( pfbegin < pfend )
        {
            /*
             * if current table entry is non-NULL, call thru it.
             */
            if ( *pfbegin != NULL )
                (**pfbegin)();
            ++pfbegin;
        }
}

 

_initterm( )

先来看看第二步、第三步中都做了什么。这里继续沿用一段空的C代码(main函数中没有任何东西)来build成exe,随后运行该exe,并且OD到_cinit 内部。

这里的两个call 分别表示调用了 _initterm( __xi_a, __xi_z ) 和_initterm( __xc_a, __xc_z ) 。对应的有:

__xi_a = 00406008

__xi_z = 00406010

__xc_a = 00406000

__xc_z = 00406004

 

继续跟进可以发现,在00406008 至 00406010之间仅有一个函数指针,指向__initmbctable()函数。 __initmbctable() 在第(4)篇_setargv 中曾经有过介绍,它会创建一个新的 创建一个新的multibyte code page,前提是之前并没有被创建。但是 _setargv 中已经调用过了该函数, 并将__mbctype_initialized 被设置为1 ,因此这里 __initmbctable()实际上不会重复创建。

 

在00406000 至 00406004 之间没有函数指针,实际上什么也不执行。

 

因此,对于空的C程序:

  • _initterm( __xi_a, __xi_z )  ------> 调用__initmbctable() ------> 实际上没做什么
  • _initterm( __xc_a, __xc_z ) ------> 不产生调用

 

现在来换一段C程序:

#include <stdio.h>
#include <math.h>

void main()
{
	int a = 5;
	double b = sqrt(a);
	printf("%f\n",b);
}
 

跟踪的结果为:

  • _initterm( __xi_a,  __xi_z )  ------> 先后调用__initstdio ,__initmbctable 
  • _initterm( __xc_a, __xc_z ) ------> 不产生调用

这里由于调用了stdio标准库,因此为了stdio能够正确的工作,需要进行初始化。暂时没有找到关于initstdio 函数的资料,暂时不作深究。

 

至于为什么第二个initterm 不会产生函数调用,这是因为第二个initterm 是用于C++ data initializations,所以在C 程序中毫无作为。来看一段简单的C++ 代码:

int foo(){
	int a=1;
	int b=9;
	return a+b;
}

int a = foo();

void main()
{
	
}

注意这里的全局变量a,C语言里是不会允许这种写法的,在C中全局变量只能用常量进行赋值。准确说C中的全局变量在编译期就需要被确定,链接器会把所有的全局变量都放进PE的data区域。说白了,这些全局变量都是直接写死在PE中的。

 

但是C++ 中确允许像上面那样动态赋值,原因就在于第二次调用 initterm 时,会调用foo函数为A进行初始化。对于上面的C++代码:

  • _initterm( __xi_a, __xi_z )  ------> 调用__initmbctable() ------> 实际上没做什么
  • _initterm( __xc_a, __xc_z ) ------> 调用foo()函数

利用_initterm( __xc_a, __xc_z ) 对C++ data 进行 initialization 有点儿复杂,这里不讨论。

 

 

另参考:

http://blog.donews.com/x140yu/archive/2005/05/26/399256.aspx

 

 

 

 

 

### 关于 `TypeError` 错误分析 当 Python 抛出类似于 `TypeError: __cinit__() got an unexpected keyword argument 'index'` 的错误时,通常表明某个类的初始化方法 (`__cinit__`) 不支持传递给它的特定关键字参数。这种问题可能源于以下几个方面: 1. **API 或库版本不匹配**:某些库更新后更改了其 API 接口定义,旧版代码中的调用方式不再适用。 2. **拼写错误或误解文档**:开发者可能无意间传入了一个未被目标函数接受的关键字参数。 3. **Cython 编译类的行为差异**:如果涉及 Cython 定义的扩展类型,则需注意这些类型的特殊行为及其限制条件。 针对此具体案例,“unexpected keyword argument 'index'”,可以推测该问题是由于尝试向某对象创建过程提供了名为 `'index'` 的额外选项而引发的。以下是几种常见场景以及对应的解决方案[^1]。 #### 场景一:Pandas DataFrame 创建过程中出现问题 假设正在利用 Pandas 库构建数据框实例,并且错误发生在如下语句附近: ```python df = pd.DataFrame(data, index=index_param) ``` 此时应仔细核对所使用的 pandas 版本号与官方手册说明是否一致;另外确认变量名无歧义冲突情况存在。例如,在较新版本中直接指定列索引的方式有所调整,或许需要改写成这样形式来规避潜在风险: ```python import pandas as pd data = {'A': [1, 2], 'B': [3, 4]} custom_index = ['row1', 'row2'] df = pd.DataFrame(data=data, columns=list(data.keys())) df.index = custom_index print(df) ``` #### 场景二:自定义 Cython 类型实例化失败 如果是基于 Cython 开发的应用程序遇到了此类异常,则可能是原生 C 函数签名未能正确映射至高层级接口所致。建议重新编译源码并确保所有依赖项均已同步升级到兼容状态之下。同时还可以通过调试模式打印更多上下文信息辅助定位根本原因所在。 #### 调试技巧提示 为了更高效地诊断实际运行环境下的状况,可采取以下措施之一或者组合运用它们获取更多信息以便进一步处理: - 启动交互式解释器逐行执行可疑部分直至触发崩溃点; - 添加日志记录功能捕获即时输入输出值变化轨迹; - 借助单元测试框架验证单个组件独立运作正常与否。 ```python try: obj = MyClass(param=value) except TypeError as e: print(f"Caught exception during initialization: {e}") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值