写在开篇的话:编程语言是规则的集合,语法只是这些规则的一部分,C/C++是允许扩展的,vc++的部分规则是微软扩展的,并不具有移植性;编程就是利用规则实现一个可行的方案;语言的不同在于设计的思想不同(前提是语言真的实现了该种设计思想),不同的想法,不同的设计,导致了不同的语言适用于不同的应用环境。
书到用时方恨少
输入---程序-->输出
- main函数,main函数是程序的入口函数,有且仅有一个,操作系统从启动代码(C RUNTIME)呼叫该函数,从而开始程序的执行。main函数可以返回一个值给操作系统,这个值可以在命令行通过“echo %errorlevel%”显示,这个值也可以用作bat文件中的判断条件。(本文基于windows操作系统,所使用的一些命令适用于windows平台)
- 一个程序从0到1分为几步:1)使用文本编辑器编写源代码2)通过预处理器替换头文件内容,替换宏等3)编译器编译源代码(.obj),这一步判断语法错误4)链接器链接对应的函数(二进制代码),包括标准库函数,并生成对应的可执行文件。如果使用IDE(如visual studio等),则所有的部分都集成在一个开发环境里面。一般会遇到的错误有,或者需要注意的点:1)头文件找不到,需要注意是否添加了头文件的包含路径。2)语法错误,这个明显,要注意的是API的使用规则。3)无法识别的外部符号,记得要添加lib。4)如果指定了预编译头文件,注意每个cpp文件都应该包含该预编译头文件。
- IO,IO并不是C++语法的一部分,IO是编译器实现厂商提供的标准库的一部分功能。C++规定了IO的函数名。但说到底,要清楚,IO是标准库函数的一部分。C++定义了4个标准的输入输出对象,cin,cout,cerr,clog;一般情况下,这些对象与程序窗口关联,但也可以重定向这些对象与文件对应起来。流的概念,流出去了就没有了,所以如果读取流,那么被读取的数据会从流中删除。
4.左右结合特性,想想赋值操作符,右结合特性,肯定是先把右边的数值算完再赋给左边的变量。左右结合特性有其合理性。
5.初始化,int i = 0; //赋值还是初始化?初始化,调用copy构造函数。int i(0);
6.if语句怎么判断,非0就是真,只有0为假,if的括号类可以是表达式,关于流的判断,如果if的条件是流,判断流的failbit/badbit是否被设置。(eofbit属于正常)ios::goodbit/eofbit/failbit/badbit - 0/1/2/4.
iostate value
(member constants) indicates functions to check state flags
good() eof() fail() bad() rdstate()
goodbit No errors (zero value iostate) true false false false goodbit
eofbit End-of-File reached on input operation false true false false eofbit
failbit Logical error on i/o operation false false true false failbit
badbit Read/writing error on i/o operation false false true true badbit
7.关于作用范围和生命周期:C++变量的作用范围分为这几种,全局作用域,局部作用域(函数或者代码块),语句作用域(for),(*静态作用域)。不同的作用域是允许覆盖的,也就是说,在不同的域可以定义相同名字的变量。小的作用域会屏蔽大的作用域的变量。类的作用域,类的非静态成员属于对象,必须通过对象来引用,静态成员属于类,通过类::直接引用。全局作用域的变量在程序结束时被释放,局部作用域在作用域结束后被释放掉,语句作用域在语句结束后释放掉。函数中定义静态变量,该变量存储在静态变量存储区域,程序结束时被释放。全局变量如果声明为static,则本文件可见。
静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。
栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
堆区:亦称动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。 但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。
- 基本上所有的语言都要提供下列特征:
• 内置数据类型,如整型、字符型等。算术类型:bool, char, wchar_t(字符类型), short, int, long, (long long?)除bool外这些类型是由符号的,定义无符号数,前面要加unsigned. unsigned int可以缺省写为unsigned。float,double, long double.
• 表达式和语句:表达式和语句用于操纵上述类型的值。
• 变量:程序员可以使用变量对所用的对象命名。
• 控制结构:如 if 或 while,程序员可以使用控制结构有条件地执行或重复执行一组动作。
• 函数:程序员可以使用函数把行为抽象成可调用的计算单元。
9.c++是静态类型语言,但不是类型安全语言(允许类型的强制转换)
10.不可打印字符,转义字符\r,\n,\t,\v,\b,\f,\a,\,?,',",\ooo八进制数\xddd十六进制数
11.变量的声明:加extern关键字.
12.const,这个关键字比较重要,注意函数可以基于const重载。但是注意,函数参数是值传递,也就是说,函数调用的时候,如果是实际的对象,函数会使用对象的副本(调用copy构造函数),这个时候const会歧义,从根上来说,有无const都无法修改原来的对象,所以无法基于这种情况重载;但是可以基于const指针和const引用重载,表明指针或引用指向的对象是const。关于类的const函数,const放在()后面,const修饰this指针,this指针是类非static成员的隐含参数,所以能够基于const重载,同时该const函数由const对象调用。const定义的全局对象默认是本文件可见,如果想全局可见,在最前面加extern(或者说非const全局对象默认是extern修饰的)。
const指针(int const p,需要初始化,不能让其指向别的对象)和指向const对象的指针(const int p,不能改变对象的值,可以先不初始化),主要看const在的前后,前就是对象是const,后就是const指针。注意typedef定义过的类型:typedef int intptr; const intptr p = ...;//必须初始化,因为这时候,p 类似于int* const p。const对象只能用const指针来指向。
const 允许出现在类型的前面或者后面,相同效果,但是*这个符号的前后,是有差别的。
13.指针和引用是复合类型,所谓复合类型是因为他们在其他类型基础上的一种类型。引用是对象别名,必须在定义时初始化(除函数的参数外),且无法修改其指向其他对象(注意是别名),使用别名实际上是用的是对象本身。const 引用允许是一个表达式的值,但是非const引用不行,因为const引用表明没有改变结果的可能性(编译器会在const变量使用处替换为该表达式),所以允许是一个中间的结果。但是非const引用需要改变对象的值,需要有对象存在。
14.隐式转化: double d = 1.2; int& i = d;//实际上中间会有一步 int temp = d; int& i = temp; //这有语义错误,所以这种定义是会有编译错误的。如果是const引用则不会有问题,因为const已经表明变量没有被修改的可能。所以非const引用只能绑定同类型的对象。
15.enum name{enum1,enum2};//注意enum跟class一样,定义了一种新的类型。其成员是常量,值默认从0开始,依次加1,可以自己定义某个成员的值,后面的成员从该成员后加1.
16.头文件,注意使用头文件保护符,以免重复包含。#ifndef #define #endif. 头文件主要用于声明,只有三个例外:类的定义,const对象(const对象仅本文件可见,属于文件的局部变量(.obj)),inline函数(如果多个头文件有相同的函数,必须定义相同)。inline函数因为需要在调用处展开,所以需要对编译器可见,所以需要源代码,inline放在头文件中的好处就在于此。inline函数可以在多个文件定义,但是必须函数体相同,但要保证在某个源文件只定义一次。(其实源文件编译为.obj,函数属于本文件可见。)在头文件定义函数并不是不可以,不明智,不科学。inline函数这样做有其现实意义。 - 函数的参数的值和返回值都会调用copy构造函数,除非使用引用。注意如果函数返回值类型是一个引用,注意不要返回函数中定义的临时变量。
(1) istream& getline (istream& is, string& str, char delim);
(2) istream& getline (istream& is, string& str);
- 链接指示 extern "C", 编译器按处理普通 C++ 函数一样的方式检查对外部语言函数的调用(返回值,参数列表),但是,编译器一般必须产生不同的代码来调用用其他语言编写的函数。extern "C" 可以修饰单个函数,也可以修饰多个函数,也可以修饰头文件(需要用{}括起来),也可以到处C++函数到其他语言,也是用extern "C"修饰,其他语言应该还有extern "Ada"等。注意C语言不支持函数重载,所以不要extern两个同名的重载函数。C和C++的函数指针也是不同的类型,不可以相互赋值。链接器到底怎么做呢?可以直接链接C的函数,反之不然?
20.字节对齐,struct的自己对齐,为了存取方便,一般编译器会将struct的变量放在其大小的整数倍的地址上,所以假设int成员前面有一个bool,那么bool会占用四个字节,然后才存储int,所以一般将bool放在一起比较好。union的大小等于单个元素所占空间最大的成员的最小倍数,这个空间要大于或等于最长的成员所占用的字节数。如 union myunion{int i,char c[4]}; 大小会是8.
21.操作符的结果是一个对象。操作符重载,必须有一个是类的对象。
22.copy构造函数,和赋值操作符,析构,默认构造函数,如果没有显式定义,则类会为我们定义一个。注意,赋值操作符,只有在非定义的时候调用,如果发生对象的定义,则会调用copy构造函数,而不会调用赋值操作符重载的函数。
23.const_cast(expression),一般用来定义一个非const指针或引用指向一个const指针或引用。
24.关于char** 不能转换到const char,是类型不能转换,I dont know why. 但是const char 是(p)指向的object的值不能变,p可以指向其他object。char(p),当然可以使用const_cast进行转换后赋值。
25.va_list //typedef char * ; va_start(ap,v) //((void)(ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v))); va_arg(ap,type) //((t)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))); va_end(ap);//((void)(ap = (va_list)0)). 在stdarg.h中定义。参数按4个字节对齐,所以_INTSIZEOF
26.string* s1,s2; s2 is string, not string. so which type of writing format you want to use. string s1,s2; I think it is better. 指针对象,也是对象。&(&i);//incorrect. &操作的对象必须是一个lvalue,但是(&i)返回的不是一个lvalue,是一个数值常量。[]下标操作符返回的是实际的对象,而不是指针。数组名可以看作数组的首指针。