MyException - 我的异常网
当前位置:我的异常网» C++ » Tips for C++ Primer Chapter 二

Tips for C++ Primer Chapter 二

www.MyException.Cn  网友分享于:2013-10-11  浏览:0次
Tips for C++ Primer Chapter 2

第2章 变量和基本类型

基本内置类型

基本内置类型:算术类型和空类型;算术类型又包括整型(字符和布尔类型也包括在内)和浮点型。

 

可寻址的最小内存块称为“字节”(byte),存储的基本单元称为“字”(word);一个字通常由几个字节组成。

1字节(byte)=8比特(bit),比特非0即1。

 

类型unsigned int可以缩写为unsigned。

 

int与singed int相同(除了布尔型和扩展字类型(例如宽字符wchat_t)之外,其他的整型类型(例如short、long)也类似),但字符型例外,字符型有char、unsigned char、signed char三种类型,但char与signed char不相同;字符的表现形式只有两种:带符号的和无符号的,char实际上会表现为上述两者的其中一种,具体由编译器决定。

 

在算数表达式中不要使用char或bool。因为char在一些机器上可能是有符号的,而在另外的机器上可能是无符号的,使用char进行运算可能出错;如果硬要使用一个不大的整数,那么明确指定它为unsigned char或signed char。bool取值非0即1,不适宜用于算数表达式。

 

当赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值的总数取模后的余数。

例如:unsigned char占8个比特(1个字节),可以表示[0, 255]的共256个数值,当赋予区间外的一个值,假设是-1,则实际结果是(-1) % 256 = 255。

 

当赋给一个带符号类型一个超出它表示范围的值时,结果是未定义的,此时程序可能继续工作、崩溃或产生垃圾数据。

 

当一个算数表达式中既有无符号数又有int值时,那个int值会转换成无符号数。把int转换成无符号数的过程相当于把int赋给无符号类型。

切勿混用带符号类型和无符号类型。如果表达式既有带符号类型又有无符号类型,当带符号类型取值为负时会出现异常结果,这是因为带符号数会自动转换成无符号数。

例如:当a=-1,b=1,c=a*b,若a、b都是int,则c为-1;若a是int,b是unsigned int,则c的结果是(-1)%(2^32) * 1 = 4294967295(视当前机器上int所占bit数而定,这里假设int是32bit)。

 

整型字面值可写作十进制数、八进制数、十六进制数的形式。

0开头:八进制

0x、0X开头:十六进制

 

浮点数字面值可省略整数部分或小数部分。

例如:0.、.001

浮点数字面值默认是一个double。

 

可以以前缀或后缀指定字面值类型。

前缀

  u:char16_t(Unicode16字符)

  U:char32_t(Unicode32字符)

  L:wchar_t(宽字符)

  u8:char(UTF-8、仅用于字符串字面常量)

后缀

对整型字面值:

  u、U:unsigned

  l、L:long

  ll、LL:long long

对浮点型字面值:

  f、F:float

  l、L:long double

 

变量

初始化与赋值是不同的概念。

初始化:创建变量时赋予其一个初始值

赋值:把对象的当前值擦除,并以一个新值替代

所以赋值的开销大于初始化

 

默认初始化规则

如果内置类型的变量未被显式初始化,它的值由定义的位置决定。

定义在任何函数体外:被初始化为0

定义在函数体内:将不被初始化,其值未定义

注:指针类型并非基本内置类型,但也有上述特性。

如果类的对象没有显示初始化,其值由类确定。(通过类的默认构造函数类内初始值等)

 

声明使得名字为程序所知,而定义创建与名字关联的实体。

如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而不要显式地初始化变量。

例如:

extern int i; //声明i而非定义i

int j; //声明并定义j

 

包含显式初始化的声明即成为定义。

extern double pi = 3.14; //定义;允许在函数体外这么做,但extern关键字的存在就没意义了

不允许在函数体内部初始化一个由extern关键字标记的变量。

 

复合类型

引用本身并非对象,也没有实际的地址(若尝试用取地址符&取得某个“引用的地址”,实际取得的是所引用的对象的地址),它只是已存在的对象的别名。无法定义引用的引用,也无法定义指向引用的指针。

引用必须初始化,因为无法令引用重新绑定到另一对象。

所有引用的类型都要和与之绑定的对象严格匹配(有两个情况例外,将在后面提到)。

 

引用必须绑定到对象上,不能绑定到字面值或某个表达式的计算结果。

(可以将引用绑定到const对象上,就像绑定到其它的对象上一样,称之为“对常量的引用”。与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象。)

 

指针本身是一个对象,允许对指针赋值和拷贝,指针在其生命周期内可以指向不同对象。

指针无需在定义时赋初值。和基本内置类型相同,定义在所有函数体外的指针若未初始化,将有默认初值0,而块作用域内定义的指针将拥有一个不确定的值。

所有指针的类型都要和所指的对象严格匹配(有两个情况例外,将在后面提到)。

 

&、*出现在声明语句中,用来组成复合类型;&出现在表达式中,是一个取地址符;*出现在表达式中,是一个解引用符(或乘法运算符)。

 

nullptr = NULL = 0

 

void*是一种特殊的指针类型,可用于存放任意对象的地址。但不能直接操作void*指针所指的对象,因为对象的类型决定了能对对象所做的操作,而我们并不知道对象的类型。

 

指向指针的指针

指针是内存中的对象,像其它对象一样也有自己的地址,因此允许把指针的地址再存放到另一个指针当中。

通过*的个数区分指针的级别。**是指向指针的指针,***是指向指针的指针的指针,等等。

int i = 1024;

int *pi = &i; //pi指向一个int型的数

int *ppi = π //ppi指向一个int型的指针

解引用int型指针会得到一个int型的数,解引用一个指向指针的指针会得到一个指针。

这里有三种方式取得i的值:i、*pi、**ppi。

 

指向指针的引用

int i = 1;

int *p; // p是一个int型指针

int *&r = p; // r是一个对指针p的引用

r = &i; // 因为r是对指针p的引用,即r是p的别名,因此给r赋值&i就是令p指向i

*r = 0; // 解引用r得到i,也就是p指向的对象,因此i的值将改为0

 

const限定符

const对象一旦创建后其值就不能再改变,所以const对象必须初始化(初始值允许是任意的表达式,不一定是字面量常量)。

const int i = get_size(); // i将在运行时初始化

const int j = 1; // j将在编译时初始化

const int k; // 错误,k没有初始化

 

默认状态下,const对象仅在文件内有效;如果想在多个文件共享const对象,必须在变量的定义前添加extern关键字。

当多个文件出现了同名的const对象时,其实等同于在不同文件中分别定义了独立的变量。

如果想要:只在一个文件中定义const对象,而在其它多个文件中声明并使用它,则:对于const变量不管是声明还是定义都添加extern关键字,这样只需定义一次就够了。

【file_1.cc】

int get_size() {...}

extern const int bufSize = get_size(); //定义并初始化了一个常量(extern表示bufSize能被其它文件访问)

【file_1.h】

extern const int bufSize; //只是声明,这个bufSize与file_1.cc中定义的bufSize是同一个(extern表示bufSize并非本文件所有,它的定义在别处出现)

 

对const的引用

可以将引用绑定到const对象上,就像绑定到其它的对象上一样,称之为“对常量的引用”(“reference to const”,或“对const的引用”,或“常量引用”)。与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象。

const int ci = 1;

const int &r1 = ci; //正确;引用及其对应的对象都是常量

r1 = 2; //错误;ri是对常量的引用

int &r2 = ci; //错误;试图让一个非常量引用指向一个常量对象(设想:若这条语句合法,即默认“允许通过r2来修改ci的值”,而ci是常量,显然矛盾)

 

对const的引用与初始化

前面提到引用的类型必须与其所引用的对象一致,但是有两个例外。这里说它的第一种例外:

在初始化常量引用时,允许用任意表达式作为初始值(只要该表达式的结果能转换成引用的类型即可);

尤其,允许为一个常量引用绑定非常量的对象、字面值,或者一般的表达式。

int i = 1;

const int &r1 = i; //允许将const int&绑定到一个普通int对象上

const int &r2 = 1; //正确;r2是一个常量引用

const int &r3 = r1 * 2; //正确;r3是一个常量引用

int &r4 = r1 * 2; //错误;r4是一个普通的非常量引用,不允许绑定到字面值或某个表达式的计算结果

 

看看当一个常量引用被绑定到另外一种类型(合法)时发生了什么:

double dval = 3.14;

const int &ri = dval; //合法;ri值为3

ri引用了一个int型的数,但dval却是一个double型的浮点数而非整数。因此为了确保让ri绑定一个整数,编译器把上述代码变成了:

const int temp = dval; //由double型浮点数生成一个临时的int型整数常量

const &ri = temp; //让ri绑定这个临时量(临时量是临时量对象的简称,是内存中的一个未命名对象)

 

顺便,对前例,探讨一下当ri不是常量(非法)时会发生什么:

如果ri不是常量,意味着允许对ri赋值(或者说通过ri修改其绑定的对象)。上例谈到,ri绑定到了一个临时量,而非dval。

我们既然让ri引用dval,肯定想通过ri改变dval的值,所以不会想要把引用绑定到临时量上,所以,C++认为“普通的非常量指针去绑定另外一种类型”的行为是非法的。

 

对const的引用可能引用一个并非const的对象

对const的引用的机制是:不允许通过引用来修改所引用的对象的值,但所引用的对象的值可通过其它途径修改。

int i = 1;

int &r1 = i; //引用r1绑定非const的对象i

const int &r2 = i; //r2也绑定对象i

r1 = 0; //合法;i的值修改为0

r2 = 0; //非法;r2是一个常量引用,不允许通过r2修改i的值

修改i的值的“其它途径”可以是各种合法的途径,例如直接对i赋值。

 

指针和const

与引用一样,也可以令指针指向常量或非常量。

类似于常量引用,指向常量的指针(pointer to const)不能用于改变其所指对象的值。

要想存放常量对象的地址,只能使用指向常量的指针。

const double pi = 3.14;

double *ptr = π //非法;ptr是一个普通指针

const double *cptr = π //合法

*cptr = 1.0; //非法

 

前面提到,指针的类型必须与其所指的对象的类型一致,但是有两个例外。这里先讨论第一种例外:

允许令一个指向常量的指针指向一个非常量对象。

double dval = 3.14; //dval是一个非常量对象

const double *cptr = &dval; //合法;但是不能通过cptr改变dval的值(与指向常量的引用类似,这里的dval仍然可能通过其它途径修改)

 

const指针(常量指针)

常量指针的意义是指针本身是常量。

注:注意“指向常量的指针”与“常量指针”相区别。前者机制是:不能通过指针改变所指对象的值;后者的机制是:不能改变指针本身的值(也就是存放在指针中的那个地址)。

注:在讨论“对const的引用”(reference to const,对常量的引用)时,说“对const的引用”简称“常量引用”,但严格来说并不存在“常量引用”。因为引用并不是一个对象,所以没办法让引用“恒定不变”。事实上,由于C++不允许改变引用所绑定的对象,所以从这层意义上理解,所有的引用都算是“常量”。所以以“常量引用”代称“对const的引用”未尝不可。

const double pi = 3.14;  //pi是一个常量对象

const double *const pip = π //pip是一个指向常量对象的常量指针

从右往左,离pip最近的那个const说明pip本身是一个常量对象,对象的类型由声明符的其余部分决定。声明符中的下一个符号是*,说明pip是一个常量指针,最后const double确定了常量指针指向的是一个double型常量对象。

 

顶层const(top-level const)与底层const(low-level const)

当仅对指针而言:

顶层const:指针本身是个常量

底层const:指针所指的对象是一个常量

更一般的:

顶层const可以表示任意对象是常量,这一点对任何数据类型都适用,比如算数类型、类、指针等;

底层const则与指针和引用等复合类型的基本类型部分有关。

特殊的是:

指针既可以是顶层const也可以是底层const;用于声明引用的const都是底层const。

int i = 0;

int *const p1 = &i; //不能改变p1的值,顶层const

const int ci = 1; //不能改变ci的值,顶层const

const int *p2 = &ci; //允许改变p2的值,底层const

const int *const p3  = p2; //靠右的是顶层const,靠左的是底层const

const int &r = ci; //用于声明引用的const都是底层const

 

当执行对象的拷贝操作时,常量的顶层const不需要被考虑,因为拷贝操作不改变被拷贝对象的值,因此,拷入(指的是对常量对象的初始化时的拷贝动作,当然,初始化完成后就不能对常量对象有拷入动作了)和拷出的对象是否是常量都没什么影响。(例如上述示例的倒数第二条语句)

底层const的限制不能被忽略,拷入和拷出的对象必须有相同的底层const资格,或者两个对象的数据类型能够转换。(非常量可以转换成常量,反之不行)

int *p = p3; //非法;p3包含底层const定义,而p没有

p2 = p3; //合法;p2、p3都是底层const

p2 = &i; //合法;int*能转换成const int*(非常量可以转换成常量,反之不行)

int &r = ci; //非法;普通的int&不能绑定到int常量上

const int &r2 = i; //合法;const int&可以绑定到一个普通int上

 思考一下:为何“非常量可以转换成常量,反之不行”?

看第一条语句(非法:常量不可转为非常量),假设第一条语句合法,也就是说,假设允许以p3初始化p,看会发生什么:

p3包含底层const定义,即p3指向的对象是常量;而p不包含底层const定义,p指向的是一个普通的(非常量)整数,也就是说:“可能通过p修改它所指的对象的值”,但所指的对象是常量,这是矛盾的。

 

constexpr和常量表达式

常量表达式(const expression):值不会改变,且在编译过程就能得到计算结果的表达式。

const int i = 1; //i是常量表达式

const int j = i + 1; //j是常量表达式

int k = 2; //k不是常量表达式(值可能改变)

const int sz = get_size(); //sz不是常量表达式(在运行过程才能得到具体值)

 

constexpr变量

C++11可以用constexpr来明确声明常量表达式。

constexpr int ce = 1; //ok

constexpr int sz = size(); //仅当size()函数是一个constexpr函数时才是一条合法语句

 

字面值类型

字面值类型:包含算术类型、引用、指针、字面值常量类、枚举类型。

字面值类型可以定义成constexpr。

 

字面值类型定义constexpr的特殊情况

指针和引用定义成constexpr时,它们的初始值有严格限制:

关于指针:

一个constexpr指针的初始值必须是0(nullptr),或者是存储于某个固定地址中的对象;

  故constexpr指针不能指向函数体内定义的变量,可以指向函数体外的变量;

  函数体内的static local object局部静态对象与函数体外的对象一样有固定地址,故constexpr指针能指向局部静态对象。

在constexpr的声明中如果定义了一个指针,限定符constexpr仅对指针有效,而与指针所指的对象无关;

  const int *p = nullptr; //p是一个指向整型常量指针(p本身可被改变)

  constexpr int *q = nullptr; //q是一个指向整数常量指针(q本身不可被改变)

  原因是:constexpr把它所定义的对象置为了顶层const。

与const指针类似,constexpr指针既可以指向常量,也可以指向非常量。(指向非常量时,必须是存储于某个固定地址中的对象)

  constexpr int i = 1; //i是整型常量(i必须定义在函数体外,否则编译错误)

  constexpr const int *p = &i; //p是常量指针,指向整型常量i

  注意这个特殊写法,若以const限定符声明常量指针,const的位置紧邻p的左边:const int *const p = &i;

  而这里由于constexpr的特殊性(限定符constexpr仅对指针有效)而有其特殊语法:constexpr const int *p = &i。

 

关于引用:

constexpr引用只能绑定到局部静态对象。

 

处理类型

类型别名

方式一:使用关键字typedef。

  typedef double wages; //wages是double的同义词

方式二:(C++11)使用别名声明(alias declaration)

  using wages = double;

 

指针、常量和类型别名

如果某个类型别名指代的是复合类型或常量,那么把它用到声明语句里会产生意想不到的后果。

  typedef char *pstring; //类型pstring是类型char*的别名

  const pstring cstr = 0; //cstr是指向char的常量指针(指针本身是常量)

  const pstring *ps; //ps是一个指针,它所指的对象是一个指向char的常量指针

  上述两条语句的基本数据类型都是const pstring,const是对类型pstring的修饰,类型pstring是指向char的指针,因此const pstring就是指向char的常量指针而非指向常量字符对象的指针

一个错误的理解方式:

  尝试把类型别名替换成它本来的样子,以理解该语句的含义。(这种方式是错误的)

  尝试把pstring替换成char*,有:

  const char *cstr = 0;

  这样看来,*成了声明符的一部分,用以说明cstr是一个指针,const char成了基本数据类型,const是对类型char的修饰,这条语句的含义就是“指向const char的(普通)指针”。这与替换前的实际意义截然不同。

 

auto类型说明符

使用auto也允许在一条语句中声明多个变量,但是,因为一条语句只能有一个基本数据类型,所以该语句中的所有变量的初始基本数据类型都必须相同。

auto i = 0, *p = &i; //合法;i是int型、p是int型指针

auto sz = 0, pi = 3.14; //非法;sz是int,pi是double,类型不一致

const int ci = i;

auto &n = i, *p2 = &ci; //非法;i的类型是int,而ci的类型是const int

 

复合类型、常量和auto

编译器推断出来的auto类型有时候和初始值的类型并不完全一样,编译器会适当地改变结果类型使其更符合初始化规则。

例如:

使用引用其实就是使用引用的对象,特别是当引用作为初始值时,真正参与初始化的其实是引用对象的值。此时编译器以引用对象的类型作为auto的类型。

int i = 0, &ri = i;

auto a = ri; //a是一个整数,因为ri是i的别名,而i是一个整数

 

其次,auto一般会忽略顶层const,保留底层const。(后面会谈到特例)

例如:

当初始值是一个指向常量的指针时:

const int ci = i, &cr = ci; //ci是一个常量,cr是对常量的引用

auto b = ci; //b是一个整数(非const),因为ci的顶层const特性被忽略了

auto c = cr; //c是一个整数(非const),因为cr是ci的别名,ci本身是一个顶层const,而顶层const被忽略了

auto d = &i; //d是一个整型指针,因为一个整数的地址也就是指向整数的指针

auto e = &ci; //e是一个指向整数常量(const)的指针,因为ci是一个常量对象,而对常量对象取地址是一种底层const,且auto会保留底层cons

 

如果希望推断出的auto类型是一个顶层const,需要明确指出:

const auto f = ci; //ci的推演类型为int(而非const int),但明确指出后,f是const int

 

前面说到“auto一般会忽略顶层const”,此例即为特例:

当设置一个类型为auto引用时,初始值的顶层const仍然保留。

auto &g = ci; //g绑定到ci,g是一个整型常量引用(reference to const,对const的引用),因为ci的顶层const特性被保留了

 

decltype类型指示符

decltype:编译器分析表达式并得到它的类型,却不实际计算表达式的值。

decltype(f()) sum = x; //sum的类型被设定为函数f的返回类型(注意:编译器并不实际调用函数f)

 

decltype处理顶层const和引用的方式与auto不同。如果decltype使用的表达式是一个变量,则decltype返回该变量的类型(包括顶层const和引用在内)。

const int ci = 0, &cj = ci;

decltype(ci) x = 0; //x的类型被设定为const int

decltype(cj) y = x; //y的类型是const int&,y绑定到变量x

decltype(cj) z; //非法;z的类型是const int&,是一个引用,引用必须初始化

 

引用从来都作为其所指对象的同义词出现,只有用在decltype处是一个例外。

也就是说:不要因为cj引用ci,就认为“decltype(cj) y = x”等同于“decltype(ci) y = x”。

 

decltype和引用

如果decltype使用的表达式不是一个变量,则decltype返回表达式结果的对应类型。

int i = 1, *p = &i, &r = i;

decltype(r+0) b; //合法;加法的结果是int,因此b是一个为未始化的int

decltype(*p) c; //非法;c是int&(下面会解释),必须初始化

解释:因为r是一个引用,所以decltype(r)的结果是引用类型(而不是引用所指对象的类型)。如果想让结果是r所指对象的类型,可以把r作为表达式的一部分,如r+0,显然这个表达式的结果是一个具体值而非一个引用;

如果表达式的内容是解引用操作,则decltype将得到引用类型。解引用指针可以得到指针所指的对象,而且还能给这个对象赋值。因此,decltype(*p)的结果是int&,而非int。

 

变量给变量加上一层或多层括号,编译器就会把它当作一个表达式。变量是一个可以作为赋值语句左值的特殊表达式,所以这样的decltype就会得到引用类型。

int i = 1;

decltype(i) d; //合法;d是一个(未初始化的)int

decltype((i)) e; //非法;e是int&,必须初始化

总结:decltype((variable))的结果永远是引用,而decltype(variable)的结果仅当variable本身就是一个引用的时候才是引用。

 

补充例子:

赋值是会产生引用的一类表达式,引用的类型就是左值的类型。例如:如果i是int,则表达式i=x的类型是int&。

int a = 3, b = 4;

decltype(a) c = a; //c是int,值为3

decltype(a=b) d = a; //d是int&,值为3

d的值为何不是4?

由于“a=b”,故a的值变为4,而d是a的引用,故d的值是4?这是错误的,“a=b”只是用于编译器推断表达式的类型,实际上该表达式并不会真的执行,所以a的值仍旧是它原来的值3。

 

自定义数据结构

C++11允许为类内数据成员提供一个类内初始值(in-class initializer)。创建对象时,类内初始值将用于初始化数据成员。没有初始值的成员将被默认初始化(前面讨论过各种变量的默认初始化的规则)。

struct Sales_data

{

  std::string bookNo; //将会进行默认初始化;将会初始化为空字符串

  unsigned sold = 0; //将由类内初始值初始化;将会初始化为0

  double revenue; //将会进行默认初始化(结果:其值未定义,可能是乱七八糟的值)

};

 

头文件保护符

【Sales_data.h】(类通常定义在头文件中,头文件的名字应与类名相同)

#ifndef SALES_DATA_H

#define SALES_DATA_H

//#include <...>

struct Sales_data

{

  //...

};

#endif

防止头文件被多次include,避免重复定义。

预处理指令#ifdef用于判断给定预处理变量是否已经定义;#ifndef用于判断给定预处理变量是否尚未定义。

 

文章评论

程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
那些争议最大的编程观点
那些争议最大的编程观点
一个程序员的时间管理
一个程序员的时间管理
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
编程语言是女人
编程语言是女人
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
程序员和编码员之间的区别
程序员和编码员之间的区别
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
程序员应该关注的一些事儿
程序员应该关注的一些事儿
程序员都该阅读的书
程序员都该阅读的书
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
为什么程序员都是夜猫子
为什么程序员都是夜猫子
每天工作4小时的程序员
每天工作4小时的程序员
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
中美印日四国程序员比较
中美印日四国程序员比较
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
代码女神横空出世
代码女神横空出世
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
鲜为人知的编程真相
鲜为人知的编程真相
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
程序员的鄙视链
程序员的鄙视链
我是如何打败拖延症的
我是如何打败拖延症的
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
漫画:程序员的工作
漫画:程序员的工作
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
Java程序员必看电影
Java程序员必看电影
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
程序员必看的十大电影
程序员必看的十大电影
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
我的丈夫是个程序员
我的丈夫是个程序员
10个调试和排错的小建议
10个调试和排错的小建议
总结2014中国互联网十大段子
总结2014中国互联网十大段子
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
如何成为一名黑客
如何成为一名黑客
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有