Programming Notes 2011_1

来源:互联网 发布:汽车电脑编程怎么编 编辑:程序博客网 时间:2024/05/17 23:41

 

元月15

1.检测IOs是否处于有效状态用 s.good() 例如cin.good();

  IOs中的所有状态都重设为有效状态 s.clear() 例如cin.clear();

2.在用顺序容器的时候,有可能用到逆序迭代器,例如在处理将二进制数据压入容器内部的时候,先压的数据是高位,后压的数据是低位(vector是这样的),计算这串二进制数对应的十进制数的时候要先从低位累加。这时候用(如果只读不写,加const)

for(vector<Type>::const_reverse_iteratorvec_it=XXX.rbegin() ; vec_it=XXX.rend(); ++vec_it)

 

代码量:85

 

元月16

1.打印出此时此刻的时间:(要用到头文件:time.h)

       time_tt1=time(0);

       cout<<ctime(&t1)<<endl;

或者:    time_t t1;

       time(&t1);

       cout<<ctime(&t1);

其中time()函数功能:time_t time( time_t *time )

                   返回当前时间,如果发生错误返回零。如果给定参数time ,那么当前时间存储到参数time中。

    ctime()函数功能:把日期和时间转换为字符串

    例如:time_t t1=0;

         cout<<ctime(&t1)<<endl;

         输出:Thu Jan 0108:00:00 1970

2.如果遇到了这样一条语句:malloc(strlen(str));几乎可以断定它是错误的,而malloc(strlen(str)+1)才是正确的。因为要考虑到容纳字符串结尾的'/0'字符。

3.分析编程语言缺陷的一种方法就是把所有的缺陷归于3类:不该做的做了;该做的没做;该做但做得不合适。(即多做之过、少做之过和误做之过)

 

代码量:10

 

元月17

 

代码量:105

 

元月18

1.对文件进行操作的时候需要注意:尽量不要使用绝对路径;如果需要返回上一级目录,可以用"../XXX",例如需要访问上一级目录的data.txt文件,访问路径可以设置为:"../data.txt"(注意是/而不是/符号,即是正斜杠而不是反斜杠)

 

元月19

1.C中,const的用途不大,不能取代#define

另外,C中的const默认是external linkageC++中默认是internallinkage

 

元月21

1.有关constCC++中的区别(这个问题可纠缠了我不少时间^-^)

引言:(高质量C/C++编程指南3rd edtion)C语言中,用const定义的常量其实是只读变量,因此会给它分配存储空间(外连接的);但是在C++中,const定义是常量,它要具情况具体对待:对于基本数据类型的常量,编译器会把它放到符号表中而不分配内存空间,而ADT/UDTconst对象则需要分配存储空间(大对象)。还有一些情况也需要分配存储空间,例如强制声明为extern的符号常量或取符号常量的地址等操作,都将强迫编译器为这些常量分配存储空间,以满足用户的要求。

    你可以取一个const符号常量的地址:对于基本数据类型的const常量,编译器会重新在内存中创建它的一个拷贝,你通过其他地址访问到的就是这个拷贝而非原始的符号常量;对于构造类型的const常量,实际上它是编译时不允许修改的变量,因此如果你能绕过编译器的静态类型安全检查机制,就可以在运行时修改其内存单元。例如:

***********************************示例*************************************

const long lng=10;

long *pl=(long*)&lng;

*pl=1000;                       //"迂回修改"

cout<<*pl<<endl;                //1000,修改的是拷贝的内容!

cout<<lng<<endl;                //10,原始常量并没有改变!

 

class Integer{

public:

 Integer():m_lng(100){}

 longm_lng;

};

const Integer int_l;

Integer *pInt=(Integer*)&int_l;         //去除常数属性

pInt->m_lng=1000;                       //"迂回修改"

cout<<pInt->m_lng<<endl;                //1000,修改const对象

cout<<int_l.m_lng<<endl;               //1000"迂回修改"成功*****************************************************************************

上面说明:const符号只是编译时(源代码级或语言层面)强类型安全检查机制的一种手段,以帮助程序员发现无意中要修改他们的代码并进行纠正,而在运行时(二进制层面)无法阻止恶意的修改。也可以说就是"防君子不防小人"

提示:(a).从理论上讲,只要你手中握有一个对象的指针(内存地址),你就可以设法绕过编译器随意修改它的内容,除非该内容收到操作系统的保护。也就是说,C++并没有提供对指针有效性的静态检查,而是把它丢给了操作系统,这正是使用指针的危险所在。

    (b).在标准C语言中,const符号常量默认是外连接的(分配存储),也就是说你不能在两个编译单元中同时定义一个同名的const符号常量(重复定义错误),或者把一个const符号常量定义放在一个头文件中而在多个编译单元中同时包含该头文件,但是在标准C++中,const符号常量默认是内连接的,因此可以定义在头文件中。当在不同的编译单元中同时包含该头文件时,编译器认为他们是不同的符号常量,因此编译单元独立编译时会分别为他们分配存储空间,而在连接时进行常量合并(注:目的减少内存消耗,且优化程序效率。)。

 

(1).c程序中,const符号常量定义的默认连接类型是external linkage。因此,如果要在头文件中定义,必须使用static关键字,否则会导致"redefinition"的编译诊断信息;但是在c++程序中,const符号常量定义的默认连接类型是static的,即internallinkage。这就是在头文件中定义而不需要static的原因。

(2).

************************C++代码******************************

header.h

const int test = 1;

test1.cpp

#include <iostream>

#include "header.h"

using namespace std;

int main()

{

 cout<< "in test1 :" << test << endl;

}

test2.cpp

#include <iostream>

#include "header.h"

using namespace std;

void print()

{

 cout<< "in test2:" << test << endl;   

}

以上代码编译连接完全不会出问题,但如果把header.h改为:

extern const int test = 1;

在连接的时候,便会出现以下错误信息:

test2 error LNK2005: "int consttest" (?test@@3HB) 已经在 test1.obj 中定义

因为extern关键字告诉C++编译器test会在其他地方引用,所以,C++编译器就会为test创建存储空间,不再是简单的存储在名字表里面.所以,当两个文件同时包含header.h的时候,会发生名字上的冲突.

此种情况和Cconst含义相似:(实际上就是因为在C++,const默认使用内部连接.C中使用外部连接.)

header.h

const int test = 1;

test1.c

#include <stdio.h>

#include "header.h"

int main()

{

 printf("in test1:%d/n",test);

}

test2.c

#include <stdio.h>

#include "header.h"

void print()

{

 printf("in test2:%d/n",test);   

}

错误消息:

test3 fatal error LNK1169: 找到一个或多个多重定义的符号

test3 error LNK2005: _test 已经在 test1.obj 中定义

C++,是否为const分配空间要看具体情况.

如果const前加上关键字extern或者取const变量地址,则编译器就要为const分配存储空间.

(3).

C++中的const属性是可以被去掉的,通过const_cast就可以强转掉。你试过这样的代码吗?

#include <iostream>

using namespace std;

int main()

{

   const int a = 10;

   const int *pa = &a;

   int* pb = const_cast<int*>(pa);

   *pb = 20;

   cout << *pa << endl;

    cout << *pb << endl;

   cout << a << endl;

        return 0;

}

请猜测上面程序的运行结果!

 

2011.1.26

1.register关键字:这个关键字能够给编译器设计者提供线索,就是程序中的哪些变量属于热门(经常被使用),这样就可以把它们存放到寄存器中。

2.第一个C编译器大约出现在1970年。在过去的这几十年里,C语言也随着C编译器茁壮成长,它对直接由硬件支持的底层操作的强调,带来了极高的效率和移植性,反过来也帮助UNIX取得了巨大的成功。

3.C预处理器实现的3个主要功能是:

(1).字符串替换:通常用于为常量提供一个符号名;

2)头文件包含(这是在BCPL中首创的)

3)通用代码模版的扩展。与函数不同,宏在连续几个调用中所接收的参数的类型可以不同(宏的实际参数只是按照原样输出)。这个特性的加入比前两个稍晚,而且多少显得有些笨拙。在宏的扩展中,空格会对扩展的结果造成很大的影响。

#define a(y) a_expanded(y)

那么a(x);被扩展为 a_expanded(x);

#define a (y) a_expanded (y)

那么a(x);则被扩展为:(y) a_expanded (y) (x)

它们所表示的意思风马牛不相及。

4. ANSI(美国国家标准化组织) “ANSI C”

5. char *s const char *p 是相容的,但是char **s const char **p是不相容的。例如:

foo(const char**p){}

 main(int argc, char **argv)

{

     Foo(argv);   //warning: argument is incompatible withprototype

}

标准说明:表达式1=表达式2

左边指针所指向的类型必须具有右边指针所指向类型的全部限定符。

原因:char*是指向char类型的指针

      Const char*是指向具有const限定符的char类型的指针

     char *cp;

     const char *ccp;

     ccp=cp; 合法,因为:

    左操作数是一个指向具有const限定符的char的指针;右操作数是一个指向没有限定符的char的指针。Charchar类型是相容的,左操作数所指向的类型具有右操作数所指向的类型的限定符(无),然后再加上自己的限定符(const)。容易分析,反过来就不行。

char **是指向char类型的指针的指针

      Const char **是指向具有const限定符的char类型的指针的指针

      char **cpp;

     const char **ccpp;

      ccpp=cpp;//不合法,因为

Const char **是一个没有限定符的指针类型,因为它是指向具有const限定符的指针的指针,由于char**constchar **都是没有限定符的指针类型,但是它们所指向的类型不一样(前者指向char *,后者指向const char *),因此它们是不相容的。

 

2011.1.27

1.对于无符号类型的建议:尽量不要在你的代码中使用无符号类型,以免增加不必要的复杂性。尤其是,不要仅仅因为无符号数不存在负值(如年龄、国债等),而用它来表示数量。其实我以前经常这样做,现在才发现很危险)尽量使用像int那样的有符号类型,这样在涉及升级混合类型的复杂细节时,不必担心边界情况(如-1被翻译为非常大的正数)。只有在使用尾段和二进制掩码时,才使用无符号数。应该在表达式中使用强制类型转换,使操作数均为有符号数或者无符号数,这样就不必由编译器来选择结果的类型。

2.#pragma用于向编译器提供一些信息,例如将某个特定的函数扩展为内联函数,或者取消边界的检查。

 

2011.1.28

1.ANSI C引入的一个新特性就是:相邻的字符串常量会被自动合并为一个字符串常量。这就省掉了过去在书写多行信息时必须在行末加上“/”的做法,后续的字符串可以出现在每行的开头。

Printf(“I’m /

Chen.”);

现在可以直接用printf(“I’m””chen”);来替代它。

所以字符串数组在初始化的时候一定要小心。

2.

C语言中的符号重载(只摘取了一部分)

C语言运算符优先级存在的问题

i=1,2

的运算结果是i=1,因为赋值运算符优先于“,”运算符,所以语句先执行i=1赋值运算,然后执行常量2的运算,运算结果丢弃,所以最后i的结果是1

3.C语言操作符的“结合性”:它是仲裁者,在几个操作符具有相同的优先级时,决定先执行哪一个。

例如int a,b=1,c=2;

a=b=c;

我们发现这个表达式只有赋值符,这样的优先级无法帮助我们知道哪个操作先执行。是先执行b=c呢?还是先执行a=b呢?如果按前者,a=2;如果按后者,a=1

实际上,所有的赋值符(包括复合赋值符)都具有右结合性,就是说表达式中最右边的操作最先执行,然后从右到左依次执行。这样,c先赋值给bb再赋值给a,最终a的值是2。类似地,具有左结合性的操作符(如位操作符&|)则是从左至右依次执行

事实上,所有优先级相同的操作符,他们的结合性也相同。

注意:这与函数的调用不同,函数的调用时,各个参数的计算顺序是不确定的,取决于编译器,目的是为了让编译器设计者选取最合适的方法来产生最快的代码

 

2011.1.29

1. 有关参数传递的问题:

有些C语言书籍声称“在调用函数时,参数按照从右到左的次序压到堆栈里”,这种说法过于简单了---如果你有一本这样的书,把那一页撕下烧掉,如果你有一个这样的编译器,把编译器源代码的那几行删掉。参数在传递时,首先尽可能地存放在寄存器中(追求速度)。注意:int型变量跟只包含一个int型成员的结构变量s在参数传递时的方式可能完全不同。一个int型参数一般会被传递到寄存器中,而结构参数则很可能被传递到堆栈中。

2.联合的用途:

联合一般被用来节省空间,因为有些数据项是不可能同时出现的,如果同时存储它们显然颇为浪费。

3.枚举

很多时候,枚举可以代替大量的#define。而且枚举具有一个优点:#define定义的名字一般在编译时被丢弃,而枚举名字则通常一直在调试器中可见,可以在调试代码时使用它们

4.typedef int x[10]#definex int[10]的区别

   typedef和宏文本替换之间存在着一个关键性的区别:正确思考这个问题的方法就是typedef看成是一种彻底的“封装”类型---在声明它之后不能再往里面加别的东西。它和宏的区别体现在两个方面:

首先,可以用其它类型说明符对宏类型名进行扩展,但对typedef所定义的类姓名却不能这样做。如下所示:

#define peach int

Unsigned peach i; /* 没问题*/

 

Typedef int banana

Unsigned banana i;   /*错误!非法!*/

其次,在连续几个变量的声明中,用Typedef定义的类型能够保证在声明中所有的变量均为同一种类型,而用#define定义的类型则无法保证。如下所示:

#define int_ptr int *

Int_ptr chalk,cheese;

经过宏扩展,第2行变为:

Int * chalk,cheese;

Chalk是一个指向int的指针,而cheese只是一个int

相反,下面的代码中:

Typedef char* char_ptr;

Char_ptr AAA,BBB;

AAABBB都是指向char的指针。

 

总结:typedef#define的区别

typedefC语言语句,其功能是用户为已有数据类型取“别名”

例如:

typedef int INTEGER

这以后就可用INTEGER来代替int作整型变量的类型说明了,如:

INTEGER a,b;

typedef定义数组、指针、结构等类型将带来很大的方便,不仅使程序书写简单而且使意义更为明确,因而增强了可读性。例如:

typedef int a[10]; 表示a是整型数组类型,数组长度为10。然后就可用a说明变量,如:a s1,s2;

完全等效于: int s1[10],s2[10]

同理

typedef void (*p)(void) 表示p是一种指向void型的指针类型!

#define是预处理中的宏定义命令,例如:#define int PARA

表示在源程序中的所在int将会被PARA原样代替!

如:程序中有int a,b

则在编译前将被替换为PAPAa,b

Typedefdefine都可以用来给对象取一个别名,但是两者却有着很大不同。

(1).首先,二者执行时间不同

关键字typedef在编译阶段有效,由于是在编译阶段,因此typedef有类型检查的功能。

Define则是宏定义,发生在预处理阶段,也就是编译之前,它只进行简单而机械的字符串替换,而不进行任何检查。

 

#define用法例子:

#define f(x) x*x

main( )

{

 int a=6b=2c

 c=f(a) / f(b)

 printf("%d/n"c)

}

程序的输出结果是: 36,根本原因就在于#define只是简单的字符串替换,应当加个括号“(X*X)”。

(2)功能不同

Typedef用来定义类型的别名,这些类型不只包含内部类型(intchar等),还包括自定义类型(如struct),可以起到使类型易于记忆的功能。

如: typedef int (*PF) (const char *, const char *);

定义一个指向函数的指针的数据类型PF,其中函数返回值为int,参数为const char *

 

typedef 有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以获得最高的精度:typedef long double REAL;

在不支持 longdouble 的机器上,该 typedef看起来会是下面这样:typedefdouble REAL;

并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样:typedef float REAL;

 

#define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。

 

(3). 作用域不同

#define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用。

typedef有自己的作用域。

void  fun()  

  {  

      #define   A   int  

  }  

 

  void   gun()  

  {  

        //在这里也可以使用A,因为宏替换没有作用域,  

        //但如果上面用的是typedef,那这里就不能用A   ,不过一般不在函数内使用typedef

   }

(4). 对指针的操作

二者修饰指针类型时,作用不同。

Typedef int * pint

#define PINT int *

 

Const pint p//p不可更改,p指向的内容可以更改,相当于 int * const p;

Const PINT p//p可以更改,p指向的内容不能更改,相当于 const int *p;或 int const *p

pint s1, s2; //s1s2都是int型指针

PINT s3, s4; //相当于int *s3s4;只有一个是指针。