现在的位置: 首页 > 综合 > 正文

c_c++基础问题(平时读书时笔记)

2018年03月31日 ⁄ 综合 ⁄ 共 9437字 ⁄ 字号 评论关闭
1 IP私有地址:
10.0.0.0 -- 10.255.255.255
172.16.0.0 -- 172.31.255.255
192.168.0.0 -- 192.168.255.255

2OS中页面调入调出顺序:好比上地铁,旧的人先下来,新的人才能上去,所以页面调出为先,再是页面调入。页面调出之前又必须决定哪些页面需要调用。

3 数据库是的记录可以重复,主键要求既是唯一的,也是非空的,数据库为每个主键默认建立索引,但用户可以通过CREATE INDEX另建其他索引,所以索引个数不唯一,非主键亦可有索引

进入目录都要x权限(执行权限),查看目录下的文件需要r权限(读权限)和x权限,因为相当于进入了目录。执行目录下某个可执行文件,需要进入目录的x权限,以及对该执行文件的x权限。

5 在做sizeof相关题时,要注意是32位机器,还是64位机器

程序启动
跟程序运行的区别,启动的时候不用加载动态链接库,运行的时候需要查找动态链接库中函数

7 类与类之间的继承覆盖问题:主要在于指针对象所指向的类,而确定所调用的函数

1 x = x +1 ,x += 1, x ++ 那个效率最高?

x++效率最高。
x = x + 1  先读取右x的地址,执行x + 1,在读取左x的地址,将右值传给左边的x
x += 1  读取x的地址,执行x+1, 再将得到的值传给x
x++ 读取x的地址,x自增

2 a, b 交换
a = a^b; b = a^b; a = a^b;      // a ^= b; b ^= a; a ^= b;
OR
a = a + b; b = a + b; a = a - b;

3 定义一个空类型,用sizeof测试该类型,结果为多少?
结果为1
空类型中不包含任何信息,本来求sizeof应该为0,但是当我们声明该类型的实例的时候,他必须在内存中占有一定的空间,否则无法使用这些实例,至于占多少内存,由编译器决定
若在该类型添加一个构造函数和析构函数,再对该类型求sizeof,得到的结果是多少?
结果为1
因为调用构造函数和析构函数只需知道函数的地址即可,而这些函数至于类型有关,而与类型的实例无关,编译器也不会因为这两个函数而在实例内添加任何额外的信息
如果把析构函数标记为虚函数呢?
在C++编译器一旦发现一个类型中有虚函数,就会为该类型生成一个虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针。在32机器上,一个指针占4个字节的空间,在64位机器上,一个指针占8个字节的空间

4.如下面试题
class A
{
public :
     A() {}
     A( int value ) : m_value( value ) {}
     A( A other ) { m_value = other.m_value; }
private :
     int m_value;
};
int main( void )
{
     A a;
     A b( a  );
     return 0;
}

该运行结果为编译错误,因为在拷贝构造函数中的参数不能为值传递,如果用值传递,为了构造形参对象,则会不断的递归调用拷贝构造函数,最后导致堆栈溢出。所以C++标准不能拷贝构造函数用值传递传递形参。可以这样: A( const A& other)

5.找错题

对内存的考查主要集中在:
1.指针的理解
2.变量的生存期及作用范围
3.良好的动态内存申请和释放习惯
4.要时时刻刻注意指针的野指针问题和指针的内存泄露问题
5.字符串以'\0'结尾
6.对数组越界把握的敏感度
7.库函数strcpy的工作方式,如何编写一个正确strcpy函数

实现库函数strcpy
char *strcpy( char *strDst, const char *strSrc )                //函数返回值类型为char*目的是是为了实现链式表达式
{
     assert( strDst != NULL && strSrc != NULL );
     char *dst = strDst;
     while( *strDst++ = *strSrc++ )
          ;
     *strDst = '\0';

     return dst;
}

数组字符串的复制问题

void test1()
{
     char string[10];
     char* str1 = "0123456789";     //strlen(str1) = 11
     strcpy( string, str1 );              //strlen(string)  = 10< strlen(str1) = 11,拷贝字符串时导致数组string溢出,不能正确地将str1复制到string数组中

}

void test2()
{
     char string[10], str1[10];
     int i;

     for(i=0; i<10; i++)
     {
       str1[i] = 'a';                    //字符串str不能在循环中结束
     }
     strcpy( string, str1 );         //str1字符数组中没有'\0',不满足strcpy函数形参的要求,不能正确的完成将str1复制到string中
}

void test3(char* str1)
{
     char string[10];
     if( strlen( str1 ) <= 10 )          //应该讲条件判断语句改为strlen(str1) < 10,因为strlen(str1)求的长度中不包括'\0'     
     {
       strcpy( string, str1 );

     }
内存操作的考虑
void GetMemory( char *p )            //传入函数的参数是实参的一份拷贝,对形参的操作,不改变实参
{
     p = (char *) malloc( 100 );
}

void Test( void )
{
     char *str = NULL;
     GetMemory( str );                             //此时实参str仍然没有变化,即str = NULL,所以不能将一个字符串复制到空指针
     strcpy( str, "hello world" );
     printf( str );
}

char *GetMemory( void )
{
     char p[] = "hello world";                        //数组p为函数内部的局部变量,当函数结束时,p所存储的内容也就释放掉啦,此时指针p指向的位置不能确定,有可能是系统区
     return p;
}

void Test( void )
{
     char *str = NULL;
     str = GetMemory();                                    //打印str的信息不能确定
     printf( str );
}

void GetMemory( char **p, int num )
{
     *p = (char *) malloc( num );                          //没有进行对str是否分配成功的判断
     //if( p == NULL )

     //.....进行内存失败的处理
}

void Test( void )
{
     char *str = NULL;
     GetMemory( &str, 100 );
     strcpy( str, "hello" );                                        //未释放申请的内存空间,导致内存泄露
     printf( str ); 

     //free( str );
}

void Test( void )
{
     char *str = (char *) malloc( 100 );                    //没有进行对str是否分配成功的判断

     //if( p == NULL )
     //.....进行内存失败的处理

     strcpy( str, "hello" );
     free( str );
     ... //省略的其它语句


swap( int* p1,int* p2 )
{
     int * p;                                    //指针p没有初始化,没有指向确定的内存空间,是一个野指针,将其改为     int     p;就可以啦
      *p = *p1;                                // p = *p1;
     *p1 = *p2;                               // *p1 = *p2;
     *p2 = *p;                                // *p2 = p;


6.数组名的实质
1>.数组名指代一种数据结构,这种数据结构就是数组
2>.数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能做自增,自减等操作,不能被修改;
eg:     char str[ 10 ];
p++;           //编译错误,指针常量不能进行自增操作
3>.数组名作为函数实参时,沦为普通指针

7.写一个"标准"宏MIN(注意宏末尾不能添加分号)
#define     MIN( a, b )     ((a) < (b) ? (a) : (b) )

8. 为什么标准头文件都有类似以下的结构? 
#ifndef __INCvxWorksh
#define __INCvxWorksh
#ifdef __cplusplus

extern "C" {
#endif
/*...*/
#ifdef __cplusplus
}

#endif
#endif /* __INCvxWorksh */ 


头文件中编译宏
#ifndef __INCvxWorksh
#define __INCvxWorksh 
#ifdef __cplusplus
是为了防止被重复引用
下面代码的作用解析如下:
C++作为面向对象编程语言,支持函数重载,而C语言作为过程式语言,不支持函数的重载,比如以下函数
void foo( int );       该函数在C语言编译器中的名字为_foo,而在C++编译器中的名字就是_foo_int之类的名字,C++利用这用命名机制来实现函数重载。为了实现C与C++的混合编程,C++提供了C连接交换指定符号extern "C"来解决名字匹配问题,函数声明前加上extern
"C"后,则编译器就会按照C语言的方式将该函数编译为_foo,这样C语言中就可以调用C++的函数了。

9类型转换 
unsigned int a = 6;
int b = -20;
char c;
( ( a + b ) > 6 ) ? ( c = 1 ) : ( c = 0)
return c;
c = 1;
int类型与unsigned int类型相加后,运算结果自动转化为unsigned int类型(无符号类型保护 )而-14在转换为unsigned int后是一个很大的整数,a + b 肯定大于6

9. #include "filename.h" 与 #include <filename.h> 的区别
前一种编译器从用户的工作路径搜索filename.h,若找不到,再按标准方式查找
后一种编译器从标准库路径开始搜索filename.h,称为标准方式

11.用处理器指令#define声明一个常数,用以表明一年又多少秒
#define PER_YEAR_SECOND  ( 365 * 24 * 60 * 60 )UL

12.const用法与#define 的区别
const可以修饰函数的参数和返回值,甚至为函数的定义体(C++ 中可以),被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性
1>编译器可以对const进行类型安全检查,而宏只进行字符转换,没有类型检查,并且在字符替换中可能会产生意料不到的结果(副作用的存在)
2>有些IDE可以对const常量进行调试,但是不能对宏常量进行调试,在C++程序中只是用const常量,而不是用宏常量
宏与typedef的区别
1>#define是在预编译时用字符串代替另一个字符串,而typedef是为类型定义一个别名,并不是简单的字符串替换
2>宏替换的句尾没有分号作为结束语句 的标志,而typedef的句尾符号必须跟上;

13 sizeof 与strlen 的区别
1>sizeof是运算符,strlen是函数
2>strlen是有效字符串长度,不包含'\0',与初始化有关系,而sizeof与初始化无关
3>sizeof可以用类型做参数,用来计算类型占内存大小,strlen只能是char*类型做参数,且必须是以'\0'来结束,用来计算字符串的长度
4>数组做strlen的参数不会退化,传递给sizeof就退化成指针     ·
5>sizeof在编译时计算,erstrlen的结果在运行时才能计算出来

14 static全局变量与普通变量的区别
static全局变量与普通变量的区别是:static全局变量初始化一次,防止在其他文件单元被使用
static局部变量和普通变量局部变量的区别:static局部变量只能被初始化一次,下一次根据上一次结果值
static函数与普通函数的区别:static函数在内存中只有一份,普通函数在每次被调用中维持一份复制品

15 宏与内联函数的区别
1>宏是有预处理器对宏进行替代,而内联函数是真正的函数,是通过编译器控制来实现的
2>宏没有类型识别,内联函数可以做参数类型的检查
3>内联函数是真正的函数,只是在需要用到的时候,内联函数就像宏一样的展开,所以取消了函数的参数压栈,较少了调用开销
inline:在设计一般将规模比较小,频繁被调用的成员函数设计成内联函数
优缺点:增大了空间开销,提高了时间效率

16 枚举与宏的区别
1>宏常量是在预编译阶段进行简单替换,枚举常量则是在变异的时候确定其值
2>一般在编译器里,可以调试枚举常量,但是不能调试宏常量
3>枚举可以一次定义大量相关的常量,而宏一次只能定义一个所谓枚举常量
枚举常量的优点
枚举常量相当于一个符号常量,因此具有见名知意的好处,可以增加程序的可读性
枚举常量的变量取值范围列出的枚举常量范围,若取值不在列出的范围内,系统会视为错误,这样可以帮助系统检查错误,降低程序的理解程度
3>枚举类型便于系统对枚举变量进行类型检查,从而增强了安全性

17 指针与引用的区别
指针通过某个指针变量指向一个对象后,对他所指向的变量间接操作。程序中使用指针,程序的可读性差,而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。此外,对函数值引用与传指针的区别
1>引用一旦被初始化则不能改变其指向,而指针可以(除const指针)
2>引用定义时,必须初始化,而指针不需要
3>没有指向空的引用,而指针可以指向空
4>引用只是变量的别名,不额外分配空间,而指针要分配4个字节的空间
5>引用使用时无需要解引用,而指针需要解引用
6>引用没有const,而指针有const
7>sizeof引用得到的是所指向的变量(对象)的大小,而sizeof指针得到是指针本身的大小
引用与值传递的区别
值传递传递的是一个值的拷贝(副本),函数对形参的操作不会影响实参的值,而引用传递的是引用对象的内存地址,函数对形参的操作会影响实参的值将会随形参的改变而同时进行更改
引用的创建和销毁不会调用类的拷贝构造函数,const指针仍然存在空指针并且有可能产生野指针
引用既具有指针的效率,又具有变量使用的方便性和直观性

18 迷途指针与空指针的区别
当delete一个指针的时候,实际上仅是让编译器释放内存,但指针本身仍然存在,这时它是一个迷途指针,当把一个迷途指针置空后,就能把该指针改为空指针

19 C++有了malloc/free为什么还需要delete/new
malloc/free是C/C++语言的标准库函数,new/free是C++中的运算符,他们都用于申请动态内存和释放内存
对于非内建数据类型的对象而言,用malloc/free无法满足动态对象的要求,对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数不是运算符,不在编译器权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free
     因此C/C++需要一个能完成动态内存分配和初始化工作的运算符new,一集一个清理与释放内存工作的运算delete

20 句柄与指针的区别
句柄和指针是两个截然不同的概念,句柄是windows系统标记系统资源,用句柄隐藏系统的信息。你只要知道有这个东西,然后只调用就可以啦,他是32为的unit,指针则将记录物理内存地址

21 析构函数可以加virtual,而构造函数不能为虚函数
虚函数采用一种虚调用的办法,虚调用是一种可以在只有部分信息情况下工作的机制,特别允许我们调用一个只知道接口而不知道其准确对象类型的函数,但是要创建一个对象,你务必要知道对象的类型,因此构造函数不能为虚函数

22 重载 覆盖 隐藏
重载 是指同一作用域的不同函数使用相同的函数名,但是函数的参数个数或类型不同。即不同的函数使用同一标识符,并且这些函数位于同一作用域(一个空间具有相同名字,不同参数的一些函数)
覆盖 是指在派生类中队基类的虚函数重新实现,即函数名和参数都一样,只是函数的实现体不一样。它与虚函数息息相关(即,派生类对基类的操作进行个性化定制)
函数的覆盖

23  OS中的Cache和CPU中的Cache
  快表--Cache在OS中运用的典型范例   
  在操作系统中,为提高系统的存取速度,在地址映射机制中增加一个小容量的联想寄存器(相联存储器),即快表,用来存取当前访问最频繁的少数活动页面的页号。
  快表查找内存块的物理地址消耗的时间大大降低了,使得系统效率得到了极大地提高
  高速缓冲存储器----Cache在CPU中运用的典型范例。Cache位于CPU与内存之间的临时存储器

24 DOS是个单任务,单用户的操作系统,不支持权限的设置
    WIndowsNT 是有权限设置的:管理员组,高级用户组,普通用户组,来宾用户,所有的用户

25  存储过程与函数的区别:
     存储过程是用户定义的一系列SQL语句的集合,涉及特定表或其他对象的任务,用户可以调用存储过程。
    而函数通常是数据库已定义的方法,它接收参数并返回某种类型的值,并且不涉及特定用户表

26  static全局变量与普通的全局变量有什么区别:
     static全局变量只初使化一次,防止在其他文件单元中被引用;   
     static局部变量和普通局部变量有什么区别:
     static局部变量只被初始化一次,下一次依据上一次结果值;   
     static函数与普通函数有什么区别:
     static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝

27 Template有什么特点?什么时候用?
  答: Template可以独立于任何特定的类型编写代码,是泛型编程的基础.
     当我们编写的类和函数能够多态的用于跨越编译时不相关的类型时,用Template.
     模板主要用于STL中的容器,算法,迭代器等以及模板元编程.
(C++的template是实现在库设计和嵌入式设计中的关键。
template能实现抽象和效率的结合;同时template还能有效地防止代码膨胀)

28 什么是预编译,何时需要预编译:

   就是指程序执行前的一些预处理工作,主要指#表示的.

何时需要预编译?

 1)、总是使用不经常改动的大型代码体。 

 2)、程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。

29 关键字volatile有什么含意?并举出三个不同的例子?
答: 提示编译器对象的值可能在编译器未监测到的情况下改变。
   例子: 硬件时钟;多线程中被多个任务共享的变量等 

30 C++:memset ,memcpy 和strcpy 的根本区别? 

答:#include "memory.h"

memset用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为' '或'';例:char a[100];memset(a, '', sizeof(a)); 

memcpy用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度;例:char a[100],b[50]; memcpy(b, a, sizeof(b));注意如用sizeof(a),会造成b的内存地址溢出。 

strcpy就只能拷贝字符串了,它遇到'\0'就结束拷贝;例:char a[100],b[50];strcpy(a,b);如用strcpy(b,a),要注意a中的字符串长度(第一个'\0'之前)是否超过50位,如超过,则会造成b的内存地址溢出。

31 ASSERT()是干什么用的

答:ASSERT()是一个调试程序时经常使用的宏,在程序运行时它计算括号内的表达式,如果表达式为FALSE (0), 程序将报告错误,并终止执行。如果表达式不为0,则继续执行后面的语句。这个宏通常原来判断程序中是否出现了明显非法的数据,如果出现了终止程序以免导致严重后果,同时也便于查找错误。


32 在函数签名匹配时,如果有模板函数,则首先选择模板函数,再选择非模板函数
    析构函数必须为公有的

33 malloc工作原理
malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用malloc函数时,它沿连接表寻找一个大到足以满足 用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传 给用户,并将剩下的那块(如果有的话)返回到连接表上。调用free函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多的小内存片
段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了。于是,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检 查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大的内存块。

37 在多态中,要注意指针的类型,指针所指向数据的类型
   STL的游标处理,erase已经将Index破坏掉,需要用新的Index,否则下一循环的++Index被破坏掉
 将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?
在内存中不产生被返回值的副本;

(1)不能返回局部变量的引用。

(2)不能返回函数内部new分配的内存的引用。

(3)可以返回类成员的引用,但最好是const。

(4)流操作符重载返回值申明为“引用”的作用:

流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << “hello” << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。赋值操作符=。这个操作符象流操作符一样,是可以连续使用的,例如:x
= j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。

38  什么时候需要“引用”?

流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、

抱歉!评论已关闭.