c++基础知识汇总(一)ASICII码|存储|malloc与new|虚函数|类|静态变量|强制类型转换

背景 : 准备秋招笔试。发现算法岗笔试依然会有不少c和数据结构的题,这些题必须好好准备。不然笔试过不了。

目录

一、ASIC II 码

二、存储

2.1 浮点数的存储

2.2 结构体struct存储

2.3 sizeof

三、malloc| free| delete

3.1 内存相关

3.2 内存相关

四、虚函数|类|父类

4.1 内联

4.2 虚函数

4.3 虚函|内联

4.4 静态函数

4.5 几种存储方式

五、循环

六、static与const

6.1 什么是static?

相关问题与理解

static在类内初始化还是类外初始化?

6.2 const

6.3 指针与const的关系

七、强制类型转换


一、ASIC II 码

在下列字符中,其 ASCII 码值最大的一个是()。

9,Q,d,f

对此我们需要知道ASIC 码从小到大的排列顺序为,数字,大写,小写;符号穿插中间。一共128个。

例如,下面操作就能输出相应的值:

	int a = 'P';//P ASIC is 80
	cout << a << endl;
	char c = 0x50;// 0x50==80
	cout << c << endl;
/* 输出
80
P                     */

二、存储

2.1 浮点数的存储

由什么组成?

即阶码和尾数组成。

浮点数由阶码和尾数两部分组成,对任意一个带符号的二进制数N可以如下表示

  • N=[R∧±j]×(±x)
  • x为二进制小数,通称尾数
  • R为阶码的底,即阶的基数,通常为2
  • j为数的阶码

2.2 结构体struct存储

typedef struct _tag_PARAM {
	int ia;
	char  cb;
	char  cc;
	int id;
	char  ce;
} Param;
int sz = sizeof(Param);

问sz值为多少?为16

4+1+1+2+4+1+3=16,其中2,3为了补齐用。

https://www.cnblogs.com/lazycoding/archive/2011/03/22/sizeof-struct.html

  • Char偏移量必须为sizeof(char)即1的倍数
  • int偏移量必须为sizeof(int)即4的倍数
  • float偏移量必须为sizeof(float)即4的倍数
  • double偏移量必须为sizeof(double)即8的倍数
  • Short偏移量必须为sizeof(short)即2的倍数

2.3 sizeof

如下代码,调用printf("%s\n", _string(  ))输出结果是Hello

编译器实验过,确实是Hello

char buffer[6] = { 0 };
char *_string() {
	char *s = "Hello world";
	for (int i = 0; i < (sizeof(buffer)-1); i++) {
		buffer[i] = *(s + i);
	}
	return buffer;
}

解析:sizeof(buffer)为6,所以输出buffer的前5个,前5个对应于s为Hello

三、malloc| free| delete

3.1 内存相关

下面有关new/delete和malloc/free的区别,描述错误的是( )

  • malloc/free是标准库函数,new/delete是运算符(正确,C 库函数 void *malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针。new/delete 是C++的运算符,这是在堆上开辟内存,返回一个指向该内存空间的地址。new/delete会调用类的构造函数和析构函数。)
  • new初始化对象,调用对象的构造函数,malloc仅仅分配内存(正确)
  • new返回的是所分配变量(对象)的指针,malloc返回的是void指针(正确)
  • new/delete只能在C++使用,malloc/free只能在C中使用(错误,malloc与free来说,c++也能用)

这题很简单,错的是最后一个,另外几项需要我们查阅一下。

new与delete:

https://blog.csdn.net/wue1206/article/details/81144450

对于基本数据类型,假如有如下代码

int *a = new int[10];
delete a;    // 方式1
delete [ ] a;    //方式2

方式1和方式2均可正常工作

因为:基本的数据类型对象没有析构函数,且new 在分配内存时会记录分配的空间大小,则delete时能正确释放内存,无需调用析构函数释放其余指针。因此两种方式均可。

3.2 内存相关

使用char* p = new char[100]申请一段内存,然后使用delete p释放,有什么问题( )?

  • 不会有内存泄露,但不建议用(选这个)
  • 会有内存泄露
  • 编译就会报错,必须使用delete [] p
  • 编译没问题,运行会直接崩溃

编译器编译,输出4

	char* p = new char[100];
	cout << sizeof(p) << endl;
	delete p;

这题我们认为,开辟了内存,但是删除了指针,找不到相应的内存,因此内存泄露。不知道是否正确。

正确答案是,不会有内存泄露,但是不建议用,可能开辟的时候已经将指针p设置为针对new char的指针,因此delete的时候可以delete其占用的内存。

删除时候应该用 delete[] p;因为开辟的时候用了new [],删除的时候最好也用[]

 

四、虚函数|类|父类

 以下描述正确的是( )?

  • 虚函数是可以内联的,可以减少函数调用的开销,提高效率(错误,指针调用的时候,类不确定,所以虚函数不内联)
  • 类里面可以同时存在函数名和参数都一样的虚函数和静态函数(静态函数文件之外的函数可以与静态函数重名,但是文件只能的函数不能与静态函数重名,调用时候无法调用,存在二义性)
  • 父类的析构函数是非虚的,但是子类的析构函数是虚的,delete子类对象指针会调用父类的析构函数(正确,这里需要记得,父类的析构函数最终会被调用)
  • 选项都不对

解析:正确选项为C,删除子类后,会调用子类的析构函数,子类的析构函数是虚函数,因此往上寻找父类,调用了父类的析构函数。

4.1 内联

计算机科学中,内联函数(有时称作在线函数编译时期展开函数)是一种编程语言结构,用来建议编译器对一些特殊函数进行内联扩展(有时称作在线扩展)。

inline?Inline function。即可以理解为编译器直接讲函数编译到程序之中,而不是去调用。

4.2 虚函数

https://baike.baidu.com/item/%E8%99%9A%E5%87%BD%E6%95%B0/2912832?fr=aladdin

通过基类中virtual修饰的函数。在派生类中重新定义的成员函数。

用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。

虚函数具有多态性。以不同的策略实现不同的方法。

简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。

虚函数实例

#include<iostream>
using namespace std;
class A
{
public:
	void print()
	{
		cout << "This is A" << endl;
	}
};

class B : public A
{
public:
	void print()
	{
		cout << "This is B" << endl;
	}
};

int main()
{
	A a;
	B b;
	A *ptr_a = &a;
	B *ptr_b = &b;
	a.print();
	b.print();
	ptr_a->print();
	ptr_b->print();
	int end; cin >> end;
	return 0;
}
输出:
This is A
This is B
This is A
This is B

如果将main函数改为下面这样:

int main()
{
	//main2
	A a;
	B b;
	A *p1 = &a;
	A *p2 = &b;
	p1->print();
	p2->print();
	a.print();
	b.print();
	int end; cin >> end;
	return 0;
}
输出就为 
This is A
This is A
This is A
This is B

为什么会这样?直接用类就会调用当前类内的函数,用指针就调用父类的函数。

使用类的引用或指针调用虚函数时,无论虚函数什么时间被调用,它都不能是内联的(因为虚函数的调用在运行时确定);

但是,使用类的对象调用虚函数时,无论虚函数什么时间被调用,它都可以是内联的(因为在编译时编译器知道对象的确定类型)

为了避免这个问题,所以就实现了虚函数,在每次继承中都需要重新初始化。

class A
{
    public:
        virtual void print(){cout<<"This is A"<<endl;}
};

class B : public A
{
    public:
    void print(){cout<<"This is B"<<endl;}
};

这样无论如何都能输出:
 

This is A
This is B
This is A
This is B

解析:基类中函数定义为虚函数,则派生类中函数自动定义为虚函数,每次派生类中都需要重新定义。

4.3 虚函|内联

https://blog.csdn.net/shuangshuang37278752/article/details/38875351

虚函数用于运行时多态或晚绑定或动态绑定,内联函数用于提高程序的效率,内联函数的思想:无论内联函数什么时间被调用,在编译时,内联函数的代码段都可以在被替换或插入,当一些小函数在程序中被频繁使用和调用时,内联函数变得十分有用。

类内部定义的所有函数(虚函数除外)都被隐式或自动认为是内联的

使用类的引用或指针调用虚函数时,无论虚函数什么时间被调用,它都不能是内联的(因为虚函数的调用在运行时确定);

但是,使用类的对象调用虚函数时,无论虚函数什么时间被调用,它都可以是内联的(因为在编译时编译器知道对象的确定类型)

所以,虚函数不同条件下是否内联不确定,但是是可以内联的。类内调用可以内联。

4.4 静态函数

https://baike.baidu.com/item/%E9%9D%99%E6%80%81%E5%87%BD%E6%95%B0/5644260

全局函数:在类外声明static函数的话,相当于一个全局函数,由于由于 static 的限制,它只能在文件所在的编译单位内使用,不能在其它编译单位内使用。

静态函数:需要出现在类中,函数调用的结果不会访问或者修改任何对象(非static)数据成员,这样的成员声明为静态成员函数比较好。它为该类的公用变量,在第一次使用时被初始化,对于该类的所有对象来说,static成员变量只有一份。

静态函数特点:

<1> 其他文件中可以定义相同名字的函数,不会发生冲突

<2> 静态函数不能被其他文件所用。

内部函数又称静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件。 使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。

4.5 几种存储方式

存储说明符auto,register,extern,static,对应两种存储期:

自动存储期和静态存储期。 auto和register对应自动存储期。具有自动存储期的变量在进入声明该变量的程序块时被建立,它在该程序块活动时存在,退出该程序块时撤销。

关键字extern和static用来说明具有静态存储期的变量和函数。用static声明的局部变量具有静态存储持续期(static storage duration),或静态范围(static extent)。虽然他的值在函数调用之间保持有效,但是其名字的可视性仍限制在其局部域内。静态局部对象在程序执行到该对象的声明处时被首次初始化。

五、循环

以下描述正确的是( )?

  • while循环语句的循环体至少执行1次(错误)
  • do-while循环可以写成while循环的格式(错误,do-while至少执行一次,与while不同)
  • continue语句可以出现在各种循环体中
  • break语句不可以出现在循环体内(错误,break可以出现)

这题选,continue语句可以出现在各种循环体中,是正确的。

do while至少执行一次。do-while写成while不一样,一个是先执行后判断,一个是先判断后执行。break语句就是出现在循环内提早结束所有循环的,continue是结束当前循环的。

5.1 看程序说结果

int main(){
	int idx = 0;
	while (idx++ < 10){
		cout << idx<<" ";
	}
	cout << endl;
	idx = 0;
	while (idx++ < 10){
		if (idx == 5)continue;
		cout << idx << " ";
	}
	int end; cin >> end;
	return 0;
}

输出:

1 2 3 4 5 6 7 8 9 10
1 2 3 4 6 7 8 9 10

为什么能等于10,因为idx++<10是先取idx判断,再++

如果改为++idx就是1到9了

六、staticconst

6.1 什么是static?

静态变量(Static Variable)在计算机编程领域指在程序执行前系统就为之静态分配(也即在运行时中不再改变分配情况)存储空间的一类变量。

存储类名

生命周期

作用域

extern

静态(程序结束后释放)

外部(整个程序)

static

静态(程序结束后释放)

内部(仅翻译单元,一般指单个源文件)

auto,register

函数调用(调用结束后释放)

定义全局变量前,加上关键字static,该变量就被定义成了一个静态全局变量.

特点:

  • 该变量在全局数据区分配内存.
  • 初始化:如果不是显示初始化,那么将被隐式初始化为0.
  • 访变量只在本文件可见,即应该为定义之处开始到本文件结束.

程序在内存中一般分为四个区域:

  • 代码区
  • 全局数据区
  • 堆区
  • 栈区

一般程序由new产生的动态数据放在堆区,函数内部的自动变量放在栈区.自动变量一般会随着函数的退出而释放空间,静态数据(即使是函数内部的静态局部变量)都存放在全局数据区.因此它们并不会随着函数的退出而释放空间.

 static int n;//定义静态全局变量,当前文件内开辟了一个空间,一直生存到当前编译单位结束。

改为:  int n;//定义全局变量

相关问题与理解

在C语言中,关于静态变量的说法,正确的是( )。

  • 静态变量和常量的作用相同(错误,静态变量表示内存区中固定位置的变量)
  • 函数中的静态变量,在函数退出后不被释放(正确,静态变量生存周期是当前编译单位,例如.cpp文件)
  • 静态变量只可以赋值一次,赋值后则不能改变(错误,静态变量只是内存区中的位置不能改变,const是赋值不能变)
  • 静态全局变量的作用域为一个程序的所有源文件(错误,只是当前程序文件,想要扩展所有源文件需要加extern修饰)

解析:

正确答案选第二个,静态变量在函数退出后不会被释放。因为静态变量是程序执行之前系统就静态分配的变量了。

作用域为当前文件,从定义/声明位置到文件结尾。动态全局变量可以通过extern关键字在外部文件中使用,但静态全局变量不可以在外部文件中使用。静态全局变量相当于限制了动态全局变量的作用域。

静态变量并不是说其就不能改变值,不能改变值的量叫常量。 其拥有的值是可变的 ,而且它会保持最新的值。说其静态,是因为它不会随着函数的调用和退出而发生变化。即上次调用函数的时候,如果我们给静态变量赋予某个值的话,下次函数调用时,这个值保持不变。

static在类内初始化还是类外初始化?

在C++中,类的静态成员(static member)必须在类内声明,在类外初始化,像下面这样。

class A
{
private:
	static int count; // 类内声明
};
int A::count = 0; // 类外初始化,不必再加static关键字

为什么?因为静态成员属于整个类,而不属于某个对象,如果在类内初始化,会导致每个对象都包含该静态成员,这是矛盾的。

6.2 const

const是一个C语言(ANSI C)的关键字,具有着举足轻重的地位。它限定一个变量不允许被改变,产生静态作用。使用const在一定程度上可以提高程序的安全性和可靠性。另外,在观看别人代码的时候,清晰理解const所起的作用,对理解对方的程序也有一定帮助。

加了const修饰必须在一开始就初始化值,下面代码编译时候就会报错,因为没有定义时就初始化值;

const int a;

a = 10;

下面代码编译的时候就会报错,因为const的值被更改了。

const int a=10;

cin >> a;

6.3 指针与const的关系

1)指针指向的变量的值不能变,指向可变

int x = 1;

int y = 2;

const int* px = &x;

int const* px = &x; //这两句表达式一样效果

px = &y; //正确,允许改变指向

*px = 3; //错误,不允许改变指针指向的变量的值

2)指针指向的变量的值可以改变,指向不可变

int x = 1;

int y = 2;

int* const px = &x;

px = &y; //错误,不允许改变指针指向

*px = 3; //正确,允许改变指针指向的变量的值

3)指针指向的变量的值不可变,指向不可变

int x = 1;

int y = 2;

const int* const px = &x;

int const* const px = &x;

px = &y; //错误,不允许改变指针指向

*px = 3; //错误,不允许改变指针指向的变量的值

七、强制类型转换

有如下一段程序:

  • int f1(float);
  • int f2(char);
  • void f3(float);
  • int (*pf)(float);

以下语句合法的是( )?

int (*p)(float)=&amp;f1;
pf=&amp;f2;
pf=&amp;f3;
pf=f3;

正确答案是第一个。不太懂。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

祥瑞Coding

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

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

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

打赏作者

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

抵扣说明:

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

余额充值