C++语言疑点难点记录分析

1:一些代码

(1)获取本地时间:              
   QDateTime current_date_time =QDateTime::currentDateTime();

  QString current_date =current_date_time.toString("hh_mm_ss_zzz");

 (2)创建目录

m_scan_picture_target_dir = QDir::currentPath() + "/picture_targets/auto_scan_picture_" + current_date;

                        QDir dir;

                        //目录不存在创建目录

                        if (!dir.exists(m_scan_picture_dir))

                        {

                             bool res = dir.mkpath(m_scan_picture_dir);

                        }

                        if (!dir.exists(m_scan_picture_target_dir))

                        {

                             bool res = dir.mkpath(m_scan_picture_target_dir);

                        }

(3)/文件保存路径

                    fileName = m_scan_picture_dir + "/" + fileName;

                    template_picture_dir = QDir::currentPath() + "/Template_picture/auto_scan_template" + QString::number(m_pictureInfo[0].m_pic_index) +  ".jpeg";

                    QFileInfo fileTemplate(template_picture_dir);

                    m_pictureInfo[0].img.save(fileName,"JPEG",100);

2:logger日志用法记录

 main.cpp

#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/spdlog.h"
#include <iostream>
int main()
{
  // logger
  auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
  if (!console_sink)
  {
    std::cout << "create console log error" << std::endl;
    exit(0);
  }
  console_sink->set_level(spdlog::level::debug);
  console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");

  auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
      "logs/errors.txt", 1024 * 1024 * 10, 3);
  if (!rotating_sink) {
    std::cout << "create file log error" << std::endl;
    exit(0);
  }
  rotating_sink->set_level(spdlog::level::err);

  std::vector<spdlog::sink_ptr> sinks{console_sink, rotating_sink};
  auto logger =
      std::make_shared<spdlog::logger>("logger", sinks.begin(), sinks.end());
  logger->set_level(spdlog::level::debug);
   logger->set_level(spdlog::level::info);
  spdlog::register_logger(logger);
}

makefile

BINOBJ    := ./logger_test


CC = g++

COMPILE_FLAG = -L. -fPIC -std=c++11

CFLAGS = $(COMPILE_FLAG) -fPIC -Wall -pipe -I.

OBJS =    main.o


SRCS = $(OBJS:.o=.cpp)


Test : $(OBJS)

    $(CC) -o $(BINOBJ) $(OBJS) $(COMPILE_FLAG)


%.o: %.c

    $(CC) $(CFLAGS) -c -o $@ $<


%.o: %.cpp

    $(CC) $(CFLAGS) -c -o $@ $<


clean:

    rm $(OBJS)

    rm $(BINOBJ)

3:在QT程序中可以调用被C 编译器编译后的函数吗?如果可以怎么实现?

答:可以,通过加“extern "C"” 连接申明,被extern "C"修饰的变量和函数是按照C语言方式编译和连接的

4:结构与联合有和区别?

答:

《1》结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。

《2》 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。
5:全局变量和局部变量可否同名,他们是否有区别?如果有,是什么区别?     

 答:可以同名,从生命周期来说全局变量存活于整个程序运行周期,而局部变量只存在所在函数的运行周期,当函数返回后,局部变量也消失,全局变量储存在静态数据库,局部变量在堆栈。

6:static有什么用途?static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?

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

    2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
    3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错 误。从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。 static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和 定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件 static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用; static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值; static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝

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

预编译又称为预处理,是做些代码文本的替换工作。 处理#开头的指令,比如拷贝#include 包含的文件代码, #define 宏定义的替换,条件编译等, 就是为编译做的预备工作的阶段,主要处理#开始的预编译指令,预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。
c 编译系统在对程序进行通常的编译之前,先进行预处理。 c 提供的预处理功能主要有以下三种:1)宏定义  2)文件包含  3)条件编译
1、总是使用不经常改动的大型代码体。
2、程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。

8:头文件中有如下宏定义,
#define DECLARE_TASKLET(name, func, data)  \   
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
.c源文件中有如下语句,static DECLARE_TASKLET(btn_tasklet, btn_tasklet_func, (unsigned long)&mydata);
请写出预编译后,该语句展开的结果.
static struct tasklet_struct btn_tasklet = { NULL, 0, ATOMIC_INIT(0), btn_tasklet_func, ( unsigned long)&mydata };

9:以递归方式反序输出一个字符串。如给定字符串“abc”输出“cba ”。
#include<stdio.h>
void reverse(char *p)
{
if( *p == ‘\0′)
return;
reverse(p+1);
printf(“%c”,*p);
}
int main()
{
reverse(“abc”);
printf(“\n”);
return 0;
}

10:参数传递有哪些形式?寄存器和堆栈传递各有什么优缺点?【考点】编译优化、调用性能、接口设计。
答案:每种体系结构及对应的编译器对参数传递都有自己的规定。参数传递并非总是通过堆栈进行的,参数入栈出栈是需要耗费时间的,编译器总是尽量优化利用寄存器来 传递参数,因为寄存器的访问效率要高,但当参数过多时,将放弃优化从而用栈传递参数。因此为了提高调用性能,应尽量减少参数个数,太多时可以将所有参数重 新定义为一个结构体,利用结构体指针来传递参数。在函数接口设计时应考虑硬件平台和编译器的特性,以灵活定义参数形式
11:Define宏语句和inline函数有什么区别?【考点】时空效率及宏的副作用。
答:嵌入式系统平台通常存储资源有限,但同时又对实时性有一定的要求,二者如何权衡需要考虑。Define宏语句相对于函数调用能提高运行时间性能,但消耗了 空间,并且不标准的宏语句定义在不标准的编码中更容易出现副作用,因此inline函数则是define语句的完美替代品。

12:关于动态申请内存的问题
void GetMemory(char *p)
{
p = (char *)malloc(100);
}

13:请说出 const 与#define相比,有何优点?
Const 作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被 Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
1)const  常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
2)有些集成化的调试工具可以对 const  常量进行调试,但是不能对宏常量进行调试。

14:简述数组与指针的区别?
数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。
(1)修改内容上的差别
char a[] =  “hello”;
a[0] =  ‘X’;
char *p =  “world”; //  注意 p  指向常量字符串
p[0] =  ‘X’; //  编译器不能发现该错误,运行时错误
(2)  用运算符 sizeof  可以计算出数组的容量(字节数)。sizeof(p),p  为指针得到的是一个指针变量的字节数,而不是 p  所指的内存容量。 C++/C  语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。
char a[] = "hello world";
char *p = a;
cout<< sizeof(a) << endl; // 12  字节
cout<< sizeof(p) << endl; // 4  字节
计算数组和指针的内存容量
void Func(char a[100])
{
cout<< sizeof(a) << endl; // 4  字节而不是 100  字节
}
void Test(void)
{
char *str = NULL;
GetMemory(str);  
strcpy(str, "hello world");
printf(str);
}
请问运行 Test 函数会有什么样的结果?
传入 GetMemory(  char *p  )函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,执行完  char *str = NULL; GetMemory( str );  后的 str 仍然为 NULL;

15:写标准 strlen 函数
对 strlen 的掌握,它没有包括字符串末尾的'\0'。读者看了不同分值的 strcpy 版本,应该也可以写出一个 10 分的 strlen函数了,完美的版本为:
int strlen( const char *str ) //输入参数 const  以下是引用片段:
{
assert( strt != NULL ); //断言字符串地址非 0
int len=0; //注,一定要初始化。
while( (*str++) != '\0' )
{
len++;
}
return len;
}
16:如何引用一个已经定义过的全局变量?
答 :可以用引用头文件的方式,也可以用 extern 关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用 extern 方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。

17:写一个“标准”宏 MIN
#define min(a,b)       ((a)<=(b)?(a):(b))
注意:在调用时一定要注意这个宏定义的副作用,如下调用: ((++*p)<=(x)?(++*p):(x)。p 指针就自加了两次,违背了 MIN 的本意。
18:typedef 和 define 有什么区别
(1)用法不同:typedef 用来定义一种数据类型的别名,增强程序的可读性。define 主要用来定义常量,以及书写复杂使用频繁的宏。
(2)执行时间不同:typedef 是编译过程的一部分,有类型检查的功能。define 是宏定义,是预编译的部分,其发生在编译之前,只是简单的进行字符串的替换,不进行类型的检查。
(3)作用域不同:typedef 有作用域限定。define 不受作用域约束,只要是在 define 声明后的引用都是正确的。
(4)对指针的操作不同:t ypedef 和 define 定义的指针时有很大的区别。
注意:typedef 定义是语句,因为句尾要加上分号。而 define 不是语句,千万不能在句尾加分号。

19:谈谈你对面向对象的认识
面向对象可以理解成对待每一个问题,都是首先要确定这个问题由几个部分组成,而每一个部分其实就是一个对象。然后再分别设计这些对象,最后得到整个程序。传统的程序设计多是基于功能的思想来进行考虑和设计的,而面向对象的程序设计则是基于对象的角度来考虑问题。这样做能够使得程序更加的简洁清晰。
说明:编程中接触最多的“面向对象编程技术”仅仅是面向对象技术中的一个组成部分。发挥面向对象技术的优势是一个综合的技术问题,不仅需要面向对象的分析,设计和编程技术,而且需要借助必要的建模和开发工具。
 
20:什么是“引用”?申明和使用“引用”要注意哪些问题?将“引用”作为函数参数有哪些特点?在什么时候需要使用“引用”? 指针和引用的区别
答:
1. 引用就是某个目标变量的“别名”(alias),对引用的操作与对变量直接操作效果完全相同。
2. 申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。
3.(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
4. 流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。
5. 指针指向一块内存,它的内容是指向内存的地址;引用是某内存的别名
引用使用是无需解引用,指针需解引用
引用不能为空,指针可以为空
引用在定义是被初始化一次,之后不可变;指针可变
程序为指针变量分配内存区域,而引用不需要分配内存区域
(1)、从现象上看:指针在运行时可以改变其所指向的值,而引用一旦和某个对象绑定后就不再改变。
(2)、从内存分配上看:程序为指针变量分配内存区域,而引用不分配内存区域。
(3)、从编译上看:程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地 址值,而引用在符号表上对应的地址值为引用对象的地址值。符号表生成后就不会再改,因此指针可以改变指向的对象(指针变量中的值可以改),而引用对象不能 改。
引用:一个变量的别名,为什么引入别名呢?原因是我们想定义一个变量,他共享另一个变量的内存空间,使用别名无疑是一个好的选择。变量是什么?是一个内存空间的名字,如果我们给这个内存空间在起另外一个名字,那就是能够共享这个内存了,引用(别名)的由此而来。
指针:指向另一个内存空间的变量,我们可以通过它来索引另一个内存空间的内容,本身有自己的内存空间
 
21:New delete 与malloc free 的联系与区别?
面试中可以这样回答:
都是在堆(heap)上进行动态的内存操作。用malloc函数需要指定内存分配的字节数并且不能初始化对象,new 会自动调用对象的构造函数。delete 会调用对象的destructor,而free 不会调用对象的destructor.
详细讲解:
① malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都用于在堆(heap)上进行动态的内存操作(申请动态内存和释放内存)。
② delete不仅会释放空间,在释放前会调用析构函数,和new对应,new调用构造函数,free只会释放内存。
③ 对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象      在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符del

22:类成员函数的重载、覆盖和隐藏区别?
a.成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。
b.覆盖是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字。
c.“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)

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

24:请讲一讲析构函数和虚函数的用法和作用?构造函数可否是虚汗数,为什么?析构函数呢,可否是纯虚的呢?
析构函数是在类对象死亡时由系统自动调用, 其作用是用来释放对象的指针数据成员所指的动态空间,如果在构造函数中,你申请了动态空间,那么为了避免引起程序错误,你必须在析构函数中释放这部分内存空间。如果基类的函数用 virtual 修饰,成为虚函数,则其派生类相应的重载函数仍能继承该虚函数的性质,虚函数进行动态联编,也即具有多态性,也就是派生类可以改变基类同名函数的行为,在面向对象世界中, 多态是最强大的机制, 虚函数就是这一机制的 c++实现方式 构造函数不能为虚函数,要构造一个对象,必须清楚地知道要构造什么,否则无法构造一个对象。析构函数可以为纯虚函数。

25:多态的作用?
答:主要是两个:1. 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;2. 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。

26:请说出 static 和 const 关键字尽可能多的作用
static 关键字至少有下列 n 个作用:
(1)函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
(2)在模块内的 static 全局变量可以被模块内所用函数访问, 但不能被模块外其它函数访问;
(3)在模块内的 static 函数只可被这一模块内的其它函数调用, 这个函数的使用范围被限制在声明它的模块内;
(4)在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
(5)在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的 static 成员变量。
const 关键字至少有下列 n 个作用:
(1)欲阻止一个变量被改变,可以使用 const 关键字。在定义该 const 变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本身为 const,也可以指定指针所指的数据为 const,或二者同时指定为 const;
(3)在一个函数声明中,const 可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为 const 类型,则表明其是一个常函数,不能修改类的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为 const 类型,以使得其返回值不为“左值” 。例如:
const classA operator*(const classA& a1,const classA& a2);
operator*的返回结果必须是一个 const 对象。如果不是,这样的变态代码也不会编译出错:
classA a, b, c;
(a * b) = c; // 对 a*b 的结果赋值
操作(a * b) =c 显然不符合编程者的初衷,也没有任何意义。

27:关键字 const 有什么含意?下述三个有什么区别?
char * const p;
char const * p
const char *p

const int a;

int const a;

const int *a;

int * const a;

int const * a const;

前两个的作用是一样,a 是一个常整型数。第三个意味着 a 是一个指向常整型数的指针(也

就是,整型数是不可修改的,但指针可以)。第四个意思 a 是一个指向整型数的常指针(也

就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着 a 是一

个指向常整型数的常指针 (也就是说, 指针指向的整型数是不可修改的, 同时指针也是不可修改的) 。 如果应试者能正确回答这些问题, 那么他就给我留下了一个好印象。 顺带提一句,也许你可能会问,即使不用关键字  const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字 const 呢?我也如下的几下理由:

1).  关键字 const 的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃

圾,你就会很快学会感谢这点多余的信息。(当然,懂得用 const 的程序员很少会留下的垃

圾让别人来清理的。)

2).  通过给优化器一些附加的信息,使用关键字 const 也许能产生更紧凑的代码。

3).  合理地使用关键字 const 可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少 bug 的出现。

最终回答:

1)表示常量不可以修改的变量。

2)可以修饰参数,作为输入参数.

3)修饰函数,防止以外的改动.

4)修饰类的成员函数,不改变类中的数据成员.

char * const p; //常量指针,p 的值不可以修改

char const * p;//指向常量的指针,指向的常量值不可以改

const char *p;//和 char const *p

28:关键字 volatile 有什么含意?并举出三个不同的例子?
答:
一个定义为 volatile 的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去
假设这个变量的值了。精确地说就是, 优化器在用到这个变量时必须每次都小心地重新读取这个变量的值, 而不是使用保存在寄存器里的备份。
下面是 volatile 变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
回答不出这个问题的人是不会被雇佣的。 我认为这是区分 C 程序员和嵌入式系统程序员最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS 等等打交道,所用这些都要求 volatile 变量。不懂得 volatile 内容将会带来灾难。
 29:用宏定义写出 swap(x,y)
答#define swap(x, y)
x = x + y;
y = x - y;
x = x - y;

30:编码实现冒泡排序
#include <stdio.h>
#define LEN 10                //数组长度
void main( void )
{
int ARRAY[10] = { 0, 6, 3, 2, 7, 5, 4, 9, 1, 8 };  //待排序数组
printf( "\n" );
for( int a = 0; a < LEN; a++ )        //打印数组内容
{
printf( "%d ", ARRAY[a] );
}
int i = 0;
int j = 0;
bool isChange;               //设定交换标志
for( i = 1; i < LEN; i++ )
{                  //最多做 LEN-1 趟排序
isChange  = 0;            //本趟排序开始前,交换标志应为假
for( j = LEN-1; j >= i; j-- )        //对当前无序区 ARRAY[i..LEN]自下向上扫描
{
if( ARRAY[j+1] < ARRAY[j] )
{              //交换记录
ARRAY[0] = ARRAY[j+1];    //ARRAY[0]不是哨兵,仅做暂存单元
ARRAY[j+1] = ARRAY[j];
ARRAY[j] = ARRAY[0];
isChange  = 1;        //发生了交换,故将交换标志置为真
}
}
printf( "\n" );
for( a = 0; a < LEN; a++)        //打印本次排序后数组内容
{
printf( "%d ", ARRAY[a] );
}
if( !isChange )            //本趟排序未发生交换,提前终止算法
{
break;
}
}
printf( "\n" );
return;
}

31:介绍一下你对设计模式的理解
(这个过程中有很多很细节的问题随机问的)
  设计模式概念是由建筑设计师Christopher Alexander提出:"每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心.这样,你就能一次又一次地使用该方案而不必 做重复劳动."上述定义是对设计模式的广义定义.将其应用到面向对象软件的领域内,就形成了对设计模式的狭义定义.
 可以简单的认为:设计模式就是解决某个特定的面向对象软件问题的特定方法, 并且已经上升到理论程度。   框架与设计模式的区别:
   1,设计模式和框架针对的问题域不同.设计模式针对面向对象的问题域;框架针对特定业务的问题域
   2,设计模式比框架更为抽象.设计模式在碰到具体问题后,才能产生代码;框架已经可以用代码表示
   3,设计模式是比框架更小的体系结构元素.框架中可以包括多个设计模式设计模式就像武术中基本的招式.将这些招式合理地纵组合起来,就形成套路(框架),框架是一种半成品.

32:delete 与  delete []区别
delete 只会调用一次析构函数,而 delete[]会调用每一个成员的析构函数。在 More Effective   C++中有更为详细的解释:“当 delete 操作符用于数组时,它为每个数组元素调用析构函数,然后调用 operatordelete 来释放内存。”delete 与 New 配套,delete []与 new []配套
MemTest*mTest1=newMemTest[10];
MemTest*mTest2=newMemTest;
int*pInt1=newint[10];
int*pInt2=newint;
delete[]pInt1;   //-1-delete[]pInt2;   //-2-delete[]mTest1;//-3-delete[]mTest2;//-4-在-4-处报错。
这就说明:对于内建简单数据类型,delete 和 delete[]功能是相同的。对于自定义的复杂数据类型, delete 和 delete[]不能互用。 delete[]删除一个数组,delete 删除一个指针简单来说,用 new 分配的内存用 delete 删除用 new[]分配的内存用 delete[]删除 delete[]会调用数组元素的析构函数。内部数据类型没有析构函数,所以问题不大。如果你在用 delete 时没用括号,delete就会认为指向的是单个对象,否则,它就会认为指向的是一个数组。

33:继承优缺点。
类继承是在编译时刻静态定义的,且可直接使用,类继承可以较方便地改变父类的实现。但是类继承也有一些不足之处。首先,因为继承在编译时刻就定义了,所以无法在运行时刻改变从父类继承的实现。更糟的是,父类通常至少定义了子类的部分行为,父类的任何改变都可能影响子类的行为。如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。

34:子类析构时要调用父类的析构函数吗?
析构函数调用的次序是先派生类的析构后基类的析构,也就是说在基类的的析构调用的时候,派生类的信息已经全部销毁了定义一个对象时先调用基类的构造函数、然后调用派生类的构造函数;析构的时候恰好相反:先调用派生类的析构函数、然后调用基类的析构函数 JAVA 无析构函数深拷贝和浅拷贝
 
35:关联、聚合(Aggregation)以及组合(Composition)的区别?
涉及到 UML 中的一些概念:关联是表示两个类的一般性联系,比如“学生”和“老师”就是一种关联关系;聚合表示 has-a 的关系,是一种相对松散的关系,聚合类不需要对被聚合类负责,如下图所示,用空的菱形表示聚合关系:从实现的角度讲,聚合可以表示为:
class A {...}   class B { A* a; .....}
而组合表示 contains-a 的关系,关联性强于聚合:组合类与被组合类有相同的生命周期,组合类要对被组合类负责,采用实心的菱形表示组合关系:
实现的形式是:
class A{...} class B{ A a; ...}

36:在 C++  程序中调用被 C  编译器编译后的函数,为什么要加 extern “C”?
C++语言支持函数重载,C 语言不支持函数重载。函数被 C++编译后在库中的名字与 C 语言的不同。假设某个函数的原型为:  void foo(int  x,  int  y);该函数被 C 编译器编译后在库中的名字为_foo,而 C++编译器则会产生像_foo_int_int之类的名字。 C++提供了 C 连接交换指定符号 extern“C”来解决名字匹配问题。
37:描述内存分配方式以及它们的区别?
1)   从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static  变量。
2)   在栈上创建。在执行函数时, 函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
3)  从堆上分配,亦称动态内存分配。程序在运行的时候用 malloc  或 new 申请任意多少的内存,程序员自己负责在何时用 free  或 delete  释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。
38:如何判断一段程序是由 C  编译程序还是由 C++编译程序编译的?
答案:
#ifdef __cplusplus
cout<<"c++";
#else
cout<<"c";
#endif

39:C++是不是类型安全的?
答案:不是。两个不同类型的指针之间可以强制转换(用 reinterpret cast)。C#是类型安全的。

40:Itearator 和指针的区别
游标和指针
我说过游标是指针,但不仅仅是指针。游标和指针很像,功能很像指针,但是实际上,游标是通过重载一元的”*”和”->”来从容器中间接地返回一个值。将这些值存储在容器中并不是一个好主意,因为每当一个新值添加到容器中或者有一个值从容器中删除,这些值就会失效。在某种程度上,游标可以看作是句柄(handle)。通常情况下游标(iterator)的类型可以有所变化,这样容器也会有几种不同方式的转变:
iterator——对于除了 vector 以外的其他任何容器,你可以通过这种游标在一次操作中在容器中朝向前的方向走一步。这意味着对于这种游标你只能使用“++”操作符。而不能使用“--”或“+=”操作符。而对于 vector 这一种容器,你可以使用“+=”、“—”、“++”、“-=”中的任何一种操作符和“<”、“<=”、“>”、“>=”、“==”、“!=”等比较运算符。

41:冒泡排序算法的时间复杂度是什么? 答  O(n^2)

42:C中的 malloc 和C++中的 new 有什么区别
malloc 和 new 有以下不同:
(1)new、delete  是操作符,可以重载,只能在 C++中使用。
(2)malloc、free 是函数,可以覆盖,C、C++中都可以使用。
(3)new  可以调用对象的构造函数,对应的 delete 调用相应的析构函数。
(4)malloc 仅仅分配内存,free 仅仅回收内存,并不执行构造和析构函数
(5)new、delete 返回的是某种数据类型指针,malloc、free 返回的是 void 指针。
注意:malloc 申请的内存空间要用 free 释放,而 new 申请的内存空间要用 delete 释放,不要混用。
因为两者实现的机理不同。
 
43:简述 C、C++程序编译的内存分配情况
C、C++中内存分配方式可以分为三种:
(1)从静态存储区域分配:
内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在。速度快、不容易出错,因为有系统会善后。例如全局变量,static 变量等。
(2)在栈上分配:
在执行函数时,函数内局部变量的存储单元都在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3)从堆上分配:
即动态内存分配。程序在运行的时候用 malloc 或 new 申请任意大小的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活。 如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,另外频繁地分配和释放不同大小的堆空间将会产生堆内碎块。
一个 C、C++程序编译时内存分为 5 大存储区:堆区、栈区、全局区、文字常量区、程序代码区。
 
44:C++的空类有哪些成员函数
q  缺省构造函数。
q  缺省拷贝构造函数。
q  缺省析构函数。
q  缺省赋值运算符。
q  缺省取址运算符。
q  缺省取址运算符  const。
注意:有些书上只是简单的介绍了前四个函数。没有提及后面这两个函数。但后面这两个函数也是空类的默认函数。另外需要注意的是,只有当实际使用这些函数的时候,编译器才会去定义它们。
45:谈谈你对拷贝构造函数和赋值运算符的认识
拷贝构造函数和赋值运算符重载有以下两个不同之处:
(1)拷贝构造函数生成新的类对象,而赋值运算符不能。
(2)由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不用检验源对象是否和新建对象相同。而赋值运算符则需要这个操作,另外赋值运算中如果原来的对象中有内存分配要先把内存释放掉
注意:当有类中有指针类型的成员变量时,一定要重写拷贝构造函数和赋值运算符,不要使用默认的。

46:C++的引用和 C 语言的指针有什么区别
指针和引用主要有以下区别:
(1)引用必须被初始化,但是不分配存储空间。指针不声明时初始化,在初始化的时候需要分配存储空间。
(2)引用初始化以后不能被改变,指针可以改变所指的对象。
(3)不存在指向空值的引用,但是存在指向空值的指针。
注意:引用作为函数参数时,会引发一定的问题,因为让引用作参数,目的就是想改变这个引用所指向地址的内容,而函数调用时传入的是实参,看不出函数的参数是正常变量,还是引用,因此可能会引发错误。所以使用时一定要小心谨慎。

47:如何避免“野指针”
“野指针”产生原因及解决办法如下:
(1)指针变量声明时没有被初始化。解决办法:指针声明时初始化,可以是具体的地址值,也可让它指向 NULL。
(2)指针  p  被  free   或者  delete  之后,没有置为  NULL。解决办法:指针指向的内存空间被释放后指针应该指向 NULL。
(3)指针操作超越了变量的作用范围。解决办法:在变量的作用域结束前释放掉变量的地址空间并且让指针指向 NULL。
注意:“野指针”的解决方法也是编程规范的基本原则,平时使用指针时一定要避免产生“野指针”,在使用指针前一定要检验指针的合法性。
48:常引用有什么作用
常引用的引入主要是为了避免使用变量的引用时,在不知情的情况下改变变量的值。常引用主要用于定义一个普通变量的只读属性的别名、作为函数的传入形参,避免实参在调用函数中被意外的改变。
说明:很多情况下,需要用常引用做形参,被引用对象等效于常对象, 不能在函数中改变实参的值,这样的好处是有较高的易读性和较小的出错率。
 
49:C和C++有什么不同?
① 从机制上:c是面向过程的(但c也可以编写面向对象的程序);c++是面向对象的,提供了类。但是,c++编写面向对象的程序比c容易
② 从适用的方向:c适合要求代码体积小的,效率高的场合,如嵌入式;c++适合更上层的,复杂的;linux核心大部分是c写的,因为它是系统软件,效率要求极高。
③ C语言是结构化编程语言,C++是面向对象编程语言。C++侧重于对象而不是过程,侧重于类的设计而不是逻辑的设计。
 
50:什么是浅拷贝?什么是深拷贝?
浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)其中任何一个对象的改动都会影响另外一个对象。
深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。
一般来说,浅拷贝就是复制那个对象的指针。深拷贝就是复制了那个对象。

51:C++容器的分类及各自的特性?
C++容器分为顺序容器和关联容器。二者的本质区别在于:关联容器通过键(key) 存储和读取元素,而顺序容器的元素排列次序与元素值无关,是由元素添加到容器的顺序决定的,通过元素在容器中的位置顺序存储和读取元素。
标准库定义了三种顺序容器类型,包括:vector、list、deque。
容器内的元素类型必须至少满足2个条件:可复制和可赋值。
vector是连续存储的,能支持快速随机访问,list不需要连续存储,能在中间快速插入和删除,deque是双端队列,具有后进先出的特性。
所有的迭代器范围都是左闭合区间,  [begin,end)  包括begin,但不包括end
     list迭代器不支持算术运算,不支持关系运算,也没有下标操作,只有最简单的自增 自减 相等 不等 运算。
标准库还提供了三种容器适配器:后进先出的stack、先进先出的queue和有优先级管理的priority_queue。stack可以建立在vector、list、deque上,queue要能提供push_front操作,不能建立在vector上,priority_queue要求提供随机访问能力,只能建立在vector或deque上。
 
标准库提供的关联容器有map、set、multimap和multiset。
map存储键值对,set存储单个键,multimap和multiset支持同一个键多次出现。
set与map不同的地方在于:set仅有key_type类型,它的value_type 也就是key_type;而且set不提供下标操作。map支持下标操作,而且与下标访问数组或者vector的行为截然不同:用下标访问不存在的元素将导致在map容器中添加一个新元素。

52:用find 写出查找/opt/kernel目录下(包括子目录)文件名为Kconfig的命令。
cd /opt/kernel/; find -name Kconfig 或find /opt/kernel -name Kconfig

53:变量的声明和定义有什么区别?
为变量分配地址和存储空间的称为定义,不分配地址的称为声明。一个变量可以在多个地方声明,但是只在一个地方定义。加入 extern 修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。
说明:很多时候一个变量,只是声明不分配内存空间,直到具体使用时才初始化,分配内存空间,如外部变量。
 54:链表和数组有什么区别
数组和链表有以下几点不同:
(1)存储形式:数组是一块连续的空间,声明时就要确定长度。链表是一块可不连续的动态空间,长度可变,每个结点要保存相邻结点指针。
(2)数据查找:数组的线性查找速度快,查找操作直接使用偏移地址。链表需要按顺序检索结点,效率低。
(3)数据插入或删除:链表可以快速插入和删除结点,而数组则可能需要大量数据移动。
(4)越界问题:链表不存在越界问题,数组有越界问题。
说明:在选择数组或链表数据结构时,一定要根据实际需要进行选择。数组便于查询,链表便于插入删除。数组节省空间但是长度固定,链表虽然变长但是占了更多的存储空间。

55:.h头文件中的ifndef/define/endif 的作用?

#include<file.h> 与 #include "file.h"的区别?
答:

《1》防止该头文件被重复引用。
《2》 前者是从Standard Library的路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h。

56: 假设某个函数的原型为:void foo( int x, int y ); 该函数分别被C编译器和C++编译器编译后在符号库中的名字是什么?

该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。_foo_int_int 这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒听雪落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值