C/C++学习笔记:基础知识3

来源:互联网 发布:精准扶贫数据造假 编辑:程序博客网 时间:2024/06/05 19:51

1    &&和&、||和|有什么区别

(1)&和|对操作数进行求值运算,&&和||只是判断逻辑关系。 

(2)&&和||在在判断左侧操作数就能确定结果的情况下就不再对右侧操作数求值。

注意:在编程的时候有些时候将&&或|| 替换成&或|没有出错,但是其逻辑是错误的,可能会导致不可预想的后果(比如当两个操作数一个是 1 另一个是 2 时)。


2 编程规范

编程规范可总结为:程序的可行性,可读性、可移植性以及可测试性。


C++的引用和 C 语言的指针有什么区别

指针和引用主要有以下区别:

(1)引用必须被初始化,但是不分配存储空间。指针不声明时初始化,在初始化的时候需要分配存储空间。

(2)引用初始化以后不能被改变,指针可以改变所指的对象。

(3)不存在指向空值的引用,但是存在指向空值的指针。

注意:引用作为函数参数时,会引发一定的问题,因为让引用作参数,目的就是想改变这个引用所指向地址的内容,而函数调用时传入的是实参,看不出函数的参数是正常变量,还是引用,因此可能会引发错误。所以使用时一定要小心谨慎。


4   写一个“标准”宏 MIN
这个宏输入两个参数并且返回较小的一个:

#define min(a,b)((a)<=(b)?(a):(b))

注意:在调用时一定要注意这个宏定义的副作用,如下调用: 
      ((++*p)<=(x)?(++*p):(x)
p 指针就自加了两次,违背了 MIN 的本意。


typedef 和 define 有什么区别

(1)用法不同:typedef 用来定义一种数据类型的别名,增强程序的可读性。define 主要用来定义常量,以及书写复杂使用频繁的宏。

(2)执行时间不同:typedef 是编译过程的一部分,有类型检查的功能。define 是宏定义,是预编译的部分,其发生在编译之前,只是简单的进行字符串的替换,不进行类型的检查。

(3)作用域不同:typedef 有作用域限定。define 不受作用域约束,只要是在 define 声明后的引用都是正确的。

(4)对指针的操作不同:t ypedef 和 define 定义的指针时有很大的区别。

注意:typedef 定义是语句,因为句尾要加上分号。而 define 不是语句,千万不能在句尾加分号。


6 关键字 const     static      extern 

(1)const 用来定义一个只读的变量或对象。主要优点:便于类型检查、同宏定义一样可以方便地进行参数的修改和调整、节省空间,避免不必要的内存分配、可为函数重载提供参考。

说明:const 修饰函数参数,是一种编程规范的要求,便于阅读,一看即知这个参数不能被改变,实现时不易出错。

(2)static 在 C 中主要用于定义全局静态变量、定义局部静态变量、定义静态函数。在 C++中新增了两种作用:定义静态数据成员、静态函数成员。

注意:因为 static 定义的变量分配在静态区,所以其定义的变量的默认值为 0,普通变量的默认值为随机数,在定义指针变量时要特别注意。

(3)extern 标识的变量或者函数声明其定义在别的文件中,提示编译器遇到此变量和函数时在其它模块中寻找其定义。


C++中const的作用:

(1)const用于定义常量:const定义的常量编译器可以对其进行数据静态类型安全检查

(2)const修饰函数形式参数:当输入参数为用户自定义类型和抽象数据类型时,将“值传递”改为“const & 传递”,提高效率。“引用传递”不需要产生临时对象,省了临时对象的构造、复制、析构过程消耗的时间。

(3)const修饰函数的返回值:如果给“指针传递”的函数返回值加上const,则返回值不能被直接修改,且返回值只能被赋值给const修饰的同类型指针。

(4)const修饰成员函数(函数定义体):任何不需要修改数据成员的函数都应该使用const修饰。const修饰类的成员函数的形式为: int getCount(void) const


C++ const数据存储位置

class GamePlayer {private:    static const int NumTurns = 5; // 常量声明式    int scores[NumTurns];          // 使用该常量    ...}

然而你所看到的是NumTurns的声明式而非定义式。通常 C++ 要求你对你所使用的任何东西提供一个定义式,但如果它是一个 class 专属常量又是static且为整数类型(intergral type,例如ints,chars,bools),则需特殊处理。只要不取他们的地址,你可以声明并使用它们而无须提供定义式。但如果你取某个 class 专属常量的地址,或纵使你不取其地址而你的编译器却(不正确地)坚持要看到一个定义式,你就必须另外提供定义式如下:

const int GamePlayer::NumTurns; // NumTurns 的定义;                                // 下面告诉你为什么给予数值[/code]

请把这个式子放进一个实现文件而非头文件。由于class常量已在声明时获得初值,因此定义时不可以再设初值。

——以上所有来自《Effective C++》第三版 P14


我不理解的是不取其地址的时候,可以访问NumTurns?,于是我代码进行验证:

#include <iostream>class GamePlayer {public:    static const int NumTurns = 5; // 常量声明式};const int GamePlayer::NumTurns;int main(){    std::cout &lt;&lt; GamePlayer::NumTurns &lt;&lt; std::endl;    // 如果没有加上 const int GamePlayer::NumTurns; 对 NumTurns 进行定义,下面这样语句会报错:    //   undefined reference to `GamePlayer::NumTurns    std::cout &lt;&lt; &amp;(GamePlayer::NumTurns) &lt;&lt; std::endl;    return 0;}

代码的验证结果和书中说的是一致的。那么问题就来了,NumTurns 没有定义的时候可以访问,却无地址,那它存储在什么地方呢?

  • 调试的时候会发现 GamePlayer::NumTurns 没有定义的时候,Watch 窗口找不到 GamePlayer::NumTurn s这个符号,而有定义了之后,Watch 窗口可以找到 GamePlayer::NumTurns 这个符号并且值为 5。
  • 对两次的代码进行反汇编,发现他们的反汇编代码是完全相同的。

于是我猜测,难道未定义时,编译器会把它当成宏来处理?只是进行了符号替换操作(我指的是狭义的工作方式,并不是指在预处理时候进行的替换),要不然怎么会没有地址呢?我网上查的时候看到一个CSDN的帖子内容挺好的。我将感觉能解释的通的几个点罗列了一下:

  • const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。
  • 编译器通常不为普通 cons t常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
  • C++ 中的 const 默认为内部连接,也就是说,const 仅在 const 被定义过的文件里才是可见的,而在连接时不能被其他编译单元看到。当定义一个 const 时,必须赋一个值给它,除非用 extern 作出了清楚的说明。
  • 通常 C++ 编译器并不为 const 创建存储空间,相反它把这个定义保存在它的符号表里。但是 extern 强制进行了存储空间分配(另外还有一些情况,如取一个 const 的地址,也要进行存储空间分配),由于 extern 意味着使用外部连接,因此必须分配存储空间,这也就是说有几个不同的编译单元应当能够引用它,所以它必须存储空间。[C++编程思想]
  • const int i=10;//这个类似宏替换,也就是说,它优化之后可能是放一个符号表里面。所有使用i的地方都用 10 代替,但是当你对 i 取址后,没办法,编译器必须为i在常量区找个地方安身。




C++中static的作用:

(1)在函数体,一个被声明为静态的变量在函数被调用的过程中维持其值不变。

(2)在模块内(在函数体外),一个被声明为静态的变量可以被模块内所有函数访问,但是不能被模块外的其他函数访问。它是一个本地的全局变量。

(3)在模块内,被声明为静态的函数只能被这一模块内的其他函数调用。即函数被限制在声明它的模块范围内。


static 全局变量与普通全局变量的区别是:static全局变量只初始化一次,防止其在其他文件单元中被引用。

static 局部变量和普通局部变量的区别是:static局部变量只是被初始化一次,下一次依据上一次结果值。

static 函数和普通函数的区别是:static函数在内存中只有一份,普通函数在每一个被调用中维持一份复制品。


类中的静态成员或者方法不属于类的实例,而属于类本身并在所有类的实例间共享。


指针常量与常量指针

(1)指针常量是指定义了一个指针,这个指针的值只能在定义时初始化,其他地方不能改变。

常量指针是指定义了一个指针,这个指针指向一个只读的对象,不能通过常量指针来改变这个对象的值。

(2)指针常量强调的是指针的不可改变性,而常量指针强调的是指针对其所指对象的不可改变性。

注意:无论是指针常量还是常量指针,其最大的用途就是作为函数的形式参数,保证实参在被调用函数中的不可改变特性。

const char *p    // (也可以写成 “char const *p”) 声明了一个指向字符常量的指针, 因此不能改变它所指向的字符; char * const p   // 声明一个指向 (可变) 字符的指针常量, 就是说, 你不能修改指针


“野指针”

“野指针”产生原因及解决办法如下:

(1)指针变量声明时没有被初始化。解决办法:指针声明时初始化,可以是具体的地址值,也可让它指向 NULL。

(2)指针  p  被  free   或者  delete  之后,没有置为  NULL。解决办法:指针指向的内存空间被释放后指针应该指向 NULL。

(3)指针操作超越了变量的作用范围。解决办法:在变量的作用域结束前释放掉变量的地址空间并且让指针指向 NULL。

注意:“野指针”的解决方法也是编程规范的基本原则,平时使用指针时一定要避免产生“野指针”,在使用指针前一定要检验指针的合法性。


常引用

常引用的引入主要是为了避免使用变量的引用时,在不知情的情况下改变变量的值。常引用主要用于定义一个普通变量的只读属性的别名、作为函数的传入形参,避免实参在调用函数中被意外的改变。

说明:很多情况下,需要用常引用做形参,被引用对象等效于常对象, 不能在函数中改变实参的值,这样的好处是有较高的易读性和较小的出错率。



0 0
原创粉丝点击