一个cdecl 程序的混乱版本 150行的代码用15行解决

本文深入解析了一段1988年的C语言程序,该程序设计复杂且混乱,通过预处理和替换表达式,揭示了其基本功能。文章详细解释了程序中的标识符处理、类型说明符、运算符优先级等关键概念,同时探讨了程序在ANSI C标准出台前的局限性。

 

一个cdecl 程序的混乱版本 
#include <stdio.h>
#include <ctype.h>

#define w printf
#define p while
#define t(s) (W=T(s))

char*X,*B,*L,I[99];
M,W,V;
D(){
	W==9 ? (w("'%.*s' is",V,X),t(0)) : W==40 ?
(t(0),D(),t(41)) : W==42 ? (t(0),D(),w("ptr to ")) : 0;
	p(w == 40 ? (t(0),w("func returning "),t(41)) : W==91 ? (t(0)==32 ? (w("array[0..%d]of ",
												   atoi(X)-1),t(0)) : w("array of "),t(93)) : 0);
}
main(){
	p( w("input:"),B=gets(I) )
		if(t(0)==9)
			L=X,M=V,t(0),D(),w("%.*s.\n\n",M,L);
}

T(s)
{
	if(!s || s==w )
	{
		p(*B==9 || *B==32)B++;
		X=B;
		V=0;
		if( W = isalpha(*B) ? 9 : isdigit(*B) ? 32 : *B++ )
		if( W < 33 )
			p(isalnum(*B))B++,V++;}
	return W;
}
预处理并用 if 语句替换 "?:" 表达式之后程序还是蛮简单的
#i nclude <stdio.h>
#i nclude <ctype.h>
char *X,*B,*L,I[99];
int M,W,V;
int T(int s);
void D()
{
 if(W==9)
 {
  /* 在此遇到的一定是 identifier 处理之并读取下一个 token */
  printf("'%.*s' is",V,X),(W=T(0));
 }
 else if(W==40)
 {
  /* 使用()改变声明优先关系的情况,比如 "int (*next)[23]" */
  (W=T(0)),D(),(W=T(41));
 }
 else if(W==42)
 {
    /* 处理 '*' 后面的所有信息 之后打印 "ptr to " */
    ((W=T(0)),D(),printf("ptr to  "));
 }
 /* 处理函数("()") 和数组("[]") 的情况并读取下一个 token */
 while(W==40?((W=T(0)),printf("func returning "),(W=T(41)))
            :W==91?((W=T(0))==32?(printf("array[0..%d]of ",atoi(X)-1),(W=T(0)))
                                :printf("array of "),(W=T(93)))
                  :0);
}
void main()
{
 while(printf("input:"),B=gets(I))
 /* 取得输入 */
 {
     if((W=T(0))==9)
     {
        /* type-specifier,qualifier是最后在这里输出的 */
         L=X,M=V,(W=T(0)),D(),printf("%.*s.\n\n",M,L);
     }
 }
}
/* lex 过程,s = 0,直接读取下一个token;
 *          否则检查当前token是否是期望的 s 并读取下一个token       
 *          *********************************************************
 *          如果是 identifier 和 type-specifier 则返回 9;
 *          如果是 number     则返回 32;
 *          如果是其他的特殊字符返回 ascii 值
 */
int T(int s)
{
 if(!s||s==W)
 /* 检查当前的token(W)是否是期望的那个(s) 这一步起 match 的作用 */
 {
  while(*B==9||*B==32)
  /* 跳过空白符 */
  {
    B++;
  }
  X=B;
  V=0;
  /* 处理识别 identifier 和 number 的情况
   * 注意 type-specifier 也被作为 identifier 处理
   */
  if(W=isalpha(*B)?9:isdigit(*B)?32:*B++)
   if(W<33)
       while(isalnum(*B))
           B++,V++;
  }
  return W;
}
/************************************************
 * B 存储的是当前分析过程处理到的输入串中的位置  如 f;
 * L 存储之前处理的输入串中的位置,用于输出       如 float f;
 * X 存储当前处理的token的开始位置               如 float f;
 * L 输入缓冲区
 * W 存储的是当前读到的 token 的值               如 9
 * M,V 存储当前token长度用于输出控制
 ************************************************
 */
note:程序是1988年的 c 混乱大赛的获胜者,,在读的过程中我开始总是想const怎么办?函数形参序列怎么办,后来才意识到当时还没有ANSI C标准,只支持K&R C形式的声明(而且没有支持存储限定符),被被郁闷了!
不过想想这么短的程序,怎么能要求那么高呢?:-)
感觉比那个打印圣诞十二天的程序在思路上以及程序本身来说都简单

                                               					----郁闷的饿猫 


 

<think>首先,用户的问题是:他想解决OpenCV相关的LNK2019链接错误,涉及"cv::String::allocate"外部符号无法解析的问题。具体错误是:当链接cv::String构造函数时,出现"unresolved external symbol 'cv::String::allocate'"。 我需要理解这个错误。LNK2019是Visual Studio中的链接错误,表示在编译时找不到外部符号的定义。这里,符号是"cv::String::allocate",这似乎是OpenCV库中cv::String类的一个成员函数。 从用户提供的引用中,我有一些线索: - 引用[1]和[3]讨论了静态成员变量导致的LNK2001错误,这类似于LNK2019,但LNK2019通常涉及函数。 - 引用[2]提到了LNK2019错误,具体是"cv::imshow"无法解析,这与用户的问题类似,但用户是cv::String::allocate。 用户的具体错误是:"unresolved external symbol 'cv::String::allocate' when linking cv::String constructor"。这暗示在链接时,编译器找不到cv::String::allocate函数的实现。 常见原因: 1. **库未正确链接**:可能OpenCV库没有正确添加到项目中,或者链接的库版本不匹配。 2. **头文件和库不一致**:使用的头文件版本与链接的库版本不同。 3. **构建配置问题**:比如Debug和Release模式不匹配。引用[2]中提到在Debug模式下配置,但其他地方需要Release。 4. **静态成员初始化问题**:但用户的问题是关于函数allocate,而不是变量。 在OpenCV中,cv::String是OpenCV的字符串类,类似于std::string。allocate函数可能是其内部实现的一部分。 解决方案步骤: 1. **确保OpenCV安装正确**:首先确认OpenCV已正确安装,并且路径设置正确。 2. **配置项目属性**: - 在Visual Studio中,打开项目属性。 - 在VC++目录中,设置包含目录(Include Directories)到OpenCV的include文件夹。 - 设置库目录(Library Directories)到OpenCV的lib文件夹。 - 在链接器(Linker) > 输入(Input)中,添加额外的依赖项(Additional Dependencies),包括需要的OpenCV库文件,如opencv_worldxxx.lib或特定模块的lib。 3. **检查构建模式**:确保项目构建模式(Debug/Release)与链接的库匹配。例如,Debug模式应链接Debug版本的库(如opencv_worldxxxd.lib),Release模式链接Release版本(如opencv_worldxxx.lib)。 4. **检查OpenCV版本**:确保所有部分使用相同版本的OpenCV。 5. **检查代码**:确认代码中正确使用了cv::String,并且没有错误。 用户错误具体提到"cv::String::allocate",这可能是OpenCV内部函数。如果库没有正确链接,这个函数就不会被找到。 参考引用[2],它讨论了类似错误,并提到配置应为Debug或Release一致。 可能的解决方案: - 在链接器输入中添加正确的lib文件。 - 确保环境变量如PATH包含OpenCV的bin目录,这样运时能找到dll。 在回答中,我需要: - 使用中文回答。 - 结构清晰,逐步解决问题。 - 在末尾添加引用标识,如[^1],但用户提供的引用是示例,我需要基于这些引用,但用户的问题是关于OpenCV的,所以引用可能不直接相关。用户说“上述引用仅供参考”,所以我应该基于问题本身回答。 - 生成相关问题。 回答结构: 1. **理解问题**:解释LNK2019错误是什么。 2. **常见原因**:列出可能导致此错误的原因。 3. **解决方案步骤**:提供详细步骤来解决。 4. **验证**:建议如何测试。 5. **相关问题**:生成一些相关问题。 最后,添加引用标识。用户提供的引用[1],[2],[3]是关于其他错误的,但既然用户提到了,我可以引用它们作为类似问题,但主要基于OpenCV。 实际引用: - [^1] 是关于静态成员变量错误。 - [^2] 是关于cv::imshow的LNK2019。 - [^3] 是另一个静态成员错误。 对于用户的问题,我可以参考[^2],因为它也涉及OpenCV的LNK2019错误。 生成相关问题: 1. 如何在不同版本的OpenCV中避免链接错误? 2. OpenCV中cv::String与std::string的区别是什么? 3. 如何调试Visual Studio中的链接错误? 确保LaTeX格式正确:所有内数学表达式用$...$,独立公式用$$...$$。但在这个问题中,可能不需要数学表达式,除非涉及算法。 现在,草拟回答。</think>### 解决OpenCV LNK2019链接错误:unresolved external symbol 'cv::String::allocate' 您在Visual Studio中遇到的LNK2019链接错误(具体为`unresolved external symbol 'cv::String::allocate' when linking cv::String constructor`)是OpenCV项目常见的配置问题。该错误表示编译器在**链接阶段**找不到`cv::String::allocate`函数的实现(这是一个OpenCV内部用于字符串内存分配的函数),通常由库文件未正确链接或版本不匹配引起。下面我将逐步解释原因和解决方案,帮助您彻底修复问题。 --- ### 错误原因分析 LNK2019错误的核心是**符号解析失败**。在您的场景中: - `cv::String`是OpenCV的字符串类(封装了`std::string`),其构造函数依赖`allocate`函数管理内存。 - 当您的代码调用`cv::String`时,链接器需要从OpenCV库文件(`.lib`)中找到`allocate`的实现。 - 如果库文件未正确链接或版本不一致,就会触发此错误。 常见原因包括: 1. **库文件未链接或路径错误**:OpenCV的库文件(如`opencv_worldxxx.lib`)未添加到项目依赖项。 2. **Debug/Release模式不匹配**:项目构建模式(如Debug)与链接的库版本(如Release)冲突(参考引用[2]中提到的配置问题)[^2]。 3. **OpenCV版本不一致**:头文件(include)和库文件(lib)来自不同版本的OpenCV。 4. **环境变量未设置**:系统PATH未包含OpenCV的DLL路径,导致运时链接失败。 --- ### 解决方案步骤 按照以下步骤检查和修复问题。使用**Visual Studio**(以VS 2019为例)操作。 #### 步骤1: 确认OpenCV安装和路径 - **检查OpenCV安装**:确保OpenCV已正确下载并解压到本地目录(如`C:\opencv`)。验证以下子目录存在: - `include`:包含头文件(如`opencv2/core.hpp`)。 - `lib`:包含库文件(如`opencv_world455d.lib`用于Debug)。 - `bin`:包含运时DLL(如`opencv_world455d.dll`)。 - **记录版本号**:例如,OpenCV 4.5.5的库文件名包含`455`。确保所有文件版本一致。 #### 步骤2: 配置Visual Studio项目属性 打开项目属性(右键项目 > 属性),进以下设置: 1. **设置包含目录**: - 转到 **VC++目录 > 包含目录**。 - 添加OpenCV的`include`路径,例如:`C:\opencv\build\include`。 - 示例: ![](https://via.placeholder.com/400x50?text=Add+Include+Directory+Here) 2. **设置库目录**: - 转到 **VC++目录 > 库目录**。 - 添加OpenCV的`lib`路径,例如:`C:\opencv\build\x64\vc15\lib`(根据您的平台选择`x64`或`x86`)。 3. **添加库依赖项**: - 转到 **链接器 > 输入 > 附加依赖项**。 - 添加所需的OpenCV库文件名(根据构建模式选择): - **Debug模式**:使用带`d`后缀的库(如`opencv_world455d.lib`)。 - **Release模式**:使用不带后缀的库(如`opencv_world455.lib`)。 - 确保模式匹配:如果项目是Debug,必须链接Debug版库(参考引用[2]的教训)[^2]。 4. **检查运时库**: - 转到 **C/C++ > 代码生成 > 运时库**。 - Debug模式应设置为`/MDd`,Release模式为`/MD`。确保与OpenCV库的编译选项一致。 #### 步骤3: 配置系统环境变量 - 将OpenCV的`bin`目录添加到系统PATH: - 例如:`C:\opencv\build\x64\vc15\bin`。 - 操作:控制面板 > 系统 > 高级设置 > 环境变量 > 编辑PATH。 - **重启Visual Studio**:使更改生效。 #### 步骤4: 验证代码和构建 - **检查代码**:确保代码中正确使用`cv::String`,例如: ```cpp #include <opencv2/core.hpp> int main() { cv::String str = "Hello, OpenCV"; // 正确使用cv::String构造函数 return 0; } ``` - **清理并重新构建**: - 在VS中执 **生成 > 清理解决方案**。 - 然后 **生成 > 重新生成解决方案**。 - **测试运**:启动程序,确认错误是否消失。 #### 步骤5: 处理常见陷阱 - **静态链接问题**:如果使用静态库(而非DLL),需确保所有依赖项(如zlib)也正确链接(参考引用[1]和[3]中静态成员导致的类似错误)[^1][^3]。 - **版本冲突**:卸载其他OpenCV版本,避免路径混乱。 - **平台一致性**:项目平台(x64/x86)必须与OpenCV库匹配。 --- ### 验证成功 - 如果以上步骤正确执,LNK2019错误应被解决。如果问题仍存在: 1. 检查输出窗口中的详细链接信息,确认缺失的符号是否与`cv::String`相关。 2. 使用Dependency Walker(或VS的`dumpbin`工具)分析库文件: ``` dumpbin /SYMBOLS opencv_world455d.lib | find "cv::String::allocate" ``` 3. 确保无多版本OpenCV干扰。 --- ### 相关问题 1. **如何避免不同OpenCV版本导致的链接错误?** (涉及版本管理技巧和兼容性检查) 2. **OpenCV中`cv::String`与`std::string`的性能差异是什么?** (讨论内存管理、API设计及适用场景) 3. **如何调试Visual Studio中其他LNK2019/LNK2001错误?** (扩展分析静态成员、模板类等常见原因) [^1]: C++类静态成员变量导致报错error LNK2001: unresolved external symbol "private: static class。【来自上述参考文章《error LNK2001:未解析的外部符号“private:static class》】 [^2]: win10 配置OpenCV LNK2019 无法解析的外部符号 "void __cdecl cv::imshow。【来自上述参考文章,强调Debug/Release模式配置】 [^3]: C++类静态成员变量导致报错error LNK2001: unresolved external symbol "private: static class。【来自上述参考文章,说明静态成员初始化的陷阱】
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值