Programming Notes 2011_2

2011.2.2

1.操作typedef的提示:

不要为了方便起见对结构使用tepedef

这样做唯一的好处是能使你不必书写 “struct” 关键字,但这个关键字可以向你提示一些信息,你不应该把它省掉。

typedef应该用在:

a).数组、结构、指针以及函数的混合类型。

b).可移植类型。比如当你需要一种至少20比特的类型时,可以对它进行Typedef操作的typedef的提示声明。这样,当把代码移植到不同的平台时,要选择正确的类型如short, int, long时,只要在typedef中进行修改就行,无需对每个声明都加以修改。

Typedef也可以为后面的强制类型转换提供一个简单的名字,如:

Typedef int{*ptr_to_int_fun}(void);

Char *p;……

XXX= (ptr_to_int_fun) p;

应该始终在结构的定义中使用普通结构标签,而不是typedef这样的结构类型,这样做可以使代码更为清晰。

说明:一般情况,以“_tag”结尾的名字通常都是结构标签。

2.声明和定义的区别:

3.

字符型指针可以用一个字符串常量进行初始化,如:char *p=”hello, world!”;

但是:在ANSI C中,初始化指针所创建的字符串常量被定义为只读。

数组也可以用字符串常量进行初始化,如 char a[]=”gooseberry”;

与指针相反,用字符串常量进行初始化的数组是可以修改的。其中的单个字符在以后可以被修改,如:

strncpy(a,”black”,5);

就将数组修改为”blackberry”

 

2011.2.3

1.链接器的基础知识:编译器创建一个输出文件,这个文件包含了可重定位的对象。这些对象就是与源程序对应的数据和机器指令。

2.静态链接:如果函数库的一份拷贝是可执行文件的物理组成部分,那么我们称之为静态链接。

动态链接:如果可执行文件只是包含了文件名,让载入器在运行时能够寻找程序所需要的函数库,那么我们称之为动态链接。

动态链接允许系统提供一个庞大的函数库集合,可以提供许多有用的服务。但是,程序将在运行时寻找它们,而不是将这些函数库的二进制代码作为自身可执行文件的一部分。最近几年,Microsoft在它的Windows操作系统中也采用了动态链接技术。

动态链接库的优点:

动态链接是一种更为现代的方法,它的优点是可执行文件的大小可以非常小。虽然运行速度稍慢些,但动态链接能更加有效的利用磁盘空间和虚拟内存,而且链接-编辑阶段的时间也会缩短(因为链接器的有些工作被推迟到载入时)。

动态链接使得函数库的版本升级更为容易。新的函数库可以随时发布,只要安装到系统中,旧的程序就能够自动获得新版本函数库的优点而无需重新链接。

 

2011.2.4

1.动态库文件的扩展名是“.so”,静态库文件的扩展名是“.a”。程序中包含的每一个头文件都可能代表着一个必须链接的库,然而头文件名字通常并不与它所对应的函数库名相似,非常遗憾,这是你“不得不知道的”C语言的一个混乱之处。

2.不要让程序中的任何符号成为全局的,除非有意把它们做为程序接口之一。(想想SUNOS中跟interpositioning有关的那个Bug,打印程序有时会出现一条错误信息)。

 

2011.2.6

1.当在一个可执行文件中运行size命令时,它会告诉你这个文件中的三个段(text文本段、data数据段和bss段)的大小。Size命令并不打印标题,所以要用echo命令产生它们。

2.段可以方便的映射到链接器在运行时可以直接载入的对象中。

3.堆栈段有3个主要用途:

a).堆栈为函数内部声明的局部变量提供存储空间。按照C语言的术语,这些变量被称为“自动变量”。

b).进行函数调用时,堆栈存储与此有关的一些维护性信息。

c).堆栈也可以被用作暂时存储区。

4.如果想返回一个指向在函数内部定义的变量的指针时,要把那个变量(不是指针!)声明为static。这样就能保证这个变量被保存在数据段中,而不是堆栈中。

5.setjmp()longjmp()C语言所独有的,它们部分弥补了C语言有限的转移能力。这两个函数协同工作,如下所示:

setjmp(jmp_buf j)必须首先被调用。它表示“使用变量j记录现在的位置”,函数返回零。

Longjmp(jmp_buf j,int i)可以接着被调用。它表示“回到j所记录的位置,让它看上去像是从原先的setjmp()函数返回一样,但是函数返回i,使代码能够知道它是否是通过longjmp()函数返回的。

使用longjmp()函数时,j的内容被销毁。

6.尽管longjmp()会导致转移,但是它和goto又有不同,区别如下:

a).goto语句不能跳出C语言当前的函数(这也是“longjmp“取名的由来,它可以跳得很远,甚至可以跳到其他文件的函数中)。

b).longjmp()只能跳回到曾经到过的地方。从这个角度讲,longjmp更像是“从何处来(comefrom)”,而不是“到何处去(goto)”,

7.setjmp()/longjmp()最大的用途是错误恢复。只要还没有从函数中返回,一旦发现一个不可恢复的错误,可以把控制转移到主输入循环,并从那里重新开始。setjmp()/longjmp()C++中变异为更普通的异常处理机制“catch”和“throw”。在使用setjmp()longjmp()的任何源文件中,必须包含头文件<setjmp.h>

 

2011.2.7

1.回学校后对下面这些工具,每个花15分钟进行一下测试。

2.需要注意,所有的磁盘制造商都是使用十进制数而不是二进制数来表示磁盘的容量。所以2GB的磁盘容量可以存储2000000000个字节的数据,而不是2147483648个字节。

3.Cache存储器是多层存储概念的更深扩展。它的特点是,容量小,价格高,速度快。它位于CPU和内存之间,是一种极快的存储缓冲区。所有的现代处理器都是用了Cache存储器。当数据从内存读入时,整“行”(一般16个或32个字节)的数据被装入Cache。所有对内存的读取和写入操作都要经过Cache.

 

2011.2.8

1.(在C语言中)从堆中获取内存的唯一方法是通过调用malloc(以及同类的callocrealloc等)库函数。Calloc函数与malloc函数类似,但它在返回指针之前先把分配好的内存的内容都清空为零。Realloc函数改变一个指针指向的内存块大小,既可将其扩大,也可将其缩小,它经常把内存拷贝到别的地方然后将指向新地址的指针返回给你。

Malloc/free从堆中获得内存,以及把内存返回给堆。

2.堆经常会出现两种类型的问题:

a).释放或改写仍在使用的内存(称为“内存损坏”)

b).未释放不再使用的内存(成为“内存泄漏”)

这是最难被调试发现的问题之一。

内存泄漏的主要可见症状就是“罪魁祸首”进程的速度会减慢。

3.如何检测内存泄漏:(两个步骤)

首先,使用swap命令观察还有多少可用的交换空间:

/usr/sbin/swap –s

Total:17228k bytes allocated +5396k reserved =22624k used,29548k available

(共计177228k已分配+5396k用于保留=22624k已用,29548k可用)

在一两分钟内键入该领命三到四次,看看可用的交换区是否减少。还可以使用其他一些/usr/bin/*stat工具如netstat,vemstat等。如果发现不断有内存被分配而且从不释放,一个可能的解释就是一个进程出现了内存泄漏。

第二个步骤就是确定可疑的进程,看看它是否是该为这内存泄漏负责。可以使用“pa-lu用户名”的命令来显示所有进程的大小,如下所示:

标题为SZ的列就是以页面数表示的进程的大小。(如果一定想知道以KB表示的页面的大小,可以使用pagesize命令)。同样数次重复这个命令,可以发现,任何动态分配内存的进程的大小都在增长。如果一个进程的大小看上去不断的增长而从不缩小,它就有可能出现了内存泄漏。

4.完成P174编程挑战:在PC上编写一个捕捉(Ctrl+break)信号的信号处理程序!(C专家编程相应章节末尾有程序答案范例)

 

2011.2.9

1.编译器通过自动分配和填充数据(在内存中)来进行数据对齐。但是,当程序员把一个char型指针转化为一个int型指针类似的情况时,就有可能出现神秘的总线错误。

2.完成P176编程挑战。(C专家编程相应章节末尾有程序答案范例)

3.在链表中释放元素程序:

错误程序:

for(p=start;p;p=p->next)

   free(p);

正确代码:

for(p=start;p;p=temp)

{

Temp=p->next;

Free(p);

}

4.每页有4096个字节。

代码量:写107

 

2011.2.10

1.标准C语言有八进制、十进制、十六进制常量,但是没有二进制常量。

2.P196的程序重新编译运行一下,学会“轮询”操作,这个操作很可能可以用到“多线程聊天对话系统当中”,使得只使用一个窗口也能像腾讯QQ的两个窗口一样的实现聊天功能。

3.如果你的函数需要多个不同的参数,可以考虑使用一个参数计数器和一个字符串指针数组,就像main函数的参数一样。我们所熟悉的int argc,char *argv[] 机制是非常普遍的,可以成功的运用到你所定义的函数中。(当然,也可以为所有的待传送的参数定义一个结构体,然后传送该结构体指针)

 

代码量:180

2011.2.11

1.debugging hooks:

你知不知道绝大多数调试器都允许从调试器命令行调用函数?如果你拥有十分复杂的数据结构时,它将会非常有用。可以编写并编译一个函数,用于遍历整个数据结构并把它打印出来,这个函数不会在代码的任何地方被调用,但它却是可执行文件的一部分。它就是“debugging hooks”。

代码量:27

 

2011.2.12

数组和指针的关系:

1所有作为函数参数的数组名总是通过编译器被转化为指针。

2.什么时候数组和指针是相同的?

C语言标准对此做了如下说明:
规则1:表达式中的数组名(与声明不同)被编译器当作一个指向该数组第一个元素的指针。(具体释义见ANSI C标准第6.2.2.1节)

规则2:下标总是与指针的偏移量相同。(具体释义见ANSI C标准第6.3.2.1节)

规则3:在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针。(具体释义见ANSI C标准第6.7.1节)

3.BCPL语言是C语言的祖先。

4.Type array[SIZE]:注意,如果Type是非内置类型,那么sizeof(Type)最好是2的乘方,因为这样编译器在每次访问数组array中第i个元素时,只需在array所指地址上加上一个偏移量offset,如果sizeof(Type)2的乘方,那么随机访问该数组的效率会高很多,因为offset=1<<N(其中N满足2.^N=m=sizeof(Type))。所以以后定义结构体时,最好将结构体的大小调整至2.^N,尤其是当该结构体可能用于定义数组的时候。(自己编程测试一下,如果不满足这种条件的结构体数组的指针进行自增操作时,移动的步长大小是多大,是结构体的大小吗?还是结构体大小向上取到2N次方的大小?)

代码量:172

 

2011.2.13

多维数组:

1.       如果在你的思维模式中,把数组看作是一种向量(即某种对象的一维数组,其元素可以是另一个数组),就能极大简化编程语言中这个相当复杂的领域。

2.       多维数组的存储:

 如果我们声明如下的多维数组:int apricot[2][3][5];

那么可以按图9-8(多维数组的存储)所示的任何一种方法在内存中为它定位:

Int (*p)[3][5]=apricot;

Int (*r)[5]=apricot[i];

Int *t=apricot[i][j];

Int u=apricot[i][j][k];

请注意下面这个程序中的重点编程语句:

int main()

{

       int arr[2][3][5]={0};

       int i,j,k,cnt=1;

       for(i=0;i<2;++i)

                 for(j=0;j<3;++j)

                          for(k=0;k<5;++k)

                                   arr[i][j][k]=cnt++;

       for(i=0;i<2;++i)

                 for(j=0;j<3;++j)

                          for(k=0;k<5;++k)

                                   cout<<*(*(*(arr+i)+j)+k)<<" ";

       cout<<"************************************************"<<endl;

       cout<<endl;

       int *p=arr[0][0];

       for(i=0;i<2;++i)

                 for(j=0;j<3;++j)

                          for(k=0;k<5;++k)

                                   cout<<*(p+i*3*5+j*5+k)<<" ";

       cout<<endl;

       cout<<"************************************************"<<endl;

       int (*pp)[5]=arr[0];

       for(i=0;i<2;++i)

                 for(j=0;j<3;++j)

                          for(k=0;k<5;++k)

                                   cout<<*(pp[j]+i*15+k)<<" ";

       cout<<endl<<"************************************************"<<endl;

       int (*ppp)[3][5]=arr;

       for(i=0;i<2;++i)

                 for(j=0;j<3;++j)

                          for(k=0;k<5;++k)

                                   cout<<*(ppp[i][j]+k)<<" ";

       return 0;

}

3.       C语言的多维数组中,最右边的下标是最先变化的,这个约定被称为“行主序”,由于“行/列主序”这个术语只适用于恰好是二维数组的多维数组,所以更确切的术语是“最右的下标先变化”。绝大部分语言都采用了这个约定,但是Fortran语言相反,它采用了“最左的下标先变化”。

4.       在定义数组的时候,如果数组的长度比所提供的初始化值的个数要多,剩余的几个元素会自动设置为0。如果元素的类型是指针,那么它们被初始化为NULL;如果元素的类型是float,那么它们被初始化为0.0.

5.       注意:char *turnip[23](char*) turnip[23]的意义是不一样的!(不是说下标方括号的优先级比指针的星号高吗?那么二者意义应该一样啊?后者应该是看成一种右值表达式,例如char *p=(char*turnip[23];

   char *turnip[23]:每个元素的类型是一个指向字符的指针(或者一个字符串---单纯从声明中无法区分二者)。

   (char*) turnip[23]:一个指向“具有23个字符类型元素的数组”的指针。

这是因为下标方括号的优先级比指针的星号高。

代码量:250

 

2011.2.14

1.锯齿状数组上使用指针:

如果需要存储50个字符串,每个字符串的最大长度可达到255个字符,那么可以声明下面的二维数组:

Char carot[50][255];

但是这样做很浪费,因为并不是每个字符串的长度都会有255个字符。

一种替代方法就是使用字符串指针数组:

Char *turnip[MAX_SIZE];

然后根据需要为各个字符串分配内存,这将会大大节省系统资源。有点人把它称作“锯齿状数组”,是因为它右端的长度不一。

2.如果有可能尽量不要选择拷贝整个字符串的方法。如果需要从两个不同的数据结构访问它,拷贝一个指针比拷贝整个字符串数组要快得多,空间也节省很多。

3.下面这个很重要:

4.怎样使用指针向函数传递一个多维数组(很重要)

5对多维数组作为参数传递的支持的缺乏是C语言存在的一个内在限制。这使得用C语言编写某些特定类型的程序非常困难(如数值分析算法)。

6.       int (*paf())[20];    //paf是一个函数,它返回一个指向包含20int型元素的数组的指针。它的定义可以如下:

int (*paf())[20]

Int (*pear)[20];   //声明一个指向包含20int型元素的数组的指针

 Pear=calloc(20*sizeof(int));

 If(!pear)

     Longjmp(error,1);

Return pear;

可以用下面的方法来调用函数:

Int (*result)[20];    //声明一个指向包含20int型元素的数组的指针

Result=paf();      //调用函数

(*result)[3]=12;   //访问结果数组

7.库函数realloc()能够对一个现在的内存块大小进行重新分配(通常是使之扩大),同时不会丢失原先内存块的内容。

calloc()函数:Allocates an array in memory with elements initialized to 0.

函数原型:void *calloc( size_t num, size_t size);

8. 请看以下程序:

int main()

{

       int *p_arr10=new int[10];

       int *pdel=p_arr10+1;

       delete pdel;

       return 0;

}

运行时错误。错误原因:(自我猜想)如果动态分配连续内存,那么删除的时候只能采用 delete[] p_arr类似的方法删除,即只能牵住所分配内存的第一个单元开始释放,不能单独对后续某个内存单元进行释放。(回学校后上查阅资料或者BYR论坛求解这个问题)

代码量:334

 

2011.2.15

代码量:150

 

2011.2.16

C++

1.       面向对象的关键就是把一些数据和对这些数据进行的操作的代码组合在一起,并用某种时髦手法将它们做成一个单元。许多编程语言将这种类型的单元成为“类”。

面向对象编程的特点是:继承和动态绑定。C++通过类的派生支持继承,通过虚函数支持动态绑定。

2.       面向对象编程的关键概念:

抽象建立了一种抽象数据类型,C++使用类这个特性来支持它。它提供了一种自上而下的、观察数据类型属性的方法来看待封装:把用户定义类型中的各种数据和方法组合在一起。它同时也提供了一种自底向上的观点来看待封装:把各种数据和方法组合在一起实现一种用户定义类型。

3.       类的名字应该以大写字母开头。

4.       C语言很容易让你在开枪时伤着自己的脚,而C++使这种情况很少发生。但是,一旦发生这种情况,它很可能轰掉你整条腿。     ------Bjarne Stroustrup

5.       操作符重载在C语言中已经以一种初步的方式存在,例如,下面的C代码就用到了重载。
int i,j,k;

double f,g,h;

i=j+k;

f=g+h;

6.       重载(按照它的定义)总是在编译时进行解析。编译器查看操作数的类型,并检查它是否是该操作符所声明的类型之一。

7.       多态---运行时绑定:(有编译时绑定这么一说吗?有!例如模版就属于编译时多态,即编译时绑定!重载是否也是编译时多态呢?)

(面试时一个很好的解释多态性用处的理由)有时你无法在编译时分辨所拥有的对象到底是基类对象还是派生类对象时,多态性可以帮上忙。这个判断并调用正确函数的过程称为“后期绑定(late binding)”,

8.       多态和interposing有相似之处:多态和interposing都允许用一个标识符来命名多个函数。Interposing是一种多少有些笨拙的方式,它在编译时把所有用该标识符命令的函数都绑定到一个函数中。

9.       newdelete操作符用于取代malloc()free()函数,这两个操作符用起来更方便一些(如能够自动完成sizeof的计算工作,并且会自动调用合适的构造函数和析构函数)。New能够真正的建立一个对象,而malloc()函数则只是分配内存。

10.   C语言原先的设计哲学:所有特性都不需要隐式的运行时支持。

11.   C++C语言的改进:

12C++中存在,但在C语言中却不存在的限制有:

13.一个很有意思的问题:

代码量:65+43

 

2011.2.17

1.语句:mango[i++]+=y;

等价于 mango[i]=mango[i]+y;  i++;

而不是 mango[i++]=mango[i++]+y;

2. “++x”表示取x的地址,增加它的内容,然后把值放在寄存器中;

x++”表示取x的地址,把它的值装入寄存器中,然后增加内存中的x的值。

3.教训:不要在一行代码里实现太多的功能

这种做法并不能使编译器产生的代码更有效率,而且会使你丧失调试代码的机会。(人人都知道调试比第一次编写代码要难上一倍!------Kernighan

4.库函数调用和系统调用区别何在:

答案:函数库调用是语言或应用程序的一部分,而系统调用是操作系统的一部分。

库函数调用通常比行内展开的代码慢,因为它需要付出函数调用的开销。但系统调用比库函数调用还要慢很多,因为它需要把上下文环境切换到内核模式。

 

2011.2.18

1.IT届面试的关键就在于正确理解问题!你需要仔细地听,如果不理解问题或者觉得它的定义不清,可以要求一个更好的解释。

2.Microsoft的绝大部分问题都想考察你在压力下能够怎样思考问题,但它们并不都是技术性的。

3.优秀的程序员将会休息得更好,精力更加充沛,而蹩脚的程序员则很可能困得脑袋和桌子打架。

4.每一位程序员都应该寻找并抓住每一次机会,使自己……

 

2011.2.28

代码量:66

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值