正文

C与C++代码精粹笔记二2004-12-24 10:44:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/book/31.html

分享到:

1,预定义宏
__LINE__    当前代码行的行号(等于读到目前为止新行的字符数)
__FILE__    源文件的名字
__DATE__    以"Mmm dd yyyy"的形式转换日期
__TIME__    以"hh:mm:ss"的形式转换日期
__cplusplus    一个包含正式的C++标准的日期

2,如果代码是在C/C++混合编程环境中编写的,可以将新的C++代码与旧的C代码相链接。所需要做的所有事情就是通过extern "C"链接规范来告诉C++翻译器,不要“砍掉”与C组件相匹配的外部名字,例如:
extern "C" void  f();    // f() 在C环境下被编译
C++标准库中的C部分知道是否该砍掉名字(在第一章已解释过),这取决于在什么模式下(C或C++)编译。如果仔细阅读标准C头文件会发现销售商使用__cplusplus宏通过一个extern "C"块根据以下模式条件隐藏了标准C声明:
#if defined(__cplusplus)
extern "C"
{
#endif
<C声明放在此处>
#if defined(__cplusplus)
}
#endif

3,#if defined(X)  <==>  #ifdef X
defined运算符比其右边的等价指令要灵活多,因为可以把许多测试联合起来构成一个表达式:
#if defined(__cplusplus) && !defined(DEBUG)

4,预处理运算符
#    字符串化
##    加标记
defined    符号表查询

#define trace(x,format)    printf(#x "=%" #format "\n",x)
#define trace2(i) trace(x##i,d)

5,当宏必须作出选择时,好的做法是把它写成一个表达式而不是语句
void __assert(char *cond,char *fname,long lineno){
    fprintf(stderr,"Assertiion failed:%s, file %s, line %ld\n",cond,fname,lineno);
    abort();
}

#define assert(cond) \
    if(!(cond)) __assert(#cond,__FILE__,__LINE__)        //    不好
#define assert(cond) \
    ((cond)?(void)0:__assert(#cond,__FILE__,__LINE__)    // 好
    
6,预处理器的宏替换功能显而易见为你提供了很大的灵活性。但要记住以下两点限制:
A,无论何时预处理器在其替代文本中遇到当前的宏,不管在进程中有多么深的嵌套,它都不做扩展,只是保持不变(否则进程将永远不终止!)。
B,如果被充分扩展的语句相似于一条预处理指令(例如扩展以后结果是一条#include指令),它没有被调用,而是逐字地留在程序文本中。

7,三字符运算符序列
??= #
??( [
??/    \
??) ]
??'    ^
??< {
??! |
??> }
??- ~

8,新的C++双字符运算符和保留字
<%    {
%>    }
<:    [
:>    ]
%%    #
Bitand    &
And        &&
Bitor    |
Or        ||
Xor        ^
Compl    ~
and_eq    &=
or_eq    |=
xor_eq    ^=
Not        !
not_eq    !=

9,翻译阶段
标准C和C++定义了9个不同的翻译阶段。当然,实现没有必要在代码中分成9个独立的阶段,但是翻译的结果必须好像已经这样做了一样,这9个阶段是:
1,物理源字符被映射到源字符集中。其中包括三字符组合替换以及诸如把回车/换行映射到一个单独的MSDOS环境下的换行字符那样的东西。在C++程序中,任何不在基础源字符集中的字符都被它的通用字符名替换。
2,所有以反斜杠结束的行都和它们接下来的行合并,并且删去反斜杠。
3,源码被分析成预处理标记,并且注释被一个单独的空字符所替换,C++双字符被识别为标记。
4,调用预处理指令并且扩展宏,对于任何被包含的文件循环地重复步骤1到4。
5,源字符退出字符常量序列,普通字符名被映射成执行字符集成员(例如,'\a'将在ASCII环境下转换成7的一个字节值)。
6,相邻的字符串被连接。
7,传统的编译:词汇和语义分析,并翻译成汇编语言或机器码。
8,(只有C++)执行任何待解决的模板实例。
9,链接:解决外部引用,准备好程序映像以便执行。

预处理器由步骤1到4组成。

10,存储两个指针差的便捷方法是把这个差存储在ptrdiff_t中,它在stddef.h中定义,包含在<iostream>中。
ptrdiff_t diff=p-q;

11,说明指针转换
#include<iostream>
using namespace std;

void main(){
    int i=7;
    char *cp=(char *)&i;
    
    cout<<"The integer at "<<&i<<" == "<<i<<endl;
    
    //分别打印每个字节的值
    for(int n=0;n<sizeof i;n++)
        cout<<"The byte at "<<(void *)(cp+n)<<" == "<<int(*(cp+n))<<endl;
}

12,位域
#include<iostream>
using namespace std;

struct Date{
    unsigned day:5;
    unsigned mon:4;
    unsigned year:7;
};

void main(){
    struct Date d;
    d.day=2;
    d.mon=8;
    d.year=92;

    cout<<hex<<*((short *)(&d))<<endl;
}

/////////////////////////////////////////////
#include<iostream>
using namespace std;

struct Date{
    unsigned day:5;
    unsigned mon:4;
    unsigned year:7;
};

void main(){
    unsigned short date,year=92,mon=8,day=2;
    Date *dp=(Date *)&date;

    dp->day=day;
    dp->mon=mon;
    dp->year=year;

    cout<<hex<<date<<endl;
}

13,在面向对象操作中,指向一个对象的指针称为“句柄”。

14,普通指针
通常编写能接收指向任意类型参数的函数是很方便的。这是很有必要的,例如,用标准的库函数memcpy,能够从一个地址向另一个地址拷贝一块内存。你也可能想调用memcpy来拷贝自己创建的结构:
struct mystruct a,b;
...
memcpy(&a,&b,sizeof(struct mystruct));
为了操作任意类型的指针,memcpy把它头两个参数声明为void型指针。可以不需要强制类型转换将任何类型的指针赋予void*类型。也可以在C而不是C++中将void*赋予其他任何类型的指针。这里说明了void指针的memcpy函数的简洁实现:
void* memcpy(void* target, const void* source, size_t n){
    char *targetp=(char *)target;
    const char *sourcep=(const char *)source;
    while(n--)
    *targetp++=*sourcep++;
    return target;
}
这个版本的memcpy必须把指向void的指针赋予指向char指针一,这样它就可以每次传递内存块的一个字节,并对这个字节中的数据进行拷贝。试图复引用一个void*是没有意义的,因为它的大小是未知的。

15,
strncpy(s,t,n)[n]='\0';

a[i]=*(a+i)=*(i+a)=i[a]

size_t n=sizeof a/sizeof a[0];

cout<<(void*)"hello"<<endl;
cout<<"hello"[3]<<endl;

p=a+n-1;
for(int i=0;i<n;i++)
    cout<<p[-i]<<endl;

16,函数指针
#include<stdio.h>

void main(){
    int (*fp)(const char *,...)=printf;
    fp("hello\n");
}

17,
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;

int comp(const void*, const void*);

void main(int argc, char *argv[]){
    qsort(argv+1, argc-1, sizeof argv[0], comp);
    while(--argc)
        cout<<*++argv<<endl;
}

int comp(const void *p1, const void *p2){
    const char *ps1=*(const char **)p1;
    const char *ps2=*(char **)p2;
    return strcmp(ps1,ps2);
}

18,
#include<stdio.h>

extern void retrieve(void);
extern void insert(void);
extern void update(void);
extern int show_menu(void);

void main(){
    int choice;
    void (*farray[])(void)={retrieve,insert,update};

    for(;;){
        choice=show_menu();
        if(choice>=1 && choice<=3)
            farray[choice-1]();
        else if(choice==4)
            break;
    }
}

19,
#include<iostream>
using namespace std;

class Object{
public:
    void retrieve(){
        cout<<"Object::retrieve"<<endl;
    }
    void insert(){
        cout<<"Object::insert"<<endl;
    }
    void update(){
        cout<<"Object::update"<<endl;
    }
    void process(int choice);

private:
    typedef void (Object::*Omf)();
    static Omf farray[3];
};

Object::Omf Object::farray[3]={&Object::retrieve,&Object::insert,&Object::update};

void Object::process(int choice){
    if(0<=choice && choice<=2)
        (this->*farray[choice])();
}

void main(){
    int show_menu();    // 您所提供的!
    
    Object o;
    
    for(;;){
        int choice=show_menu();
        if(1<=choice && choice<=3)
            o.process(choice-1);
        else if(choice==4)
            break;
    }
}

阅读(3627) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册