补遗篇之命名空间污染

2023-11-03

概念

    C标准规定,除非用static限定,否则全局变量与函数都作用于全局(见补遗篇static),也就是说一个模块中定义的函数与全局变量可在所有其他模块中被调用。这导致C的符号命名没有层次,不同模块间名字相互冲突的概率很高。学术的表述就是:C命名空间易被污染。

    比如,不同软件模块中定义了同名但不同实现的非static函数,由于它们都全局可见,编译器链接时无法正确区分和选择这些同名函数,这是C语言命名空间污染典型的问题。一些编译器在linking阶段报告重定义错误(redifined symbol error),这样还好,根据错误提示很容易找到问题。但有的编译器却在同名符号里随便选择一个,连接通过并得到最终可执行程序,对外可能只提示重定义警告。可如果链接通过,多少人会去注意警告呢?这实际导致了随机的结果,用了李逵结果正确,下次调了李鬼,杯具了。于是抓耳挠腮,为嘛呢?而且有时某些不相关改动会影响链接器的选择,这更容易把人引入歧途,鬼知道编译器什么时候看谁对眼啊!

实例描述

    在两个文件里定义同名函数,分别用gcc编成静态库a.ab.a,然后在test.c里调用:

/***a.c***/

    int libfun() {  return 2;  }

/***b.c***/

    int libfun() {  return 5;  }

/***test.c***/

    void main()

    {

      int c =libfun();

      printf(“c =%d\n”, c);

    }

    用gcc编译test.c,在makefile里同时包含ab两个库,如果把a.a放在前面,结果就是”c=2”,而b.a在前面,结果就变成”c=5”。当然这只是gcc的处理方法,其它编译器可能不一样。问题是对于模块开发者和集成者,这类问题事先似乎无法避免,事后也很难定位。

个人经历

    笔者就曾在这点上有过教训,至今记忆犹新。某次完成一个功能库后,发给分公司同事集成,结果同事反映结果时对时错:

笔者:我的模块没问题,我自己写的测试程序运行结果正确,肯定是你调用逻辑不对

同事:不可能,我的上层应用调用其他功能相同的第三方模块没问题,用你的模块就时好时坏

笔者:什么叫时好时坏,模块算法固定,之前正确就说明算法没问题,你改了什么导致出现问题?

同事开始耍赖:我哪记得,反正现在不正确

……吵了半天,实在没办法,我在库里接口函数里统统加上log,让同事重新编译运行抓log,不想过一会同事电话过来:就说是你的问题吧,你改什么了,这次结果对了

无比惊讶中:XX,什么乱七八糟,就是加了log输出而已

同事:不可能,你肯定是改了什么,再看看,这次结果是对的。

难道printf也能影响结果,这到底怎么回事呢?于是抓狂查找,一天下来,无果。第二天同事又来电,怯怯:好象不是log的原因,今天用带log库又出错了。

笔者火山爆发:我XXXX,昨天脑细胞死一半了,能不能别谎报军情,你又改什么了?

同事也很委屈:什么也没改呀,就重编了一下。

沟通半天,越说越糊涂,无奈,只好带上源码出差(两地两部门,代码权限有控制),问题依然时隐时现,无数次试验后,突然,

笔者:恩,我这个XXX函数这次怎么log没进来?外部调用前的log都有,怎么到里面跑飞了

同事:没进来,到哪去了?为啥之前的log有时能进来?

笔者一拍脑门:明白了,快,在整个项目里搜索这个XXX函数名

果真,在另一个其他同事写的功能模块里也有这个XXX函数名,但实现的功能有差别,回头仔细查看整个项目的编译连接信息,终于发现了一个XXX redifined warning。于是名字改掉,幽灵再也不出现了。

同事:真没想到,怎么会有这种问题,为什么编译器连接时不报错呢,一堆警告谁会注意!

笔者:还是你的问题,我事先怎么知道其他人也会用这个函数名呢,我自己测试好好的,集成多个模块的事当然要你把关。

同事:不能赖我呀,一开始结果是对的,后来莫名其妙出问题,谁知道往这方面想呢!再说,你们写函数干吗都用这种大路货名字,大家都用,很容易撞车呀。

最终责任认定无果。

    相信笔者不是唯一有类似体验的,这种问题时隐时现,而且容易错误联系,把不相干因素当作调查方向而浪费精力。不过“见怪不怪,其怪自败”,后来其他同事也碰到类似情况,建议他们到编译信息里查一下redefine关键字,果然很快解决问题。得意地享受完大家的崇拜眼光,不禁感慨:“还是经验最有价值!”。那能不能事先避免命名冲突问题,防止编译器乱点鸳鸯谱呢:)

解决方法

    命名冲突是C固有问题,自身范围内没有一劳永逸的解决办法(否则也不会有受争议的C++了),但一些局部防范的手段还是有的,包括:

    1)  static限制函数或变量作用域,一方面static可以限制自身作用域,防止被其他模块调用;同时C规定局部优于全局,本地文件中static量优于其他模块里同名的全局量被调用,或者说,两个同名符号,只要其中一个用static修饰,就可互不影响。

    2)  把模块名作为模块里所有非static函数的前缀,这可能很难完全做到,但起码一些冲突可能性很大的基础函数可以加上,比如add, sub, mux等名字,想不冲突都难啊。

    3)  从全局考虑,把一些公共函数提取出来,形成公共底层库,而不是在每个模块中都重复实现。比如某些常用的基础算法,字符操作等。

    可对照补遗staticC潜规则函数命名方式。

对应的interpositioning问题

    重名如果发生在用户自定义函数与C标准库之间,就是所谓interpositioningC规定,一旦实现出现与标准库同名的用户自定义函数,用户函数将取代该库函数的所有行为,注意:不仅取代用户代码里所有库函数调用,所有系统调用内部对原库函数的调用也会被代替。也就是说当编译器注意到库函数与用户定义函数同名,它认定用户函数优先级高于库函数。这是C的逻辑:程序员有最高权限,所做所为都是有原因、有意识的,错的也是对的。

    但谁也不可能记住标准库里所有函数名,有时无意间就覆盖了库函数。这种自定义函数误覆盖C标准库函数,其实是另一种形式的命名空间污染。尽管C本意是给程序员更多发挥空间,可多数人或许并不希望有这个空间,起码不应通过interpositioning这种隐含方式。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

补遗篇之命名空间污染 的相关文章

  • Java中的作用域

    目录 Java作用域 Java中变量类型主要有3种 成员变量 静态变量和局部变量 成员变量或方法也有4种作用域 静态修饰符的特点 静态使用的注意事项 静态的优缺点 当成员变量被静态修饰后 和非静态成员变量的区别 方法作用域 块作用域 基本使
  • 单词搜索--回溯算法

    LeetCode 单词搜索 给定一个二维网格 board 和一个字典中的单词列表 words 找出所有同时在二维网格和字典中出现的单词 单词必须按照字母顺序 通过相邻的单元格内的字母构成 其中 相邻 单元格是那些水平相邻或垂直相邻的单元格
  • UIView的setNeedsLayout, layoutIfNeeded 和 layoutSubviews 方法之间的关系解释

    layoutSubviews总结 ios layout机制相关方法 CGSize sizeThatFits CGSize size void sizeToFit void layoutSubviews void layoutIfNeeded
  • 《软件工程教程》(第2版) 主编:吴迪 马宏茹 丁万宁 第十二章课后习题参考答案

    第十二章 软件项目管理 课后习题参考答案 一 简答题 1 项目管理的定义是什么 简述软件项目管理的过程 答 项目管理的定义 项目管理是以项目为对象 通过使用知识 技能 工具和方法来组织 计划 实施并监控项目 使之满足项目目标需求的过程 一般
  • ipad扩展为Windows的第二屏幕【免费无线版】

    本文参考 Ipad扩展为Windows的第二屏幕 有时候想用ipad作为Windows电脑的第二屏幕 那么我们可以通过spacedesk软件进行无线扩展 1 在电脑和ipad上分别下载spacedesk软件 电脑端 ipad 打开ipad

随机推荐

  • npm ERR! tar.unpack untar error

    今天在使用nam install命令安装第三方node模块时 老是出现这样的问题 npm ERR tar unpack untar error tmp npm 5884 e9fda1ac registry npmjs org npm npm
  • cocos creater 鸿蒙 音频卡死 播放失败 不回调

    cocos creater音频播放失败 不回调 卡死 鸿蒙 问题背景 开发过程中 未发现问题 线上 部分鸿蒙用户反馈 页面卡死没反应 页面不能继续下一步 so 问题有可能是 音频没播出来 或者回调没回来 或者 获取音频的时长错误 测试现场1
  • Objects365数据简介及数据转换为XML格式

    Objects365数据简介及数据转换为XML格式 注 Obj365和COCO数据集转换为xml格式以及转为yolo的txt格式 xml数据统计处理更改见GitHub https github com lidc1004 Object det
  • Jmter自动化

    一 接口测试流程 1 拿到api接口文档 熟悉接口业务 2 编写测试用例 正例 正常参数 是否接口正常 反例 鉴权异常情况 参数异常 兼容性 黑名单 调用次数异常 3 使用接口测试用具 Jmeter 4 无人值守 持续集成 输出报告 二 J
  • Cpp学习——模板

    模板 目录 模板 1 介绍 2 函数模板的使用 3 函数模板的强制转换or显式调用 四 模板的分类 1 介绍 在Cpp3 0中 祖师爷便引入了模板的概念 这是一个重大的变革 为后来的Cpp标准化打下了铺垫 也正是因为有了模板 Cpp才能有S
  • 创建阿里云产品和添加设备

    阿里云产品的创建 概述 阿里云网址 https www aliyun com 在线加解密网址 http encode chahuo com MQTT fx下载地址 http mqttfx jensd de 产品创建 一键复制并且保存完毕之后
  • Python爬虫--异常处理

    1 URLError出现的原因 1 连不上服务器 2 远程url不存在 3 无网络 4 触发HTTPError 2 处理异常实例 import urllib request import urllib error try urllib re
  • MongoDB复杂Json数据的增删改查

    本文讲解了MongoDB在java语言下的复杂用法 并附带案例 欢迎各位MongoDB使用者点赞收藏 提出宝贵的意见 TImetableDO数据结构 orgId test 0c8bd2befcae level 33 studyStageCo
  • IPsec技术介绍(转)

    目 录 IPsec IPsec简介 IPsec的协议实现 IPsec基本概念 加密卡 IPsec虚拟隧道接口 使用IPsec保护IPv6路由协议 IKE IKE简介 IKE的安全机制 IKE的交换过程 IKE在IPsec中的作用 IPsec
  • java定时任务实现的几种方式

    摘要 在开发测试工具的应用后台 经常听到同事说要做个定时任务把做日志处理 或者数据清理 包括做些复杂的业务计算逻辑 在选择定时任务的时候 怎么能够快速实现 并且选择一种更适合自己的方式呢 我这里把定时任务的实现收集整理了一些方法 希望可以帮
  • python中sorted和sort的key

    目录 sort与sorted区别 1 调用方式 2 返回值 3 操作对象是否变化 什么对象可以排序 sort sorted 排序的key 函数 方法 自己造 有趣的排序题 多级排序 sort与sorted区别 1 调用方式 sort是方法
  • chisel可选值/模式匹配实例

    一 scala可选值语法 可选值就是类型为Option T 的一个值 其中 Option是标准库里的一个密封抽象类 T可以是任意的类型 例如标准类型或自定义的类 并且T是协变的 简单来说 就是如果类型T是类型U的超类 那么Option T
  • Keil uVision添加新文件夹

    Keil uVision5是款比较优秀的IDE编译器 最近接手一个项目 代码已经形成一个版本 在此基础上新增一些新功能 由于不破坏原先结构的目的 因此新增的部分采用独立方式 存放在独立的文件夹 一开始死活编译不通过 提示也怪怪的 后来经过折
  • js中从blob提取二进制

    文章结构 一 所遇到的问题 二 解决方法 一 服务器端通过websocket向浏览器端传输图片 二进制 需要根据不同的图片把图片显示在不同的位置 可行的一个方法是先把图片转化成二进制数组 再把二进制数组和一个字节的图片标识拼接在一块传输给浏
  • VSCode:设置默认换行符

    windows默认是回车换行符 即 r n linux默认是换行符 即 n 同时在两种操作系统里编辑 经常会造成文件里含有 r 导致在linux会显示 M 也就是回车符 可以对vscode的默认换行符进行设置 打开VSCode菜单文件 gt
  • Linux0.11内核--系统中断处理程序int 0x80实现原理

    原文地址 http blog csdn net geekcome article details 6398414 系统调用是一个软中断 中断号是0x80 它是上层应用程序与Linux系统内核进行交互通信的唯一接口 这个中断的设置在kerne
  • C语言参悟-数据类型

    C语言的数据类型 一 概述 二 基础数据类型 1 整数 1 计算 2 索引 2 浮点数 3 字符 4 字符串 5 指针 三 特殊数据类型 1 枚举 2 共用体 2 struct结构体 四 数据类型修饰符 1 const 2 unsigned
  • 数据仓库分层设计

    文章参考http ierda blog 163 com blog static 77469587201326105956470 https blog csdn net kwu ganymede article details 5136742
  • RTSP和SDP协议学习

    RTSP和SDP协议学习 版本说明 版本 作者 日期 备注 0 1 loon 2019 2 14 初稿 目录 文章目录 RTSP和SDP协议学习 版本说明 目录 第一部分 RTSP协议 一 RTSP协议概述 二 RTSP协议与HTTP协议区
  • 补遗篇之命名空间污染

    概念 C标准规定 除非用static限定 否则全局变量与函数都作用于全局 见补遗篇static 也就是说一个模块中定义的函数与全局变量可在所有其他模块中被调用 这导致C的符号命名没有层次 不同模块间名字相互冲突的概率很高 学术的表述就是 C