《C和C++程序员面试秘笈[精品]》-笔记
Posted 赤龙绕月
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《C和C++程序员面试秘笈[精品]》-笔记相关的知识,希望对你有一定的参考价值。
2015-12-16原文:在C++中可以通过域操作符“::”来直接操作全局变量
2015-12-16
原文:后缀式(i++)必须返回对象的值,所以导致在大对象的时候产生了较大的复制开销,引起效率降低。因此处理使用者自定义类型(注意不是指内建类型)的时候,应该尽可能地使用前缀式递增/递减,因为它天生“体质”较佳。
2015-12-16
原文:内建数据类型的情况,效率没有区别。 自定义数据类型的情况,++i效率较高。
2015-12-16
原文:当表达式中存在有符号类型和无符号类型时,所有的操作数都自动转换成无符号类型
2015-12-18
原文:按位异或运算符“^”的功能是将参与运算的两数各对应的二进制位相异或,如果对应的二进制位相同,则结果为0,否则结果为1
2015-12-22
原文:C是一个结构化语言,它的重点在于算法和数据结构。对语言本身而言,C是C++的子集。C程序的设计首要考虑的是如何通过一个过程,对输入进行运算处理,得到输出。对于C++,首要考虑的是如何构造一个对象模型,让这个模型能够配合对应的问题,这样就可以通过获取对象的状态信息得到输出或实现过程控制。 因此,C与C++的最大区别在于,它们用于解决问题的思想方法不一样。
2015-12-22
原文:C 是面向过程化的,但是 C++不是完全面向对象化的。在 C++中也完全可以写出与 C一样过程化的程序,所以只能说C++拥有面向对象的特性。Java是真正面向对象化的。
2015-12-24
原文:很多时候,我们需要在程序退出的时候做一些诸如释放资源的操作,但程序退出的方式有很多种,例如main()函数运行结束,在程序的某个地方用exit()结束程序,用户通过Ctrl+C等操作发信号来终止程序,等等,因此需要有一种与程序退出方式无关的方法来进行程序退出时的必要处理。方法就是用atexit()函数来注册程序正常终止时要被调用的函数。 atexit()函数的参数是一个函数指针,函数指针指向一个没有参数也没有返回值的函数。
2015-12-24
原文:可以用 atexit()函数来注册程序正常终止时要被调用的函数,并且在 main()函数结束时,调用这些函数的顺序与注册它们的顺序相反。
2015-12-25
原文:宏只是简单的文本替换
2015-12-25
原文:使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起。
2015-12-25
原文:1 #define WORD_LO(xxx) ((byte) ((word)(xxx) & 255)) 2 #define WORD_HI(xxx) ((byte) ((word)(xxx) >> 8)) 一个字由两个字节组成。因此WORD_LO(xxx)取参数xxx的低8位,WORD_HI(xxx)取参数xxx的高8位。
2015-12-25
原文:#define ARR_SIZE(a) (sizeof((a)) / sizeof((a[0]))) 假设一个数组定义如下。 int array[100]; 它含有100个int型的元素。如果int为4个字节,那么这个数组总共有400个字节。sizeof(array)为总大小,即400个字节;sizeof(array[0])为一个int大小,即4个字节。两个大小相除就是 100,也就是数组的元素个数。这里,为了保证宏定义不会发生“二义性”,在a以及a[0]上都加了括号。
2015-12-31
原文:a1定义为const int*类型,注意这里的const在int*的左侧,它是用修饰指针所指向的变量,即指针指向为常量
2015-12-31
原文:a2定义为int* const类型,注意这里的const在int*的右侧,它是用修饰指针本身,即指针本身为常量
2015-12-31
原文:a3 定义为const int* const类型,这里有两个const,分别出现在int*的左右两侧,因此它表示不仅指针本身不能修改,并且其指向的内容也不能修改
2016-01-04
原文:#define常量则是一个Compile-Time概念,它的生命周期止于编译期,它存在于程序的代码段,在实际程序中它只是一个常数、一个命令中的参数,并没有实际的存在。 const常量存在于程序的数据段,并在堆栈分配了空间。const常量是一个Run-Time的概念,它在程序中确确实实地存在着并可以被调用、传递。const常量有数据类型,而宏常量没有数据类型。编译器可以对const常量进行类型安全检查。
2016-01-04
原文:C++中const有什么作用(至少说出3个)
2016-01-04
原文:(1)const用于定义常量:const定义的常量编译器可以对其进行数据静态类型安全检查。
2016-01-04
原文:(2)const修饰函数形式参数:当输入参数为用户自定义类型和抽象数据类型时,应该将“值传递”改为“const &传递”,可以提高效率。比较下面两段代码: 1 void fun(A a); 2 void fun(A const &a); 第一个函数效率低。函数体内产生A类型的临时对象用于复制参数a,临时对象的构造、复制、析构过程都将消耗时间。而第二个函数提高了效率。用“引用传递”不需要产生临时对象,节省了临时对象的构造、复制、析构过程消耗的时间。但光用引用有可能改变a,所以加const。
2016-01-04
原文:(3)const修饰函数的返回值:如给“指针传递”的函数返回值加const,则返回值不能被直接修改,且该返回值只能被赋值给加const修饰的同类型指针。例如。 1 const char *GetChar(void){}; 2 char *ch = GetChar(); // error 3 const char *ch = GetChar(); // correct
2016-01-04
原文:(4)const修饰类的成员函数(函数定义体):任何不会修改数据成员的函数都应用const修饰,这样,当不小心修改了数据成员或调用了非const成员函数时,编译器都会报错。const修饰类的成员函数形式为: 1 int GetCount(void) const;
2016-01-05
原文:在C语言中,关键字static有3个明显的作用: (1)在函数体,一个被声明为静态的变量在这一函数被调用的过程中维持其值不变。 (2)在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所有函数访问,但不能被模块外其他函数访问。它是一个本地的全局变量。 (3)在模块内,一个被声明为静态的函数只可被这一模块内的其他函数调用。那就是这个函数被限制在声明它的模块的本地范围内使用。
2016-01-05
原文:static 全局变量与普通的全局变量有什么区别?static 局部变量和普通的局部变量有什么区别?static函数与普通函数有什么区别?
2016-01-05
原文:全局变量的说明之前再加上static就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别在于,非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的;而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其他源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其他源文件中引起错误。
2016-01-05
原文:从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式,即改变了它的生存期;把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
2016-01-05
原文:static 函数与普通函数的作用域不同。static函数的作用域仅在本文件,只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件。 因此,static全局变量与普通的全局变量的区别是,static全局变量只初始化一次,防止在其他文件单元中被引用;static局部变量和普通局部变量的区别是,static局部变量只被初始化一次,下一次依据上一次结果值;static函数与普通函数的区别是,static函数在内存中只有一份,普通函数在每个被调用中维持一份复制品。
2016-01-05
原文:static全局变量与普通全局变量的区别是,static全局变量只初始化一次,防止在其他文件单元中被引用。 static局部变量和普通局部变量的区别是,static局部变量只被初始化一次,下一次依据上一次结果值。 static函数与普通函数的区别是,static函数在内存中只有一份,普通函数在每个被调用中维持一份复制品。
2016-01-06
原文:类中的静态成员或方法不属于类的实例,而属于类本身并在所有类的实例间共享。在调用它们时应该用类名加上操作符“::”来引用。
2016-01-06
原文:对数组变量做 sizeof 运算得到的是数组占用内存的总大小。在这里,str的总大小为strlen("Hello")+1,注意数组中要有一个元素保存字符串结束符
2016-01-06
原文:这是因为当我们调用函数 Func(str)时,由于数组是“传址”的,程序会在栈上分配一个4字节的指针来指向数组,因此结果也是4。
2016-01-06
原文:总之,数组和指针的 sizeof 运算有细微的区别。如果数组变量被传入函数中做 sizeof运算,则和指针的运算没有区别,否则得到整个数组占用内存的总大小。对于指针,无论是何种类型的指针,其大小都是固定的,在32位WinNT平台下都是4。
2016-01-12
原文:字节对齐引起的。对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是,如果不按照适合其平台的要求对数据存放进行对齐,会给存取效率带来损失。例如,有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)存放在偶地址开始的地方,那么一个读周期就可以读出;而如果存放在奇地址开始的地方,可能会需要 2 个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int型数据。显然,在读取效率上下降很多。这也是空间和时间的博弈。
2016-01-12
原文:字节对齐的细节和编译器实现相关,一般而言,需要满足3个准则: 结构体变量的首地址能够被其最宽基本类型成员的大小所整除; 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要,编译器会在成员之间加上填充字节(internal adding); 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。
2016-01-12
原文:现在以下面的结构体为例。 1 struct S 2 { 3 char c1; 4 int i; 5 char c2; 6 }; c1的偏移量为0,i的偏移量为4,c1与i之间便需要3个填充字节。c2的偏移量为8,加起来就是1+3+4+1,等于9个字节。由于这里最宽的基本类型为int,大小为4个字节,再补3个字节凑成4的倍数。这样一共是12个字节。
2016-01-14
原文:普通函数不占用内存,只要有虚函数,就会占用一个指针大小的内存,原因是系统多用了一个指针维护这个类的虚函数表,并且注意这个虚函数无论含有多少项(类中含有多少个虚函数)都不会再影响类的大小。
2016-01-14
原文:使用sizeof计算虚拟继承的类对象的空间大小 出现频率:★★★★ 1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 }; 7 8 class B 9 { 10 }; 11 12 class C:public A, public B 13 { 14 }; 15
2016-01-14
原文:16 class D:virtual public A 17 { 18 }; 19 20 class E:virtual public A, virtual public B 21 { 22 }; 23
2016-01-14
原文:24 class F 25 { 26 public: 27 int a; 28 static int b; 29 } 30 31 int F::b = 10; 32 33 int main() 34 { 35 cout << "sizeof(A) = " << sizeof(A) << endl; 36 cout << "sizeof(B) = " << sizeof(B) << endl; 37 cout << "sizeof(C) = " << sizeof(C) << endl; 38 cout << "sizeof(D) = " << sizeof(D) << endl; 39 cout << "sizeof(E) = " << sizeof(E) << endl; 40 cout << "sizeof(F) = " << sizeof(F) << endl;
2016-01-14
原文:42 return 0; 43 } 【解析】 程序说明如下。 代码第 35 行,由于 A 是空类,编译器会安插一个 char 给空类,用来标记它的每一个对象。因此其大小为1个字节。 代码第36 行,类B 大小和A 相同,都是1 个字节。 代码第37 行,类C 是多重继承自A 和B,其大小仍然为1 个字节。
2016-01-14
原文:代码第38 行,类D 是虚继承自A,编译器为该类安插一个指向父类的指针,指针大小为4。由于此类有了指针,编译器不会安插一个char了。因此其大小是4个字节。 代码第 39 行,类 E 虚继承自 A 并且也虚继承自 B,因此它有指向父类 A 的指针与父类B的指针,加起来大小为8个字节。 代码第40行,类F含有一个静态成员变量,这个静态成员的空间不在类的实例中,而是像全局变量一样在静态存储区中,被每一个类的实例共享,因此其大小是4个字节。 【答案】 sizeof(A) = 1 sizeof(B) = 1 sizeof(C) = 1 sizeof(D) = 4
2016-01-14
原文:sizeof(E) = 8 sizeof(F) = 4
2016-01-14
原文:理解sizeof与strlen的区别 出现频率:★★★ 【解析】 它们的区别如下。 sizeof 是操作符,strlen 是函数。 sizeof 操作符的结果类型是size_t,它在头文件中typedef 为unsignedint 类型,该类型保证能容纳实现所建立的最大对象的字节大小。 sizeof 可以用类型做参数,strlen 只能用char*做参数,且必须是以‘‘\0‘‘结尾的。 数组做sizeof 的参数不退化,传递给strlen 就退化为指针了。 大部分编译程序在编译的时候 sizeof 就被计算过了,这就是 sizeof(x)可以用来定义数组维数的原因。strlen的结果要在运行的时候才能计算出来,它用来计算字符串的长度,不是类型占内存的大小。 sizeof 后如果是类型,必须加括弧;如果是变量名,可以不加括弧。这是因为sizeof是个操作符,而不是个函数。
2016-01-14
原文:在计算字符串数组的长度上有区别。例如, 1 char str[20]="0123456789"; 2 int a=strlen(str); 3 int b=sizeof(str); a计算的是以0x00结束的字符串的长度(不包括0x00结束符),这里结果是10。 b计算的则是分配的数组str[20]所占的内存空间的大小,不受里面存储内容的改变而改变,这里结果是20。 如果要计算指针指向的字符串的长度,则一定要使用strlen。例如。 1 char* ss = "0123456789"; 2 int a = sizeof(ss); 3 int b = strlen(ss); a计算的是ss指针占用的内存空间大小,这里结果是4。 b计算的是ss指向的字符串的长度,这里结果是10。
2016-01-14
原文:(1)函数UpperCase(char str[])的意图是将str 指向的字符串中小写字母转换为大写字母。于是在代码第9行利用sizeof(str)/sizeof(str[0])获得数组中的元素个数以便做循环操作。然而,sizeof(str)得到的并不是数组占用内存的总大小,而是一个字符指针的大小,为4字节。因此这里只能循环4次,在代码第18行main()函数的调用中只能改变对数组的前四个字符进行转换。转换的结果为“ABCDe”。 (2)在代码第 17 行,这里的意图是使用要打印字符串的长度。然而,sizeof(str)/sizeof(str[0])计算的是数组元素的个数,比字符串的长度大1,原因是数组的长度还包括字符串的结束符‘\0‘。 应该用strlen()函数来代替sizeof计算字符串长度。正确的代码如下。
2016-01-14
原文:1 #include <iostream.h> 2 #include <string.h> 3 4 void UpperCase(char str[]) 5 { 6 int test = sizeof(str); 7 int test2 = sizeof(str[0]); 8 9 for(size_t i=0; i<strlen(str); ++i) //计算字符串的长度 10 if(‘a‘<=str[i] && str[i]<=‘z‘) 11 str[i] -= (‘a‘-‘A‘); 12 } 13 14 int main() 15 { 16 char str[] = "aBcDe"; 17 18 cout << "The length of str is " << strlen(str) << endl; //计算字符串的长度 19 UpperCase( str ); 20 cout << str << endl; 21 return 0;
2016-01-14
原文:联合体的大小取决于它所有的成员中占用空间最大的一个成员的大小。并且对于复合数据类型,如 union、struct、class 的对齐方式为成员中最大的成员对齐方式。 对于u来说,大小就是最大的double类型成员a了,即sizeof(u)=sizeof(double)=8。 对于 u2 来说,最大的空间是 char[13]类型的数组。这里要注意,由于它的另一个成员int b 的存在,u2 的对齐方式变成4,也就是说,u2 的大小必须在4的对齐上,所以占用的空间变为最接近13的对齐,即16。 对于u3 来说,最大的空间是char[13]类型的数组,即sizeof(u3)=13。
2016-01-14
原文:union u 4 { 5 double a; 6 int b; 7 }; 8 9 union u2 10 { 11 char a[13]; 12 int b; 13 }; 14 15 union u3 16 { 17 char a[13]; 18 char b; 19 };
2016-01-14
原文:编译器会尽量把数据放在它的对齐上以提高内存的命中率。对齐是可以更改的,使用#pragma pack(x)可以改变编译器的对齐方式。C++固有类型的对齐取编译器对齐方式与自身大小中较小的一个。例如,指定编译器按 2 对齐,int类型的大小是4,则int的对齐为2和4中较小的2。在默认的对齐方式下,因为几乎所有的数据类型都不大于默认的对齐方式8(除了long double),所以所有的固有类型的对齐方式可以认为就是类型自身的大小
2016-01-14
原文:#pragma pack(2) 4 5 union u 6 { 7 char buf[9]; 8 int a; 9 };
2016-01-14
原文:C++固有类型的对齐取编译器对齐方式与自身大小中较小的一个。 上面的程序中,由于使用手动更改对齐方式为2,所以int的对齐也变成了2(int自身对齐为4),u的对齐取成员中最大的对齐,也是2,所以此时sizeof(u)=10。 如果注释上面的第3行,int的对齐使用4,u的对齐取成员中最大的对齐,也是4,所以此时sizeof(u)=12。
2016-01-15
原文:引入内联函数的主要目的是,用它替代 C 语言中表达式形式的宏定义来解决程序中函数调用的效率问题。在C语言里可以使用如下的宏定义。 1 #define ExpressionName(Var1,Var2) (Var1+Var2)*(Var1-Var2) 这种宏定义在形式及使用上像一个函数,但它使用预处理器实现,没有了参数压栈、代码生成等一系列的操作,因此效率很高。这种宏定义在形式上类似于一个函数,但在使用它时,仅仅只是做预处理器符号表中的简单替换,因此它不能进行参数有效性的检测,也就不能享受 C++编译器严格类型检查的好处。另外,它的返回值也不能被强制转换为可转换的合适类型,这样,它的使用就存在着一系列的隐患和局限性。 另外,在 C++中引入了类及类的访问控制,这样,如果一个操作或者说一个表达式涉及类的保护成员或私有成员,你就不可能使用这种宏定义来实现(因为无法将 this 指针放在合适的位置)。
2016-01-15
原文:inline推出的目的,也正是为了取代这种表达式形式的宏定义。它消除了它的缺点,同时又很好地继承了它的优点。
2016-01-15
原文:理解内联函数相比于宏定义的优越之处 出现频率:★★★ 【解析】 有如下几种原因: inline定义的类的内联函数,函数的代码被放入符号表中,在使用时直接进行替换(像宏一样展开),没有了调用的开销,效率也很高。 类的内联函数也是一个真正的函数。编译器在调用一个内联函数时,首先会检查它的参数的类型,保证调用正确;然后进行一系列的相关检查,就像对待任何一个真正的函数一样。这样就消除了它的隐患和局限性。 inline 可以作为某个类的成员函数,当然就可以在其中使用所在类的保护成员及私有成员。
2016-01-15
原文:理解内联函数的作用场合 出现频率:★★★ 【解析】 首先使用inline函数可以完全取代表达式形式的宏定义。 内联函数在 C++类中应用最广的,应该是用来定义存取函数。我们定义的类中一般会把数据成员定义成私有的或者保护的,这样,外界就不能直接读写我们类成员的数据了。对于私有或者保护成员的读写就必须使用成员接口函数来进行。如果我们把这些读写成员函数定义成内联函数的话,将会获得比较好的效率。例如下面的代码:
2016-01-15
原文:1 Class A 2 { 3 Private: 4 int nTest; 5 Public: 6 int readTest() 7 { 8 return nTest; 9 } 10 void setTest(int i); 11 }; 12 13 inline void A::setTest(int i) 14 { 15 nTest = i; 16 }; 类 A 的成员函数 readTest()和 setTest()都是 inline 函数。readTest()函数的定义体被放在类声明之中,因而readTest()自动转换成inline函数;setTest()函数的定义体在类声明之外,因此要加上inline关键字。
2016-01-19
原文:内联是以代码膨胀(复制)为代价的,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。以下情况不宜使用内联。 如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。 如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。 另外,类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数和析构函数。
2016-01-19
原文:所以不要随便地将构造函数和析构函数的定义体放在类声明中。一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(这说明了 inline 不应该出现在函数的声明中)。
2016-01-19
原文:理解内联函数与宏定义的区别 出现频率:★★★★ 【解析】 二者区别如下: 内联函数在编译时展开,宏在预编译时展开。 在编译的时候,内联函数可以直接被镶嵌到目标代码中,而宏只是一个简单的文本替换。 内联函数可以完成诸如类型检测、语句是否正确等编译功能,宏就不具有这样的功能。 宏不是函数,inline 函数是函数。 宏在定义时要小心处理宏参数(一般情况是把参数用括号括起来),否则容易出现二义性。而内联函数定义时不会出现二义性。
2016-01-19
原文:7 int a = 10; 8 int b = 20; 9 int &rn = a; 10 int equal; 11 12 rn=b; 13 cout << "a = " << a << endl; 14 cout << "b = " << b << endl; 15 16 rn = 100; 17 18 cout << "a = " << a << endl; 19 cout << "b = " << b << endl; 20 21 equal = (&a == &rn)? 1: 0; 22 23 cout << "equal = " << equal << endl;
2016-01-19
原文:代码第7 行和第8 行,整型变量a 和整型变量b 分别被初始化为10 和20。 代码第9 行,声明rn 为变量a 的一个引用。 代码第12 行,将rn 的值赋为b 的值。此时rn 其实就是a 的一个别名,对rn 的赋值其实就是对a的赋值。因此执行完赋值后,a的值就是b的值,即都是20。 代码第16 行,将rn 的值赋为100,于是a 的值变成了100。 代码第21行,将a的地址与rn的地址进行比较,如果相等,变量equal的值为1,否则为0。将rn声明为a的引用,不需要为rn另外开辟内存单元。rn和a占内存中的同一个存储单元,它们具有同一地址,所以equal为1。
2016-02-01
原文:引用只能在声明的时候被赋值,以后都不能再把该引用名作为其他变量名的别名。
2016-02-01
原文:把const放在引用之前表示声明的是一个常量引用。不能使用常量引用修改引用的变量的值。
2016-02-01
原文:对于常量类型的变量,其引用也必须是常量类型的;对于非常量类型的变量,其引用可以是非常量的,也可以是常量的。但是要注意,无论什么情况,都不能使用常量引用修改其引用的变量的值。
2016-02-02
原文:引用和指针的区别
2016-02-02
原文:区别如下: (1)初始化要求不同。引用在创建的同时必须初始化,即引用到一个有效的对象;而指针在定义的时候不必初始化,可以在定义后面的任何地方重新赋值。 (2)可修改性不同。引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用;而指针在任何时候都可以改变为指向另一个对象。给引用赋值并不是改变它和原始对象的绑定关系。 (3)不存在 NULL 引用,引用不能使用指向空值的引用,它必须总是指向某个对象;而指针则可以是NULL,不需要总是指向某些对象,可以把指针指向任意的对象,所以指针更加灵活,也容易出错。 (4)测试需要的区别。由于引用不会指向空值,这意味着使用引用之前不需要测试它的合法性;而指针则需要经常进行测试。因此使用引用的代码效率比使用指针的要高。
2016-02-02
原文:(5)应用的区别。如果是指一旦指向一个对象后就不会改变指向,那么应该使用引用。如果有存在指向 NULL(不指向任何对象)或在不同的时刻指向不同的对象这些可能性,应该使用指针。 实际上,在语言层面,引用的用法和对象一样;在二进制层面,引用一般都是通过指针来实现的,只不过编译器帮我们完成了转换。总体来说,引用既具有指针的效率,又具有变量使用的方便性和直观性。
2016-02-03
原文:由于不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,因此引用很安全。 对于指针来说,它可以随时指向别的对象,并且可以不被初始化,或为NULL,所以不安全。const 指针仍然存在空指针,并且有可能产生野指针。
2016-02-19
原文:扩展知识:解读复杂指针声明 使用右左法则:首先从最里面的圆括号看起,然后往右看,再往左看。每当遇到圆括号时,就应该掉转阅读方向。一旦解析完圆括号里面所有的东西,就跳出圆括号。重复这个过程,直到整个声明解析完毕。 这里对这个法则进行一个小小的修正,应该是从未定义的标识符开始阅读,而不是从括号读起。这是因为一个声明里面未定义的标识符只会有一个。
2016-02-19
原文:现在通过几个例子来讨论如何运用右左法则解读复杂指针声明。
2016-02-19
原文:1 int (*func)(int *p); 首先找到那个未定义的标识符,就是func,它的外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;然后跳出这个圆括号,先看右边,也是一个圆括号,这说明(*func)是一个函数,而 func 是一个指向这类函数的指针,就是一个函数指针,这类函数具有int*类型的形参,返回值类型是int。
2016-02-19
原文:2 int (*func)(int *p, int (*f)(int*)); func被一对括号包含,且左边有一个*号,说明func是一个指针,跳出括号,右边也有个括号,那么func 是一个指向函数的指针,这类函数具有int *和int (*)(int*)这样的形参,返回值为int类型。再来看一看func 的形参int (*f)(int*),类似前面的解释,f 也是一个函数指针,指向的函数具有int*类型的形参,返回值为int。
2016-02-19
原文:3 int (*func[5])(int *p); func右边是一个[]运算符,说明func是一个具有5个元素的数组;func的左边有一个*,说明 func 的元素是指针。要注意这里的*不是修饰 func 的,而是修饰 func[5]的,原因是[]运算符优先级比*高,func先跟[]结合,因此*修饰的是func[5]。跳出这个括号,看右边,也是一对圆括号,说明 func 数组的元素是函数类型的指针,它所指向的函数具有 int*类型的形参,返回值类型为int。
2016-02-23
原文:4 int (*(*func)[5])(int *p); func被一个圆括号包含,左边又有一个*,那么func是一个指针;跳出括号,右边是一个[]运算符号,说明func是一个指向数组的指针。现在往左看,左边有一个*号,说明这个数组的元素是指针;再跳出括号,右边又有一个括号,说明这个数组的元素是指向函数的指针。总结一下,就是:func 是一个指向数组的指针,这个数组的元素是函数指针,这些指针指向具有int*类型的形参,返回值为int类型的函数。
2016-02-23
原文:5 int (*(*func)(int *p))[5]; func是一个函数指针,这类函数具有int*类型的形参,返回值是指向数组的指针,所指向的数组的元素是具有5个int元素的数组。
2016-02-23
原文: #include <stdio.h> 2 3 int main(void) 4 { 5 int a[5]={1,2,3,4,5}; 6 int *ptr=(int *)(&a+1); 7 8 printf("%d\n", *(a+1)); 9 printf("%d\n", *(ptr-1)); 10 11 return 0; 12 } 【解析】 这里主要是考查关于指针加减操作的理解。 对指针进行加1操作,得到的是下一个元素的地址,而不是原有地址值直接加1。所以,一个类型为t的指针的移动,以sizeof(t)为移动单位。
2016-02-23
原文:代码第5 行,声明一个一维数组a,并且a 有5 个元素。 代码第6行,ptr是一个int型的指针&a + 1,即取a的地址,该地址的值加sizeof(a)的值,即&a + 5*sizeof(int),也就是a[5]的地址,显然,当前指针已经越过了数组的界限。(int *)(&a+1)则是把上一步计算出来的地址,强制转换为int *类型,赋值给ptr。 代码第 8 行,a 与&a 的地址是一样的,但意思不一样。a 是数组首地址,也就是a[0]的地址;&a是对象(数组)首地址,a+1是数组下一元素的地址,即a[1];而&a+1是下一个对象的地址,即a[5]。因此这里输出为2。 代码第9 行,因为ptr 指向a[5],并且ptr 是int*类型,所以*(ptr-1)指向a[4],输出5。
2016-02-23
原文: char str1[] = "abc"; 7 char str2[] = "abc"; 8 const char str3[] = "abc"; 9 const char str4[] = "abc"; 10 const char* str5 = "abc"; 11 const char* str6 = "abc"; 12 char* str7 = "abc"; 13 char* str8 = "abc";
2016-02-23
原文:数组str1、str2、str3和str4都是在栈中分配的,内存中的内容都为"abc"加一个‘\0‘,但是它们的位置是不同的。因此代码第15行和第16行的输出都是0。 指针str5、str6、str7和str8也是在栈中分配的,它们都指向"abc"字符串,注意"abc"存放在数据区,所以str5、str6、str7 和str8 其实指向同一块数据区的内存。因此第17、18和19行的输出是1。
2016-02-24
原文:这里有个小规则,像这样连着的两个词,前面的一个通常是修饰部分,中心词是后面一个词。 常量指针,表述为“是常量的指针”,它首先应该是一个指针。 指针常量,表述为“是指针的常量”,它首先应该是一个常量。 接下来进行详细分析。 常量指针,它是一个指向常量的指针。设置常量指针指向一个常量,为的就是防止写程序过程中对指针误操作出现了修改常量这样的错误,编译系统就会提示我们出错信息。因此,常量指针就是指向常量的指针,指针所指向的地址的内容是不可修改的。
2016-02-24
原文:指针常量,它首先是一个常量,然后才是一个指针。指针常量就是不能修改这个指针所指向的地址,一开始初始化指向哪儿,它就只能指向哪儿了,不能指向其他的地方了,就像一个数组的数组名一样,是一个固定的指针,不能对它移动操作。如果使用p++,系统就会提示出错。但是注意,这个指向的地方里的内容是可以替换的,这和上面说的常量指针是完全不同的概念。总之,指针常量就是指针的常量,它是不可改变地址的指针,但是可以对它所指向的内容进行修改。 【答案】 常量指针就是指向常量的指针,它所指向的地址的内容是不可修改的。 指针常量就是指针的常量,它是不可改变地址的指针,但是可以对它所指向的内容进行修改。
2016-02-25
原文:下述4个指针有什么区别? 1 char * const p1; 2 char const * p2; 3 const char *p3; 4 const char *constp4; 【解析】 如果const位于*号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于*号的右侧,const就是修饰指针本身,即指针本身是常量。因此,p1指针本身是常量,但它指向的内容可以被修改。p2和p3的情况相同,都是指针所指向的内容为常量。p4则表示指针本身是常量,并且它指向的内容也不可被修改。
2016-02-25
原文:下列关于this指针的叙述中,正确的是( )。 A.任何与类相关的函数都有this指针 B.类的成员函数都有this指针 C.类的友元函数都有this指针 D.类的非静态成员函数才有this指针 【解析】 A 错误。类的非静态成员函数是属于类的对象,含有this指针。而类的static 函数属于类本身,不含this指针。 B 错误。类的非静态成员函数是属于类的对象,含有this指针。而类的static 函数属于类本身,不含this指针。 C 错误。友元函数是非成员函数,所以它无法通过this 指针获得一份拷贝。 D 正确。
2016-02-25
原文:下面的代码输出结果是什么?如果取消第14行的注释,输出又是什么? 1 #include <iostream> 2 using namespace std; 3 4 class MyClass 5 { 6 public: 7 int data; 8 MyClass(int data) 9 { 10 this->data = data; 11 } 12 void print() 13 { 14 //cout << data << endl; 15 cout << "hello!" << endl; 16 } 17 };
2016-02-25
原文:20 int main() 21 { 22 MyClass * pMyClass; 23 pMyClass = new MyClass(1); 24 pMyClass->print(); 25 pMyClass[0].print(); 26 pMyClass[1].print(); 27 pMyClass[10000000].print();
2016-02-25
原文:这里需要明白类函数是如何被编译以及如何被执行的。 对于类成员函数而言,并不是一个对象对应一个单独的成员函数体,而是此类的所有对象共用这个成员函数体。当程序被编译之后,此成员函数地址即已确定。我们常说,调用类成员函数时,会将当前对象的 this 指针传给成员函数。没错,一个类的成员函数体只有一份,而成员函数之所以能把属于此类的各个对象的数据区别开,就在于每次执行类成员函数时,都会把当前对象的 this 指针(对象首地址)传入成员函数,函数体内所有对类数据成员的访问,都会被转化为this->数据成员的方式。 如果print函数里没有访问对象的任何数据成员,那么此时传进来的对象this指针实际上是没有任何用处的。这样的函数,其特征与全局函数并没有太大区别。但如果取消第 14行的注释,由于print函数要访问类的数据成员data,而类的数据成员是伴随着对象声明而产生的。但是,我们只 new 了一个 MyClass,显然,下标"1"和下标"10000000"的 MyClass对象根本不存在,那么对它们的数据成员访问也显然是非法的。
2016-02-25
原文:【答案】 注释代码第14行时的输出: 1 hello! 2 hello! 3 hello! 4 hello! 取消代码第 14行注释后的输出: 1 1 2 hello! 3 1 4 hello! 5 -33686019 6 hello! 7 段错误
2016-03-13
原文:指针数组表示它是一个数组,并且数组中的每一个元素是指针。 数组指针表示它是一个指针,并且指向了一个数组。
2016-03-14
原文:指针函数是指带指针的函数,即本质是一个函数,并且返回类型是某一类型的指针。其定义如下: 1 返回类型标识符 *返回名称(形式参数表){函数体 } 事实上,每一个函数,即使它不带有返回某种类型的指针,它本身都有一个入口地址,该地址相当于一个指针。比如函数返回一个整型值,实际上也相当于返回一个指针变量的值,不过这时的变量是函数本身而已,而整个函数相当于一个“变量”。 函数指针是指向函数的指针变量,因而它本身首先应是指针变量,只不过该指针变量指向函数。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型的变量一样。
2016-03-14
原文:指针函数是返回指针类型的函数。 函数指针是指向函数地址的指针。
2016-03-14
原文:定义下面的几种类型变量: a.含有10个元素的指针数组 b.数组指针 c.函数指针 d.指向函数的指针数组 【答案】 a.int *a[10]; b.int *a = new int[10]; c.void (*fn)(int, int); d.int (*fnArray[10])(int, int);
2016-03-14
原文:void (*f)(int, int),f 是指向void max(int x, int y)类型的函数指针。 int *fn(),fn 是返回int 指针类型的函数。 const int *p,p 是一个指向const 的指针,指向一个常量。 int* const q,q 是一个const 指针。 const int* const ptr,ptr 是指向const 的const 指针。
2016-03-14
原文:1 #include <stdio.h> 2 int add1(int a1,int b1); 3 int add2(int a2,int b2); 4 int main(int argc,char* argv[]) 5 { 6 int numa1=1,numb1=2; 7 int numa2=2,numb2=3; 8 int (*op[2])(int a,int b); 9 op[0]=add1; 10 op[1]=add2; 11 printf("%d %d\n",op[0](numa1,numb1),op[1](numa2,numb2)); 12 getchar(); 13 14 return 0; 15 } 16
2016-03-14
原文:17 int add1(int a1,int b1) 18 { 19 return a1+b1; 20 } 21 22 int add2(int a2,int b2) 23 { 24 return a2+b2; 25 }
2016-03-14
原文:【解析】 在代码第8行,定义了一个函数指针数组op,它含有两个指针元素。在第9行和第10行把这两个元素分别指向了add1和add2两个函数地址。最后在第11行打印出使用函数指针调用add1和add2这两个函数返回的结果。 【答案】 1 3 5
2016-03-14
原文:下面的定义有什么作用? 1 typedef int (*pfun)(int x,int y); 【解析】 这里的pfun是一个使用typedef自定义的数据类型。它表示一个函数指针,其参数有两个,都是int类型,返回值也是int类型。可以按如下步骤使用: 1 typedef int (*pfun)(int x,int y); 2 int fun(int x, int y); 3 pfun p = fun; 4 int ret = p(2, 3); 简单说明: 第1 行定义了pfun 类型,表示一个函数指针类型。 第2 行定义了一个函数。 第3 行定义了一个pfun 类型的函数指针p,并赋给它fun 的地址。 第4 行调用p(2, 3),实现fun(2, 3)的调用功能。
2016-03-14
原文:malloc与free是C++/C的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。 对于非内部数据类型的对象而言,光用 malloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于 malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
2016-03-14
原文:因此,C++需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意:new/delete不是库函数。请看下面的例子。 1 #include <iostream> 2 using namespace std; 3 4 class Obj 5 { 6 public: 7 Obj(void) 8 { 9 cout << "Initialization" << endl; 10 } 11 ~Obj(void) 12 { 13 cout << "Destroy" << endl; 14 } 15 }; 16
2016-03-14
原文:17 void UseMallocFree(void) 18 { 19 cout << "in UseMallocFree()..." << endl; 20 Obj *a = (Obj *)malloc(sizeof(Obj)); 21 free(a); 22 } 23
2016-03-14
原文:void UseNewDelete(void) 25 { 26 cout << "in UseNewDelete()..." << endl; 27 Obj *a = new Obj; 28 delete a; 29 } 30 31 int main() 32 { 33 UseMallocFree(); 34 UseNewDelete(); 35 36 return 0; 37 }
2016-03-14
原文:在这个示例中,类 Obj 只有构造函数和析构函数的定义,这两个成员函数分别打印一句话。函数UseMallocFree()中调用malloc/free 申请和释放堆内存;函数UseNewDelete ()中调用new/delete申请和释放堆内存。可以看到函数UseMallocFree()执行时,类Obj的构造函数和析构函数都不会被调用;而函数UseNewDelete ()执行时,类Obj的构造函数和析构函数都会被调用。执行结果如下: in UseMallocFree()... in UseNewDelete()... Initialization Destroy
2016-03-14
原文:对于非内部数据类型的对象而言,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能把执行构造函数和析构函数的任务强加于malloc/free,因此只有使用new/delete运算符。
2016-03-15
原文: #include <stdio.h> 2 #include <malloc.h> 3 4 struct Tag_Node 5 { 6 struct Tag_Node* left; 7 struct Tag_Node* right; 8 int value; 9 }; 10 typedef struct Tag_Node TNode; 11 12 TNode* root = NULL; 13
2016-03-15
原文:14 void append(int N); 15 void print(); 16 17 int main() 18 { 19 append(63); 20 append(45); 21 append(32); 22 append(77); 23 append(96); 24 append(21); 25 append(17); 26 printf("head: %d\n", root->value); 27 print(); //打印链表所有元素 28 } 29 30 void append(int N) 31 { 32 TNode* NewNode = (TNode *)malloc(sizeof(TNode)); 33 NewNode->value = N; 34 NewNode->left = NULL; //初始化left 35 NewNode->right = NULL; //初始化right 36
2016-03-15
原文:37 if(root == NULL) 38 { 39 root = NewNode; 40 return; 41 } 42 else 43 { 44 TNode* temp; 45 temp=root; 46 48 (N < temp->value && temp->right != NULL)) 47 while((N >= temp->value && temp->left != NULL) || 49 { 50 while(N >= temp->value && temp->left != NULL) 51 temp = temp->left; 52 while(N < temp->value && temp->right != NULL) 53 temp = temp->right; 54 } 55 if(N >= temp->value)
2016-03-15
原文:56 { 57 temp->left = NewNode; 58 NewNode->right = temp; //形成双向链表 59 } 60 else 61 { 62 temp->right = NewNode; 63 NewNode->left = temp; //形成双向链表 64 } 65 return; 66 } 67 } 68 69 void print() 70 { 71 TNode* leftside = NULL; 72 73 if (root == NULL) 74 { 75 printf("There is not any element1"); 76 return; 77 } 78
2016-03-15
原文:79 leftside = root->left; 80 81 while(1) 82 { 83 if (leftside->left == NULL) 84 { 85 break; 86 } 87 leftside = leftside->left; 88 } 89
2016-03-15
原文:90 while(leftside != NULL) 91 { 92 printf("%d ", leftside->value); 93 leftside = leftside->right; 94 } 95 }
2016-03-16
原文:C语言的标准内存分配函数:malloc、calloc、realloc、free等。 malloc与calloc的区别为1块与n块的区别。 malloc 的调用形式为(类型*)malloc(size):在内存的动态存储区中分配一块长度为“size”字节的连续区域,返回该区域的首地址,此时内存中的值没有初始化,是个随机数。 calloc 的调用形式为(类型*)calloc(n,size):在内存的动态存储区中分配n 块长度为“size”字节的连续区域,返回首地址,此时内存中的值都被初始化为0。 realloc 的调用形式为(类型*)realloc(*ptr,size):将 ptr 内存大小增大到 size,新增加的内存块没有初始化。 free 的调用形式为free(void*ptr):释放ptr 所指向的一块内存空间。 C++中,new/delete函数可以调用类的构造函数和析构函数。
2016-03-16
原文:1 #include <iostream> 2 using namespace std; 3 4 void GetMemory(char *p, int num) 5 { 6 p = (char *)malloc(sizeof(char) *num); 7 }; 8 9 void GetMemory2(char **p, int num) 10 { 11 *p = (char *)malloc(sizeof(char) *num); 12 }; 13 14 void GetMemory3(char* &p, int num) 15 { 16 p = (char *)malloc(sizeof(char) *num);
2016-03-16
原文:17 }; 18 19 char *GetMemory4(int num) 20 { 21 char *p = (char *)malloc(sizeof(char) *num); 22 23 return p; 24 } 25 26 int main(void) 27 { 28 char *str1 = NULL; 29 char *str2 = NULL; 30 char *str3 = NULL; 31 char *str4 = NULL; 32 33 //GetMemory(str1, 20); 34 GetMemory2(&str2, 20); 35 GetMemory3(str3, 20); 36 str4 = GetMemory4(20); 37
2016-03-16
原文:38 strcpy(str2, "GetMemory 2"); 39 strcpy(str3, "GetMemory 3"); 40 strcpy(str4, "GetMemory 4"); 41 42 cout << "str1 == NULL? " << (str1 == NULL? "yes":"no") << endl; 43 cout << "str2:" << str2 << endl; 44 cout << "str3:" << str3 << endl; 45 cout << "str4:" << str4 << endl; 46 47 free(str2); 48 free(str3); 49 free(str4); 50 str2 = NULL; 51 str3 = NULL; 52 str4 = NULL; 53 54 return 0; 55 } 在上面的代码中,GetMemory2()函数采用二维指针作为参数传递;GetMemory3()函数采用指针的引用作为参数传递;GetMemory4()函数采用返回堆内存指针的方式。可以看到这3个函数
2016-03-16
原文:都能起到相同的作用。 另外注意第47~52行,这里在主函数推出之前把指针str2、str3和str4指向的堆内存释放并把指针赋为NULL。每当决定不再使用堆内存时,应该把堆内存释放,并把指针赋为NULL,这样能避免内存泄漏以及产生野指针,是良好的编程习惯。 程序运行结果如下所示。 1 str1 == NULL? Yes 2 str2:GetMemory 2 3 str3:GetMemory 3 4 str3:GetMemory 4
2016-03-16
原文:(1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,例如全局变量。 (2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。处理器的指令集中有关于栈内存的分配运算,因此效率很高,但是分配的内存容量有限。 (3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
2016-03-16
原文:句柄在 Windows 编程中是一个很重要的概念,在许多地方都扮演着重要的角色。在Windows环境中,句柄是用来标识项目的,这些项目包括: 模块(module)。 任务(task)。 实例(instance)。 文件(file)。 内存块(block of memory)。 菜单(menu)。 控制(control)。 字体(font)。 资源(resource),包括图标(icon)、光标(cursor)、字符串(string)等。 GDI对象(GDI object),包括位图(bitmap),画刷(brush)、元文件(metafile),调色板(palette)、画笔(pen)、区域(region),以及设备描述表(device context)。
2016-03-16
原文:Windows 是一个以虚拟内存为基础的操作系统。在这种系统环境下,Windows 内存管理器经常在内存中来回移动对象,以此来满足各种应用程序的内存需要。对象被移动意味着它的地址变化了。由于地址总是如此变化,所以Windows操作系统为各应用程序腾出一些内存储地址,用来专门登记各应用对象在内存中的地址变化,而这地址(存储单元的位置)本身是不变的。Windows 内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。这样我们只需记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置。这个地址是在对象装载(Load)时由系统分配给的,当系统卸载时(Unload)又释放给系统。 因此,Windows 程序中并不是用物理地址来标识一个内存块、文件、任务或动态装入模块的,相反,WINDOWS API 给这些项目分配确定的句柄,并将句柄返回给应用程序,然后通过句柄来进行操作。
2016-03-16
原文:在Windows编程中会用到大量的句柄,比如HINSTANCE(实例句柄)、HBITMAP(位图句柄)、HDC(设备描述表句柄)、HICON(图标句柄)等。这当中还有一个通用的句柄,就是HANDLE,比如下面的语句: 1 HINSTANCE hInstance; 2 HANDLE hInstance; 句柄地址(稳定)→记载着对象在内存中的地址→对象在内存中的地址(不稳定)→实际对象。但是,必须注意的是,程序每次重新启动,系统不能保证分配给这个程序的句柄还是原来的那个句柄,而且绝大多数情况的确是不一样的。
2016-03-16
原文:指针对应着一个数据在内存中的地址,得到了指针就可以自由地修改该数据。Windows 并不希望一般程序修改其内部数据结构,因为这样太不安全。所以 Windows给每个使用GlobalAlloc等函数声明的内存区域指定一个句柄,句柄是一种指向指针的指针。
2016-03-16
原文:句柄和指针都是地址,不同之处在于: (1)句柄所指的可以是一个很复杂的结构,并且很有可能是与系统有关的。比如说线程的句柄,它指向的就是一个类或者结构,它和系统有很密切的关系。当一个线程由于不可预料的原因而终止时,系统就可以返回它所占用的资料,如 CPU、内存等。反过来想可以知道,这个句柄中的某一些项是与系统进行交互的。由于Windows系统是一个多任务的系统,它随时都可能要分配内存、回收内存、重组内存。 (2)指针也可以指向一个复杂的结构,但是通常是由用户定义的,所以必需的工作都要用户完成,特别是在删除的时候。
2016-03-16
原文:在C/C++中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。字符串是以‘\0‘作为串的结束符。C/C++提供了丰富的字符串处理函数,下面列出了几个最常用的函数: 字符串输出函数puts; 字符串输入函数gets; 字符串连接函数strcat; 字符串复制函数strcpy; 测字符串长度函数strlen。 字符串是笔试以及面试的热门考点,通过字符串测试可以考查程序员的编程规范以及编程习惯。其中也包括了许多知识点,例如内存越界、指针与数组操作等等。许多公司在面试时会要求应试者写一段 strcpy 复制字符串或字符串子串操作的程序。本章列举了一些与字符串相关的面试题及其解析,有些题要求较高的编程技巧。
2016-03-16
原文:C语言提供了几个标准库函数,可以将任意类型(整型、长整型、浮点型等)的数字转换为字符串。下面列举了各函数的方法及其说明。 itoa():将整型值转换为字符串。 ltoa():将长整型值转换为字符串。 ultoa():将无符号长整型值转换为字符串。 gcvt():将浮点型数转换为字符串,取四舍五入。 ecvt():将双精度浮点型值转换为字符串,转换结果中不包含十进制小数点。 fcvt():以指定位数为转换精度,其余同ecvt()。 还可以使用sprintf系列函数把数字转换成字符串,这种方式的速度比itoa()系列函数的速度慢。
2016-03-16
原文:如果不使用 atoi 或 sprintf 等库函数,我们可以通过把整数的各位上的数字加‘0‘转换成char 类型并存到字符数组中。但要注意,需要采用字符串逆序的方法。
2016-03-16
原文:atof():将字符串转换为双精度浮点型值。 atoi():将字符串转换为整型值。 atol():将字符串转换为长整型值。 strtod():将字符串转换为双精度浮点型值,并报告不能被转换的所有剩余数字。
2016-03-16
原文:strtol():将字符串转换为长整型值,并报告不能被转换的所有剩余数字。 strtoul():将字符串转换为无符号长整型值,并报告不能被转换的所有剩余数字。
2016-03-16
原文:已知strcpy函数的原型是: char * strcpy(char * strDest,const char * strSrc); (1)不调用库函数,实现strcpy函数。 (2)解释为什么要返回char *。 【解析】 代码如下。 1 #include <stdio.h> 2 3 char * strcpy(char * strDest, const char * strSrc)
2016-03-16
原文://实现strSrc到strDest的复制 4 { 5 if ((strDest == NULL) || (strSrc == NULL)) //判断参数strDest和strSrc的有效性 6 { 7 return NULL; 8 } 9 char *strDestCopy = strDest; //保存目标字符串的首地址 10 while ((*strDest++ = *strSrc++)!=‘\0‘); //把strSrc字符串的内容复制到strDest下 11 12 return strDestCopy; 13 } 14
2016-03-16
原文:15 int getStrLen(const char *strSrc) //实现获取strSrc字符串的长度 16 { 17 int len = 0; //保存长度 18 while(*strSrc++ != ‘\0‘) //循环直到遇见结束符‘\0‘为止 19 { 20 len++; 21 } 22 23 return len; 24 }; 25 26 int main()
2016-03-16
原文:27 { 28 char strSrc[] = "Hello World!"; //要被复制的源字符串 29 char strDest[20]; //要复制到的目的字符数组 30 int len = 0; //保存目的字符数组中字符串的长度 31 32 len = getStrLen(strcpy(strDest, strSrc)); //链式表达式,先复制后计算长度 33 printf("strDest: %s\n", strDest); 34 printf("Length of strDest: %d\n", len); 35 36 return 0; 37 }
2016-03-16
原文:实现memcpy函数
2016-03-16
原文:程序代码如下所示。 1 #include <stdio.h> 2 #include <assert.h> 3 4 void *memcpy2(void *memTo, const void *memFrom, size_t size) 5 { 6 assert((memTo != NULL) && (memFrom !=
2016-03-16
原文:NULL)); //memTo和memFrom必须有效 7 char *tempFrom = (char *)memFrom; //保存memFrom首地址 8 char *tempTo = (char *)memTo; //保存memTo首地址 9 10 while(size -- > 0) //循环size次,复制memFrom的值到memTo中 11 *tempTo++ = *tempFrom++ ; 12 13 return memTo; 14 } 15 16 int main() 17 {
2016-03-16
原文:18 char strSrc[] = "Hello World!"; //将被复制的字符数组 19 char strDest[20]; //目的字符数组 20 21 memcpy2(strDest, strSrc, 4); //复制strSrc的前4个字符到strDest中 22 strDest[4] = ‘\0‘; //把strDest的第5个元素赋为结束符‘\0‘ 23 printf("strDest: %s\n", strDest); 24 25 return 0; 26 }
2016-03-16
原文:strcpy与memcpy的区别
2016-03-16
原文:字符串复制与内存复制之间的区别
2016-03-16
原文:主要有下面几方面的区别。 复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。 复制的方法不同。strcpy 不需要指定长度,它是遇到字符串结束符‘\0‘而结束的。memcpy则是根据其第三个参数决定复制的长度。 用途不同。通常在复制字符串时用strcpy;而若需要复制其他类型数据,则一般用memcpy。
2016-03-18
原文:strcpy库函数的实现细节
2016-03-18
原文:这个题目非常简单。我们知道字符串是以‘\0‘作为结束符的,所以只需要做一次遍历就可以了。但是需要注意的是,要尽量把程序写得简单且效率高。看下面的示例代码: 1 #include <stdio.h> 2 #include <assert.h> 3 4 int strlen1(const char* src) 5 { 6 assert( NULL != src); //src必须有效 7 int len = 0; //保存src的长度 8 while(*src++ != ‘\0‘) //遇到结束符‘\0‘时退出循环 9 len++; //每循环一次,len加1 10 return len; 11 }
2016-03-18
原文:13 int strlen2(const char* src) 14 { 15 assert( NULL != src); //src必须有效 16 const char *temp = src; //保存src首地址 17 while(*src++ != ‘\0‘); //遇到结束符‘\0‘时退出循环 18 return (src-temp-1); //返回尾部指针与头部指针之差,即长度 19 } 20 21 int main() 22 { 23 char p[] = "Hello World!"; 24 printf("strlen1 len: %d\n", strlen1(p)); //打印方法1得到的结果 25 printf("strlen2 len: %d\n", strlen2(p)); //打印方法2得到的结果 26 27 return 0; 28 }
2016-03-18
原文:trlen1和strlen2这两个函数都可以用来计算字符串长度。下面来比较它们的区别: strlen1用一个局部变量len在遍历的时候做自增,然后返回len。因此,每当while循环一次,就需要执行两次自增操作。 strlen2用一个局部变量temp记录src遍历前的位置。while循环一次只需要一次自增操作。最后返回指针之间的位置差。 显然,strlen2比strlen1的效率要高,尤其是在字符串较长的时候。下面是程序的输出结果。 1 strlen1 len: 12 2 strlen2 len: 12
2016-03-18
原文:函数strcmp的实现细节
2016-03-18
原文:此题实际上就是做一个C/C++库函数中的strcmp的实现。对于两个字符串str1和str2,若相等,则返回0,若str1大于str2,则返回1,若str1小于str2,则返回?1。 程序代码如下。 1 #include <iostream> 2 using namespace std; 3 4 int mystrcmp(const char *src, const char *dst) 5 { 6 int ret = 0 ; 7 while( !(ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst) 8 { //循环比较两个字符是否相等 9 ++src; //如果不等或者到了dst字符串末尾,则 10 ++dst;
2016-03-18
原文:退出循环 11 } 12 if ( ret < 0 ) //ret保存着字符比较的结果 13 ret = -1 ; 14 else if ( ret > 0 ) 15 ret = 1 ; 16 return( ret ); 17 } 18 19 int main() 20 { 21 char str[10] = "1234567"; 22 char str1[10] = "1234567"; //str1 == str 23 char str2[10] = "12345678"; //str2 > str 24 char str3[10] = "1234566"; //str3 < str 25 26 int test1 = mystrcmp(str, str1); //测试str与str1比较 27 int test2 = mystrcmp(str, str2); //测试str与str2比较 28 int test3 = mystrcmp(str, str3); //测试str与str3比较
2016-03-18
原文:30 cout << "test1 = " << test1 << endl; 31 cout << "test2 = " << test2 << endl; 32 cout << "test3 = " << test3 << endl; 33 34 return 0;
2016-03-19
原文:char *get2String(long num) //得到二进制形
2016-03-19
原文:式的字符串 6 { 7 int i=0; 8 char* buffer;
以上是关于《C和C++程序员面试秘笈[精品]》-笔记的主要内容,如果未能解决你的问题,请参考以下文章
《Visual C++ 2010程序设计案例教程[精品]》-笔记