科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件C++语言常见问题解答(4)

C++语言常见问题解答(4)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

C++语言常见问题解答.

来源:中国软件网 2008年3月31日

关键字: 解答 问题 C++ C Linux

  • 评论
  • 分享微博
  • 分享邮件

第17节:和 C 连结/和 C 的关系
 

Q105:怎样从 C++ 中呼叫 C 的函数 "f(int,char,float)"?

告诉 C++ 编译器说:它是个 C 的函数:
        extern "C" void f(int,char,float);

确定你有 include 进来完整的函数原型 (function prototype)。一堆 C 的函数可
以用大括号框起来,如下:

        extern "C" {
          void* malloc(size_t);
          char* strcpy(char* dest, const char* src);
          int   printf(const char* fmt, ...);
        }

Q106:怎样才能建一个 C++ 函数 "f(int,char,float)",又能被 C 呼叫?

想让 C++ 编译器知道 "f(int,char,float)" 会被 C 编译器用到的话,就要用到前
一则 FAQ 已详述的 "extern C" 语法。接著在 C++ 模组内定义该函数:

        void f(int x, char y, float z)
        {
          //...
        }

"extern C" 一行会告诉编译器:送到 linker 的外部资讯要采用 C 的呼叫惯例及签
名编码法(譬如,前置一个底线)。既然 C 没有多载名称的能力,你就不能让 C 程
式能同时呼叫得到多载的函数群。

警告以及实作相关事项:
 * 你的 "main()" 应该用 C++ 编译之(为了静态物件的初始化)。
 * 你的 C++ 编译器应该能设定连结的程序(为某些特殊的程式库)。
 * 你的 C 和 C++ 编译器可能要是同一个牌子的,而且是相容的版本(亦即:有相
   同的呼叫惯例等等)。

 

Q107:为什麽 linker 有这种错误讯息:C/C++ 函数被 C/C++ 函数呼叫到?

看前两则 FAQs 关於 extern "C" 的使用。

Q108:该怎麽把 C++ 类别的物件传给/传自 C 的函数?

例子:

        /****** C/C++ header file: Fred.h ******/
        #ifdef __cplusplus    /*"__cplusplus" is #defined if/only-if
                                 compiler is C++*/
          extern "C" {
        #endif

        #ifdef __STDC__
          extern void c_fn(struct Fred*);       /* ANSI-C prototypes */
          extern struct Fred* cplusplus_callback_fn(struct Fred*);
        #else
          extern void c_fn();                   /* K&R style */
          extern struct Fred* cplusplus_callback_fn();
        #endif

        #ifdef __cplusplus
          }
        #endif

        #ifdef __cplusplus
          class Fred {
          public:
            Fred();
            void wilma(int);
          private:
            int a_;
          };
        #endif

"Fred.C" 是个 C++ 模组:

        #include "Fred.h"
        Fred::Fred() : a_(0) { }
        void Fred::wilma(int a) : a_(a) { }

        Fred* cplusplus_callback_fn(Fred* fred)
        {
          fred->wilma(123);
          return fred;
        }

"main.C" 是个 C++ 模组:

        #include "Fred.h"

        int main()
        {
          Fred fred;
          c_fn(&fred);
          return 0;
        }

"c-fn.c" 是个 C 模组:

        #include "Fred.h"
        void c_fn(struct Fred* fred)
        {
          cplusplus_callback_fn(fred);
        }

把指向 C++ 物件的指标传到/传自 C 的函数,如果传出与收回的指标不是“完全相
同”的话,就会失败。譬如,不要传出一个基底类别的指标却收回一个衍生类别的指
标,因为 C 编译器不懂该怎麽对多重及虚拟继承的指标做转型。

 

Q109:C 的函数能不能存取 C++ 类别的物件资料?

有时可以。

(请先读一读前一则关於和 C 函数间传递 C++ 物件的 FAQ。)

你可以安全地从 C 函数中存取 C++ 物件的资料,只要 C++ 的物件类别:
 * 没有虚拟函数(包含继承下来的虚拟函数).
 * 所有资料都在同一个存取等级中 (private/protected/public).
 * 完全被包含的子物件中也都没有虚拟函数.

如果 C++ 类别有任何基底类别(或是任何被完全包含的子物件中有基底类别)的话
,技术上来说,存取该资料没有可携性的,因为语言没规定在继承之下的类别配置是
什麽样子。不过经验上,所有 C++ 编译器的做法都一样:基底类别物件先出现(在
多重继承之下,则由左到右排列之),子物件次之。

还有,如果类别(或是任何基底类别)含有任何虚拟函数,你时常可以(但不是一直
都可以)假设有一个 "void*" 出现在物件第一个虚拟函数之所在,或是在该物件的
第一个 word 那里。同样的,语言对它也没规定到,但这似乎是「大家」都采取的做
法。

如果该类别有任何虚拟基底类别,情况会更复杂而且更没有可携性。常见的做法是:
让物件最後才包含基底类别之物件 (V)(不管 "V" 在继承阶层中在哪儿出现),物
件的其他部份则以正常的次序出现。每个有 V 这个虚拟基底类别的衍生类别,实际
上都有个“指标”指向最後一个物件的 V 的部份。

 

Q110:为什麽我总觉得 C++ 让我「离机器更远了」,不像 C 那样?

因为事实上正是如此。

做为一个 OOPL,C++ 让你以该问题的领域来思考,让你以问题领域的语言来设计程
式,而非以解题的领域来著手。

一个 C 最强的地方是:它没有「隐藏的机制」:你看到的就是你得到的,你可以一
边阅读 C 的程式,一边「看到」每个系统时脉。C++ 则不然; C 的老手(像从前的
我们)对这种特性常会有矛盾的心理(或是说「敌视」),但是很快的他们会发现:
C++ 提供了抽象化的层次及经济的表现能力,大大降低维护成本,又不会损及执行效
率。

很自然的,用任何语言都会写出坏程式;C++ 并不会确保任何高品质、可重用性、抽
象化,或是任何「正字标记」的品质因子。C++ 不会让差劲的程式者写不出差劲的程
式;她只是协助明智的发展者做出高人一等的软体。


===================================
■□ 第18节:指向成员函数的指标
===================================

Q111:「指向成员函数的指标」和「指到函数的指标」的型态有差别吗?

是的。

考虑底下的函数:

        int f(char a, float b);

如果它是普通的函数,它的型态是:            int (*)      (char,float);
如果它是 Fred 类别的运作行为,它的型态是:  int (Fred::*)(char,float);

 

Q112:怎样把指向成员函数的指标传给 signal handler、X event callback 等等?

【译注】这是和 UNIX、X Window System 相关的问题,但其他系统亦可推而广之。

不要这样做。

因为若无物件去启动它,成员函数是无意义的,你不能直接使用它(如果 X 视窗系
统是用 C++ 写的话,或许就可以直接传物件的参考值了,而不光是传个指向函数的
指标;自然地,物件会包含所有要用到的函数,甚至更多)。

若想修改现有的软体,可拿最顶层的(非成员的)函数当作一层包装 (wrapper),透
过其他技巧(或许是放在全域变数中),把该物件包起来。这个最顶层的函数,会透
过适当的成员函数去使用该全域变数。

譬如,你想在中断处理中呼叫 Fred::memfn() 的话:

        class Fred {
        public:
          void memfn();
          static void staticmemfn();    // 用个 static 成员函数就行了
          //...
        };

        file://wrapper 函数会记得哪个物件该去启动全域物件的成员函数:
        Fred* object_which_will_handle_signal;
        void Fred_memfn_wrapper() { object_which_will_handle_signal->memfn(); }

        main()
        {
          /* signal(SIGINT, Fred::memfn); */   file://不能这样做
          signal(SIGINT, Fred_memfn_wrapper);  file://Ok
          signal(SIGINT, Fred::staticmemfn);   file://Also Ok
        }

注意:静态成员函数不需要真正的物件才能启动,所以指向静态成员函数的指标,和
普通的指向函数的指标,具有相容的型态(详见 ARM ["Annotated Reference
Manual"] p.25, 158)。

 

Q113:当我想以成员函数做为中断服务常式 (ISR) 时,为什麽编译器产生(型态不
      符)的错误?

这是前两个问题的特例,所以请先看看前两则解答。

非静态的成员函数,都有一个隐藏的参数,对应到 ''this'' 指标,该 ''this'' 指标会
指向该物件的案例资料 (instance data),可是系统中断的硬体/韧体并未提供这个
''this'' 参数。你得用「正常的」函数(不是类别的成员)或是静态成员函数来做为
中断服务常式才行。

一个可行的解法是:用一个静态成员做为中断服务常式,让它能自己到某处去找案例
/成员的配对,以供中断呼叫之用。这麽一来,当中断产生时,正常的 method 就会
被启动,不过以技术观点来看,你得先呼叫一个中介函数。

 

Q114:为什麽我取不出 C++ 函数的位址?

这可由前一则 FAQ 推论过来。

详细的解答:在 C++ 里,成员函数有一个隐含的参数,指向该物件本身(成员函数
内的 "this" 指标)。正常的 C 函数与成员函数的呼叫惯例可视为不同,所以它们
指标的型态(指向成员函数 vs 指向函数)既不同也不相容。C++ 引进一个新的指标
型态:指向成员的指标,要提供一个物件才能启动之(见 ARM ["Annotated
Reference Manual"] 5.5)。

注意:不要去把指向成员函数的指标强制转型成指向函数的指标;这样做的结果是未
定义的,且下场可能会很惨。譬如,指向成员函数的指标,“不必然”会包含某正常
函数的机器位址(看 ARM, 8.1.2c, p.158)。如前例所提,如果你有个指向正常 C
函数的指标的话,请用上层的(非成员的)函数,或是用 "static" 成员函数(类别
成员函数)。

 

Q115:怎样宣告指向成员函数的指标阵列?

用 "typedef" 好让你的脑筋保持清醒。

        class Fred {
        public:
          int f(char x, float y);
          int g(char x, float y);
          int h(char x, float y);
          int i(char x, float y);
          //...
        };

        typedef  int (Fred::*FredPtr)(char x, float y);

这是指向成员函数的指标阵列:Here''s the array of pointers to member functions:

        FredPtr a[4] = { &Fred::f, &Fred::g, &Fred::h, &Fred::i };

呼叫物件 "fred" 的某一个成员函数:

        void userCode(Fred& fred, int methodNum, char x, float y)
        {
          file://假设 "methodNum" 在 [0,3] 区间内
          (fred.*a[methodNum])(x, y);
        }

你可以用 #define 让这个呼叫清楚些:

        #define  callMethod(object,ptrToMethod)   ((object).*(ptrToMethod))
        callMethod(fred, a[methodNum]) (x, y);


====================================
■□ 第19节:容器类别与 template
====================================

Q116:怎样自一个连结串列/杂凑表等等里面,插入/存取/改变元素?

我将以最简单的「插入连结串列」为例。想把元素插入串列的头尾很容易,但只限
於这些功能的话,会使程式库过於低能(太低能的程式库比没有更糟)。

完整的解答会让 C++ 新手消化不良,所以我只提几个项目。第一个是最简单的,第
二和第三是比较好的。

[1] 替 "List" 加入一个「现在位置」的性质,加入像是 advance()、backup()、
    atEnd()、atBegin()、getCurrElem()、setCurrElem(Elem)、insertElem(Elem)
    、removeElem() 等等的运作行为。

    即使在这个小例子里已经够用了,但「只有一个」现在位置的记号的话,想存取
    串列中两个以上位置的元素就不太容易(譬如:「对所有 x,y 序对,做底下的
    事情……」)。

[2] 把上述的 List 运作行为拿掉,移到独立的类别 "ListPosition" 中。

    ListPosition 的作用是:代表 List 里「现在的位置」,这样就允许许多位置
    并存於同一个串列中。ListPosition 是 List 的夥伴,所以 List 的内部可对
    外界隐藏起来(否则 List 的内部就会被它的公共运作行为所公开)。注意:
    ListPosition 可以把运算子多载起来,像是 advance()、backup(),因为运算
    子多载只是正常运作行为的语法糖衣而已。

[3] 把整个位置处理(iteration)当成是一个基元事件(atomic event),建一个
    class template 去涵盖该事件。

    它不会在内部回圈中使用公共存取运作行为(它有可能是虚拟函数),所以效率
    能增进。不幸的,你的应用软体会多出些额外的二元码,因为 template 是以空
    间换取时间的。欲知详情,请见 [Koenig, "Templates as interfaces,"
    JOOP, 4, 5 (Sept 91)], 以及 [Stroustrup, "The C++ Programming Language
    Second Edition," under "Comparator"].

 

Q117:「样版」(template)的用意是什麽?

Template 本意是个压饼模子,它把饼乾都压成差不多一个样子(虽然饼乾的原料不
尽相同,但它们都有相同的基本外形)。同理,class template 是个样版模子,用
来描述如何将一系列的物件类别弄成同一个基本型式;而 function template 则是
用以描述一系列看起来差不多的函数。

Class template 常用於制造型别安全的容器(即使这仅止於「如何使用它」而已)。

 

Q118:"function template" 的语法/语意是什麽?

考虑底下这个交换两个整数引数的函数:

        void swap(int& x, int& y)
        {
          int tmp = x;
          x = y;
          y = tmp;
        }

假如我们想交换 float、long、String、Set 和 FileSystems,我们还得写那些
大致看起来都一样、只有型态不同的程式码,有够烦人。这种不花脑筋的重复性工作
,正是电脑的专长,於是我们想出了 function template:

        template
        void swap(T& x, T& y)
        {
          T tmp = x;
          x = y;
          y = tmp;
        }

每次我们以一组型别来使用 "swap()",编译器会找到上面这定义,并造出另一个
"template function" ,来当作它的「案例」(instantiation)。譬如:

        main()
        {
          int    i,j;  /*...*/  swap(i,j);  // 案例化 "int"    的 swap
          float  a,b;  /*...*/  swap(a,b);  // 案例化 "float"  的 swap
          char   c,d;  /*...*/  swap(c,d);  // 案例化 "char"   的 swap
          String s,t;  /*...*/  swap(s,t);  // 案例化 "String" 的 swap
        }

(注意:"template function" 是 "function template" 实体化之後的案例。)

 

Q119:"class template" 的语法/语意是什麽?

考虑像是个整数阵列的容器类别:

        // 这会放在像是 "Array.h" 的标头档中
        class Array {
        public:
          Array(int len=10)                  : len_(len), data_(new int[len]){}
         ~Array()                            { delete [] data_; }
          int len() const                    { return len_;     }
          const int& operator[](int i) const { data_[check(i)]; }
                int& operator[](int i)       { data_[check(i)]; }
          Array(const Array&);
          Array& operator= (const Array&);
        private:
          int  len_;
          int* data_;
          int  check(int i) const
            { if (i < 0  i >= len_) throw BoundsViol("Array", i, len_);
              return i; }
        };

如同前述的 "swap()" ,一再为 float、char、String、Array-of-String 等等来重
复设计 Array 类别,是很烦人的。

        // 这会放在像是 "Array.h" 的标头档中
        template
        class Array {
        public:
          Array(int len=10)                : len_(len), data_(new T[len]) { }
         ~Array()                          { delete [] data_; }
          int len() const                  { return len_;     }
          const T& operator[](int i) const { data_[check(i)]; }
                T& operator[](int i)       { data_[check(i)]; }
          Array(const Array&);
          Array& operator= (const Array&);
        private:
          int len_;
          T*  data_;
          int check(int i) const
            { if (i < 0  i >= len_) throw BoundsViol("Array", i, len_);
              return i; }
        };

不像 template function 那样,template classes(案例化的 class template)必
须将那些用来案例化的参数型态明示出来:

        main()
        {
          Array           ai;
          Array         af;
          Array         ac;
          Array        as;
          Array< Array >  aai;
        }              // ^^^-- 注意这空格;不要用 "Array>"
                       //       (编译器会把 ">>" 看成单一的元素)

 

Q120:什麽是「参数化型别」(parameterized type)?

另一种 "class template" 的说法。

「参数化型别」是一种型别,它被另一个型别或数值所参数化(parameterized)了。
像 List 是一个型别 ("List") ,它被另一个型别 ("int") 所参数化。

 

Q121:「泛型」(genericity)是什麽?

另一种 "class template" 的说法。

不要和「一般化」(generality,指不要过於特定的解题)弄混了,「泛型」指的是
class template。


=======================
■□ 第20节:程式库
=======================

Q122:怎样拿到 "STL"?

"STL" 代表 "Standard Templates Library",标准模版程式库。取得法:

STL HP official site:   ftp://butler.hpl.hp.com/stl
STL code alternate:     ftp://ftp.cs.rpi.edu/stl
STL code + examples:    http://www.cs.rpi.edu/~musser/stl.html

STL hacks for GCC-2.6.3 已经在 GNU libg++ 2.6.2.1 或更新版本里了(可能较早
的版本也有)。多谢 Mike Lindner。

 

Q123:怎样 ftp 到 "Numerical Recipes" 附的程式?

它是用卖的,把它放到网路上散布是违法的。不过它只需 $30 美元而已。

 

Q124:为什麽我的执行档会这麽大?

很多人对这麽大的执行档感到惊讶,特别是当原始码只有一点点而已。例如一个简单
的 "hello world" 程式居然会产生大家都想不到的大小(40+K bytes)。

一个原因是:有些 C++ 执行期程式库被连结进去了。有多少被连结进去,就要看看
你用到多少,以及编译器把程式库切割成多少块而定。例如,iostream 很大,包含
一大堆类别及虚拟函数,即使你只用到一点点,因为各元件之间的交互参考依存关系
,可能会把整个 iostream 程式码都塞进来了。(【译注】如果 linker 做得好的话
,应该能把完全用不到的元件 object code 砍掉,不随之塞入你的执行档中。)

不要用静态的,改用动态连结的程式库版本,就可以使你的程式变小。

欲知详情,请看看你的编译器手册,或是寻求厂商的技术支援。


===============================
■□ 第21节:特定系统的细节
===============================

Q125:GNU C++ (g++) 把小程式造出大大的执行档,为什麽?

libg++(g++ 用到的程式库)可能在编译时带有除错的资讯(-g)。有些机器上,不
带除错资讯地重新编译它,会省下很大的磁碟空间(~1 MB;缺点是:不能追踪到
libg++ 的呼叫)。仅仅 "strip" 掉执行档,比不上先用 -g 重新编译,再 "strip"
掉 a.out 档来得有效。

用 "size a.out" 来看看执行码的程式与资料区段到底占了多大空间,而不要用
"ls -s a.out" 这种包括了符号表格(symbol table)的方式。

 

Q126:有 YACC 的 C++ 文法吗?

Jim Roskind 是 C++ 的 YACC 文法作者,它大体上和部份 USL cfront 2.0 所实作
出来的语言相容(没有 template、例外、执行期型态识别功能)。这份文法有些地
方和 C++有细小而微妙的差别。

它可用 anonymous ftp 到下列地方取得:
 * ics.uci.edu (128.195.1.1) in "gnu/c++grammar2.0.tar.Z".
 * mach1.npac.syr.edu (128.230.7.14) in "pub/C++/c++grammar2.0.tar.Z".

 

Q127:什麽是 C++ 1.2?  2.0?  2.1?  3.0?

这些不是“语言”的版本,而是 cfront 这个由 AT&T 做出来的、最早的 C++转译程
式的版本编号。以这编号来“代表”C++ 语言的演进,已经是公认的惯例了。

“非常”粗略地讲,主要的特徵有:
 * 2.0 包含多重/虚拟继承,以及纯虚拟函数。
 * 2.1 包含半巢状 (semi-nested) 类别,及 "delete [] 阵列指标"。
 * 3.0 包含全巢状 (fully-nested) 类别、template 和 "i++" vs "++i"。
 * 4.0 将包含例外处理。

 

Q128:如果签名编码标准化了,我能否将不同厂商编译器产生的程式码连结起来?

简短的回答:可能不行。

换句话说,有人希望标准化的签名编码规则能并入拟议中的 C++ ANSI 标准,避免还
要为不同厂商的编译器购买不同版本的物件程式库。然而不同的系统实作中,签名编
码的差异性只占一小部份而已,即使是在同一个基台(platform)上。这里列出一部
份其他的差异处:

1) 成员函数隐含的引数个数和型态。
   1a) ''this'' 有被特殊处理吗?
   1b) 传值的指标放在哪里?
2) 假设有用到 vtable 虚拟表格的话:
   2a) 它的内容及配置?
   2b) 多重继承时,''this'' 在何处/如何调整?
3) 类别如何配置,包含:
   3a) 基底类别的位置?
   3b) 虚拟基底类别的处理?
   3c) 虚拟表格指标的位置,如果有用虚拟表格的话?
4) 函数的呼叫惯例,包含:
   4a) 呼叫者还是被呼叫者负责调整堆叠?
   4b) 实际参数放到哪里?
   4c) 实际参数传递之顺序?
   4d) 暂存器如何存放?
   4e) 传回值放到哪里?
   4f) 对传入/传回 struct 或 double 有无特殊的规定?
   4g) 呼叫末端函数(leaf function)有无特殊的暂存器存放规定?
5) run-time-type-identification 如何配置?
6) 当一个例外被 throw 时,执行期的例外处理系统如何得知哪一个区域物件该被解
   构?


 
■□ 第22节:其他的技术和环境的事项
 
● 22A:其他的技术事项
========================

Q129:为什麽有 static 资料成员的物件类别产生了 linker 错误?

Static 的资料成员必须外显地在唯一的模组中定义。
                     ^^^^^^  ~~~~~~^^^^  ^^^^
【译注】这句话要逐字细读。原文是:Static data members must be
        explicitly defined in exactly one module.

譬如:
        class Fred {
        public:
          //...
        private:
          static int i_;  // 宣告 static 资料成员 "Fred::i_"
          //...
        };

Linker 会告诉你 "Fred::i_ is not defined(未定义)" ,除非你在任何一个(且
唯一)原始档中定义(而非宣告)了 "Fred::i_" :

        int Fred::i_ = 某个会产生 int 的运算式;
或是:
        int Fred::i_;

通常我们会在 "Fred.C" 档中定义 "Fred" 类别的 static 资料成员(或 "Fred.cpp"
等等你使用的副档名)。

 

Q130:"struct" 和 "class" 关键字差别在哪?

struct 的成员和基底类别, 都是预设为 public 的,而 class 则预设为 private。
注意:你应该“明显地”把基底类别设为 public、private 或是 protected,而不
要依赖预设值。

除此之外,两者的功能是相等的。

 

Q131:为什麽不能以函数的传回值来多载(overload)它?

如果你同时宣告了 "char f()" 及 "float f()" ,编译器会给你个错误讯息,因为
呼叫 "f()" 会造成模拟两可的情况。

 

Q132:什麽是「持续性」?什麽是「持续性物件」?

一个持续性物件 (persistent object),在创造它的程式执行结束後,仍可存活下来
。它甚至可存活於不同的父程式,存活於磁碟系统、作业系统、甚至於作业系统所处
的硬体上。

持续性物件的困难在於:如何有效地在次储存体中,存放它们的运作行为(method)
及资料位元(以及所有成员物件的资料和运作行为,及它们所有的成员物件、基底类
别……等等)。这一切都得自己来做的话,可不是件容易的事。在 C++中,你就得自
己来。C++/OO 的资料库系统,会替你把这些机制都隐藏起来。

 

Q133:为什麽浮点数 (floating point) 这麽不精确?为什麽这段程式不会印出 0.43?

        #include

        main()
        {
          float a = 1000.43;
          float a = 1000.0;
          cout << a - b << ''\n'';
        }

(附注,有些 C++ 环境下会印出 0.429993)

声明:受进位/舍位/近似值之苦,其实并不是 C++ 的问题,而是电脑科学界的问
题。不过还是一直有人在 comp.lang.c++ 里发问,所以我给你一个答案意思一下。

答案:浮点数本来就是个近似值。在 IEEE 的 32 位元浮点数标准里,有 1 位元的
正负号,8 位元的指数,23 位元的假数。因为正规化後的二进位假数都会变成像是
1.xxxxx... 的型式,所以头一项的 1 不予计入,就能得到 24 位元的有效假数。
1000.43(以及其他很多很多数字)都不是 float 或 double 的表示法,其实
1000.43 的位元内容是这样子的(''s'' 代表正负号,''e'' 代表指数,''m'' 代表假数)

    seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm
    01000100011110100001101110000101

假数移位後变成 1111101000.01101110000101 或是 1000 + 7045/16384。
分数部份为 0.429992675781。
float 的假数占 24 位元,所以你只得到 16M 分之一的精确度。
double 有较高的精确度(53 位元的假数)。

==========================
● 22B:其他环境下的琐事
==========================

Q134:有任何 TeX 或 LaTeX 的巨集,能处理 "C++" 的留白效果(spacing)吗?

有的,底下列出两个:

\def\CC{C\raise.22ex\hbox{{\footnotesize +}}\raise.22ex\hbox{\footnotesize +}}

\def\CC{{C\hspace{-.05em}\raisebox{.4ex}{\tiny\bf ++}}}

 

Q135:在哪儿可拿到 C++2LaTeX 这个 C++原始码的 LaTeX 美编工具(pretty
      printer)?

这儿列出一些 ftp 地点:

Host aix370.rrz.uni-koeln.de   (134.95.80.1) Last updated 15:41 26 Apr 1991
    Location: /tex
      FILE      rw-rw-r--     59855  May  5  1990   C++2LaTeX-1.1.tar.Z
Host utsun.s.u-tokyo.ac.jp   (133.11.11.11) Last updated 05:06 20 Apr 1991
    Location: /TeX/macros
      FILE      rw-r--r--     59855  Mar  4 08:16   C++2LaTeX-1.1.tar.Z
Host nuri.inria.fr   (128.93.1.26) Last updated 05:23  9 Apr 1991
    Location: /TeX/tools
      FILE      rw-rw-r--     59855  Oct 23 16:05   C++2LaTeX-1.1.tar.Z
Host iamsun.unibe.ch   (130.92.64.10) Last updated 05:06  4 Apr 1991
    Location: /TeX
      FILE      rw-r--r--     59855  Apr 25  1990   C++2LaTeX-1.1.tar.Z
Host iamsun.unibe.ch   (130.92.64.10) Last updated 05:06  4 Apr 1991
    Location: /TeX
      FILE      rw-r--r--     51737  Apr 30  1990
      C++2LaTeX-1.1-PL1.tar.Z
Host tupac-amaru.informatik.rwth-aachen.de   (192.35.229.9)
Last updated 05:07 18 Apr 1991
    Location: /pub/textproc/TeX
      FILE      rw-r--r--     72957  Oct 25 13:51  C++2LaTeX-1.1-PL4.tar.Z
Host wuarchive.wustl.edu   (128.252.135.4) Last updated 23:25 30 Apr 1991
    Location: /packages/tex/tex/192.35.229.9/textproc/TeX
      FILE      rw-rw-r--     49104  Apr 10  1990   C++2LaTeX-PL2.tar.Z
      FILE      rw-rw-r--     25835  Apr 10  1990   C++2LaTeX.tar.Z
Host tupac-amaru.informatik.rwth-aachen.de   (192.35.229.9)
Last updated 05:07 18 Apr 1991
    Location: /pub/textproc/TeX
      FILE rw-r--r-- 74015  Mar 22 16:23 C++2LaTeX-1.1-PL5.tar.Z
    Location: /pub
      FILE rw-r--r-- 74015  Mar 22 16:23 C++2LaTeX-1.1-PL5.tar.Z
Host sol.cs.ruu.nl   (131.211.80.5) Last updated 05:10 15 Apr 1991
    Location: /TEX/TOOLS
      FILE      rw-r--r--     74015  Apr  4 21:02x   C++2LaTeX-1.1-PL5.tar.Z
Host tupac-amaru.informatik.rwth-aachen.de (192.35.229.9)
Last updated 05:07 18 Apr 1991
    Location: /pub/textproc/TeX
      FILE      rw-r--r--      4792  Sep 11  1990 C++2LaTeX-1.1-patch#1
      FILE      rw-r--r--      2385  Sep 11  1990 C++2LaTeX-1.1-patch#2
      FILE      rw-r--r--      5069  Sep 11  1990 C++2LaTeX-1.1-patch#3
      FILE      rw-r--r--      1587  Oct 25 13:58 C++2LaTeX-1.1-patch#4
      FILE      rw-r--r--      8869  Mar 22 16:23 C++2LaTeX-1.1-patch#5
      FILE      rw-r--r--      1869  Mar 22 16:23 C++2LaTeX.README
Host rusmv1.rus.uni-stuttgart.de   (129.69.1.12)
Last updated 05:13 13 Apr 1991
    Location: /soft/tex/utilities
      FILE      rw-rw-r--    163840  Jul 16  1990   C++2LaTeX-1.1.tar

 

Q136:该到哪里取得 "tgrind" 这个 C++/C/etc 的原始码美编工具?

"tgrind" 读入 C++ 原始档案,并输出能让 Unix 印表机印出美观文件的东西。它常
会伴随在 TeX 和 LaTeX 的套件里;请找找这个目录:
 "...tex82/contrib/van/tgrind" 。 由 Jerry Leichter 所做更新的版本,可在
venus.ycc.yale.edu in [.TGRIND] 里找到。

 

Q137:有给 GNU emacs 编辑器用的 C++-mode 吗?有的话,该怎麽拿?

Yes,有一个给 GNU emacs 用的 C++-mode。

最新、最好的 C++-mode(以及 c-mode)版本是 cc-mode.el 档,是 Detlef &
Clamen 版本的延伸。Emacs 里头有一个了,较新的则在 elisp 里面。

 

Q138:我要到哪儿得到和作业系统相关的 FAQs( 譬如:BC++、DOS、Windows 等等
      )?

请参考:
 * comp.os.msdos.programmer
 * comp.windows.ms.programmer
 * comp.unix.programmer

[如果您有 BC++、VC++ 的 email address,或是 Semantic C++ 的臭□清单或可供
讨论的 mailing list,请告诉我该如何加入,我会在这儿提出的。]

 

Q139:为什麽我的 DOS C++ 程式说 "Sorry: floating point code not linked"
      “抱歉,浮点运算程式码未连结进来”?

编译器会试著节省执行档的大小,所以除非必要,否则不引入浮点数→字串格式转换
的副程式,可是有时候它会猜错,就会产生上述的错误讯息了。解决法:(1) 使用
 而不要用 ,或是 (2) 在您程式的某个地方,置入如下的函
数(但是不要真的去呼叫它!):

        static void dummyfloat(float *x) { float y; dummyfloat(&y); }

请参考关於 stream I/O 的 FAQ项目,有提到更多使用  vs 
的理由。

Q140:为什麽当我没执行 BC45 IDE 的话,BC++ 做出来的 Windows 应用程式就不能
      用?

用 BC++ 写 Windows 应用程式,如果当 BC45 IDE 正在执行时,你的程式很正常;
待会儿当 BC45 IDE 关掉了,而你的程式却在建立视窗时产生了个 exception 的话
,就把底下这行程式加到你的应用程式类别 ("YourApp::InitMainWindow()") 里头
的 InitMainWindow() 内:

        EnableBWCC(TRUE);

【译注】这是因为你用 BC++ 写的应用程式,可能会自动用到 bwcc*.dll,刚好
        BC++ 的 IDE 也会用到它,所以两者并存的话,BWCC 已先被 IDE 载入了。
        若是 IDE 未执行,则 BWCC 未被载入,你就得用上面那一行程式来通知
        OWL 去载入它。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章