学习笔记

1.以下程序的输出结果是()。

   main()
{ 
    char s[]="123",*p;
    p=s;
    printf("%c%c%c\n",*p++,*p++,*p++);
}

解析:
*p++的作用是:先解引用,获得p所指向的值,然后将p++,即p指向下一位。

(*p)++的作用是:解引用,获得p所指向的值,然后将指向的值自加,内容自加1,指针并不移动。

另:printf从最右边依次操作 输出321
2.
fprintf函数只能以字符串的形式写入到文件中;

fread函数则是从文件中读数据,而不是写数据;

fputc函数是以字符的形式写到文件中;

fwrite函数则是将某数以其机器数二进制数的形式写入到文件中
3.
后置++和->的优先级相同,从左到右结合。前置++优先级低于->
后置++比前置++优先级高;->和后置++比 * 优先级高,其他不用记。
故:语句 ++p->a; 的效果是使成员a增1
  在这里插入图片描述
4.关键字const

用const声明的变量只能只读
必须初始化
常量和指针

const int i=0; //i是常量,i的值不会被修改
const int *p1=&i; //指针p1所指内容是常量,可以不初始化
int * const p2=&j //指针p2是常量,所指内容可修改
const int * const p3i=&i; //指针p3是常量,所指内容也是常量

const 常量有数据类型,编译器可以对前者进行类型安全检查。
宏常量没有数据类型,只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
5.
类的继承后方法属性变化:

private 属性不能够被继承。

使用private继承,父类的protected和public属性在子类中变为private;私有继承是将基类的公有成员和保护成员变成自己的私有成员。

使用protected继承,父类的protected和public属性在子类中变为protected;保护继承是将基类的公有成员和保护成员变成自己的保护成员。

使用public继承,父类中的protected和public属性不发生改变; 公有继承就是将基类的公有成员变为自己的公有成员,基类的保护成员变为自己的保护成员。

private, public, protected 访问标号的访问范围:

private:只能由1.该类中的函数、2.其友元函数访问。
不能被任何其他访问,该类的对象也不能访问。

protected:可以被1.该类中的函数、2.子类的函数、以及3.其友元函数访问。
但不能被该类的对象访问。

public:可以被1.该类中的函数、2.子类的函数、3.其友元函数访问,也可以由4.该类的对象访问。

注:友元函数包括3种:设为友元的普通的非成员函数;设为友元的其他类的成员函数;设为友元类中的所有成员函数

class 默认是private;struct默认是public。
在这里插入图片描述
6.类成员函数的重载、覆盖和隐藏的区别
重载:相同的范围在同一个类中,函数名相同,参数不同,virtual关键字可省略
覆盖:不同的范围(分布基于派生类和基类),函数名相同,参数相同,基类中一定要有virtual关键字。
隐藏:是指派生类的函数屏蔽了和他同名的基类函数。
(1)派生类的函数与基类的函数同名,但是参数不同。基类的函数将被隐藏,此时不管有无关键字(注意别与重载混淆)。
(2)派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)
7.
std::vector::iterator重载的运算符:
在这里插入图片描述
8.关于虚函数:

虚函数只能是类中成员函数;

静态成员函数,可以不通过对象来调用,即没有隐藏的this指针。
virtual函数一定要通过对象(指针或引用)来调用,即有隐藏的this指针。所以虚函数不可以是static的;

一旦某个函数被声明成虚函数,则所有派生类中都是虚函数。
所以基类中说明了虚函数后,派生类中将其对应的函数可不必说明为虚函数;

派生类中虚函数的返回类型必须和基类函数匹配;
9.值传递、传地址和传引用
值传递:形参和实参占用不同的内存空间,传递的时候形参相当于一个副本,存储在另一个地址,有两个空间保存变量值(变量值相等),所以修改形参不会改变实参的值,因为根本没改变实参空间内的值。

传地址:形参和实参都是一个相同的地址,指向相同的内存空间,该空间保存变量值,所以在地址传递时可以通过地址访问改变实参变量值。

传引用:引用相当于一个别名,跟人的外号一样,指向的都是同一个人,传引用的时候虽然形式看起来跟值传递一样,但是它并不会复制一个副本进行变量保存,即只有一个空间保存变量值。

综上:传地址和传引用,形参和实参指向同一对象,修改形参会影响实参,传值则修改形参不会影响实参!

值传递:形参是实参的拷贝,改变形参的值不影响外部实参的值
指针传递:形参是指向实参地址的地址,对形参的操作实际是对实参本身操作
引用传递:形参是实参的别名,对形参的操作其实就是对实参的操作
10.
c++规定:++a 前缀运算符 a.operator() 不需要加参数。a++后缀运算符 a.operator(int) 需要加参数。

c/c++当中定义二维数组:int a[2][]={{1,2},{3,4}}是错误的; int a[][2]={{1,2},{3,4}} 是正确的! 行可省 列不能

11.对数据结构中data的处理方式:编译器会认为这就是一个长度为0的数组,而且会支持对于数组data的越界访问

struct Node
{
   int size;
   char data[0];
};

解析:
char data[0], 柔性数组,它只能放在结构体末尾,是申明一个长度为0的数组,就可以使得这个结构体是可变长的。

对于编译器来说,此时长度为0的数组并不占用空间,因为数组名本身不占空间,它只是一个偏移量, 数组名这个符号本身代 表了一个不可修改的地址常量 (注意:数组名永远都不会是指针! ),但对于这个数组的大小,我们可以进行动态分配

柔性数组是C99的扩展,简而言之就是一个在struct结构里的标识占位符(不占结构struct的空间) 比如我们需要在结构体中存放一个动态长度的字符串时,就可以用柔性数组。

用指针和用变长结构体的区别:
1).在位置方面:指针可以放在任何地方,但是变长结构体的变长部分一定要放在结构体的最后。
2).在内存占用方面:指针会占一个指针的大小的内存空间,但是变长数组是不占内存的,它只是一个占位符。
3).在内存布局方面:指针指向的内存和结构体的内存可以是不连续的,但是变长部分和结构体的内存必须是连续。
4).在内存释放方面:使用指针,就要先释放指针所指的内存在释放整个结构体的内存,否则会照成内存泄露。
但是使用变长结构体直接释放整个结构体的空间就可以了
5).一个限制:指针可以用在C++的类中,但是变长结构体就不可以了。因为有些编译器会将一些额外的信息放在类的最后,
比如vptr或者虚基类的内容,使用了变长的类,就会把这部分的值改变,这种行为是未定义的,谁也不知道会发生什么。
12.
纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”,
包含纯虚函数的类称为抽象类,抽象类是不完整的,它只能用作基类。在面向对象方法中,抽象类主要用来进行类型隐藏和充当全局变量的角色。

抽象类具有以下特性:
1.抽象类不能实例化。
2.抽象类可以包含抽象方法和抽象访问器。
3.不能用 sealed 修饰符修饰抽象类,因为这两个修饰符的含义是相反的。 采用 sealed 修饰符的类无法继承,而 abstract 修饰符要求对类进行继承。
4. 从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽象访问器的实际实现。
13.大小端
Big-Endian和Little-Endian的定义如下:

  1. Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
  2. Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
    举一个例子,比如数字0x12 34 56 78在内存中的表示形式为:

1)大端模式:
低地址 -----------------> 高地址
0x12 | 0x34 | 0x56 | 0x78

2)小端模式:
低地址 ------------------> 高地址
0x78 | 0x56 | 0x34 | 0x12

可见,大端模式和字符串的存储模式类似。

14.栈保存了一个函数调用所需要的维护信息:

  • 函数参数,函数返回值;
  • 局部变量;
  • 以及函数调用的上下文。

15.new和malloc区别:
在这里插入图片描述

16.信号量

linux下使用的进程间通信的方式主要有:管道和有名管道,信号,消息队列,共享内存,信号量,套接字
信号量是用来解决进程间的同步与互斥问题的一种进程间通信机制,包括一个称为信号量的变量和在该信号量下等待资源的进程等待队列,以及对信号量进行的两个原子操作(P/V操作)。其中,信号量对应于某一种资源,取一个非负的整形值。信号量值(常用sem_id表示)指的是当前可用的该资源的数量,若等于0则意味着目前没有可用的资源。
P操作:如果有可用的资源(信号量值>0),则此操作所在的进程占用一个资源(此时信号量值减1,进入临界区代码);如果没有可用的资源(信号量值=0),则此操作所在的进程被阻塞直到系统将资源分配给该进程(进入等待队列,一直等到资源轮到该进程)。
V操作:如果在该信号量的等待队列中有进程在等待资源,则唤醒一个阻塞进程;如果没有进程等待它,则释放一个资源(即信号量值加1)。

17.线程的优缺点是什么?什么情况不用线程?什么情况使用线程提高效率?

优点:相比进程,线程是一个更接近执行体的概念,一个进程内可以拥有多个线程,他们可以并发运行,共享地址空间,而且线程产生的速度快,线程调度之间切换也快,那么因为共享地址空间体现的优点是:系统开销小,资源率利用比较高。

缺点:线程之间的同步机制非常繁琐,(互斥锁、读写锁、条件变量、线程信号),线程之间也会发生死锁,一个线程崩溃的时候 ,会牵连到其他线程。

使用线程的情况:线程适合在对称多处理机上运行

18.buffer和cache有何区别?

答:缓存是用来加速从硬盘中读取数据的,一个程序读取了一个数据,先放在缓存里,下一个程序再来需要的时候,直接调用缓存即可,因为很显然内存的存取速度要大于硬盘嘛,而缓冲是用来加速向硬盘写入数据的,我保存数据到硬盘,不是立刻生效,而是在内存缓冲积累到一定程度后,再全部放入硬盘,避免硬盘被频繁操作,从而影响系统的运行情况。他们二者本质都是内存,不能被进程调用,只能被Linux内核使用。

19,交换函数swap的几种方法:

  1. 借助辅助变量temp的swap函数------引用类型参数
void swap(int &a,int &b)
{
	int temp;
	temp=a;
	a=b;
	b=temp;
}
  1. 借助辅助变量temp的swap函数------指针类型参数
void swap(int *a,int *b)
{
	int temp;
	temp=*a;
	*a=*b;
	*b=temp;
}
  1. 采用加减法的swap函数
void swap(int &a,int &b)
{
	a=a+b;
	b=a-b;
	a=a-b;
}
  1. 使用异或运算的函数
void swap(int &a,int &b)
{
	a=a^b;
	b=a^b;
	a=a^b;
}
  1. 其他方法
void swap(int &a,int &b)
{
	a=a+b-(b=a);
}

20.单元测试

●单元测试:单元测试是对软件基本组成单元(函数、过程、类的方法)进行正确性检验的测试工作
●集成测试:集成测试是在单元测试的基础上,将所有模块按照设计要求组装成为子系统或者系统,验证组装后功能以及模块间 接口是否正确的测试工作。
●系统测试:系统测试是将经过集成测试的软件,作为计算机系统的一个部分,与系统其他部分结合起来,在实际运行环境下对计算机系统进行一系列严格有效的测试,以发现软件潜在问题,保证系统正常运行。

21.C 程序编译过程
在这里插入图片描述
使用 gcc 汇编器编译 C 语言程序时通常会经过四个处理阶段,即预处理阶段、编译阶段、汇编阶段和链接阶段.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值