C语言重点知识

static面试题

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

1、static全局变量与普通的全局变量有什么区别 ?

  • 全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。
    全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。
    这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。 static全局变量只初使化一次,防止在其他文件单元中被引用。

2、static局部变量和普通局部变量有什么区别 ?

  • 把局部变量加static后是改变了它的存储方式即改变了它的生存期。 由栈存储改变成数据区存储,生命周期延长。

3、static函数与普通函数有什么区别?

  • static函数与普通函数最主要区别是static函数在内存中只有一份,普通静态函数在每个被调用中维持一份拷贝程序的局部变量存在于(栈)中。

4、全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?

  • 可以,在不同的C文件中以static形式来声明同名全局变量。
    可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值, 此时连接不会出错。

C++中static
1)、文件内的,匿名命名空间内,定义的变量,相当于文件静态变量。作用域为文件,生命周期是程序生命期。

//标头.h
#include<iostream>

namespace
{
	static void Print()
	{
		std::cout << "what ";
	}
}
//main.cpp
#include<iostream>
#include"标头.h"

int main()
{
	Print();
	system("pause");
	return 0;
}

2)、修饰成员变量,修饰成员变量使所有的对象只保存一个该变量,而且不需要生成对象就可以访问该成员。static修饰的变量先于对象存在,所以static修饰的变量要在类外初始化,因为static是所有对象共享的变量,必须要比对象先存在。

class A
{
public:
	static int a;
};
int A::a = 10;

3)、因为static修饰的成员数函数不属于类,所以static修饰的成员函数可以不生成对象调用,没有this指针,static 函数内不能访问非静态成员。

#include<iostream>

using namespace std;
class A
{
public:
	static void Print()
	{
		cout << "Print" << endl;
		//cout << a << endl;
	}
private:
	int a;
};
int main()
{
	A::Print();
	system("pause");
	return 0;
}

const

1、const修饰引用变量
const可以修饰const变量、非const变量,而非const变量不能修饰const变量,只能修饰非const变量。

int a=10;							//非const变量
const int b=20;						//const变量

const int& c=a;						//const修饰非const变量
const int& d=b;						//const修饰const变量

int& e=a;							//非const修饰非const

错误引用

int& f=b;							//非const修饰const

const变量说明变量只读,而非const变量来引用就改变了它的只读属性,违背了它只读原则,所以不允许

2、const修饰指针变量

int a=10;//非const data

const int * a4 = &a1;       //const data,non-const pointer
int * const a5 = &a1;       //non-const data,const pointer

int const * const a6 = &a1;   //const data,const pointer
const int * const a7 = &a1;   //const data,const pointer

const修饰指针变量时:
  1)常变量const在*前,数据是常量,不能通过解引用修改该数据;指针本身是变量,可以指向其他的内存单元。
  2)常指针:const在*后,表示指针本身是常量, 不能指向其他内存地址;指针所指的数据可以通过解引用修改。
  3)常指针变量两个const,*前后都有const,表示指针和指针所指数据都不能修改

3、const修饰函数返回值
const修饰函数返回值相对于值传递没有意义,而对于指针传递来说

const int * mallocA(){  ///const data,non-const pointer
    int *a=new int(2);
    return a;
}

int main()
{
    const int *a = mallocA();
    ///int *b = mallocA();  ///编译错误
    return 0;
}

返回值const在*之前,数据不能修改,不能使用不是常变量类型指针接受。
4、const修饰成员函数
(1)const修饰的成员函数不能修改任何的成员变量(mutable修饰的变量除外)
(2)const修饰的成员函数不能调用非const成员函数,因为非const成员函数可能会修改const修饰的成员函数的成员变量

5、const在C语言和C++中的区别

在C语言中,当const 修饰一个标识符的时候我们说,这个标识符依然是一个变量,但是它具有常属性,不能被直接改变,也就是说a只能初始化一次。

错误

const int a = 10;
a=20;

But

#include<stdio.h>

int main()
{
	const int a = 10;
	int*p = &a;
	*p = 20;
	printf("%d\n", a);

	system("pause");
	return 0;
}

结果

20
请按任意键继续. . .

在C++中const修饰的标识符就是常量。
在这里插入图片描述

函数传参

1、传值----随函数栈帧
当调用该函数时候,该函数自己形成栈帧,形参数值是拷贝过来的新变量,在函数中对形参变量操作,自然不会改变实参的值。在该函数执行结束,该函数的栈帧销毁,拷贝数据(形参)自然也就丢失了。
2、传地址----可获得实参地址,但是不安全。
函数形成栈帧,形参就是实参地址,可以改变实参的数值。
3、传引用----安全地获取实参地址
引用定义出来的形参变量相当于实参变量的别名,也就是说形参等同于实参,它们虽是两个不同的变量名,指向的是同一块内存地址,对形参操作实参也会改变。

#include<iostream>

void func_val(int a)
{
	a = 1;
}
void func_addr(int*a)
{
	*a = 2;
}
void func_quote(int&a)
{
	a=3;
}
int main()
{
	int a = 0;
	std::cout << "初值:" << a << std::endl;
	func_val(a);
	std::cout << "传值:" << a << std::endl;
	func_addr(&a);
	std::cout << "传地址:" << a << std::endl;
	func_quote(a);
	std::cout << "传引用" << a << std::endl;
	system("pause");
}

这里写图片描述

求结构体大小

1. 结构体内存对齐
  1. 对齐数:改成员类型所占大小和默认对齐数的较小者。VS中默认的值为8,Linux中的默认值为4。其它成员要对齐到对齐数的整数倍。
  2. 结构体的总大小要是最大的成员的整数倍。
  3. 内嵌结构体的对齐数=自身最大对齐数。

VS编译器

struct S3
{
	double d;									//对齐数是8,8字节
	char c;				 						//对齐数是1,9字节
	int i;										//对齐数是4,要是8的整数倍16字节
};

//结构体嵌套问题
struct S4
{
	char c1;									//对齐数是1,1字节
	struct S3 s3;								//对齐数是8,16字节
	double d;									//对齐数是8,24字节
};
2. 位段对齐规则
  1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
  2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
struct S
{
	char a : 3;					//3<8,1字节
	char b : 4;					//3+4<8,1字节
	char c : 5;					//7+5>8,2字节
	char d : 4;					//5+4>8,3字节
};

this指针

1)this指针是指向对象自身的指针。 通常在class定义时要用到类型变量自身时,因为这时候还不知道变量名,就用this这样的指针来使用变量自身。
2)this 指针是一个隐含于每一个非静态成员函数中的特殊指针。
3)this 指针被隐含地声明为: ClassName *const this,this指针是只能指向变量自身的常指针。
4)this 指针是个右值,所以不能取得 this 的地址(不能 &this)。
5)this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。

野指针和悬空指针

野指针悬空指针
未初始化的指针,其指针内容为一个随机值。指针所指向的内容已被销毁,指针未置空
任何指针变量刚被创建时不会自动成为NULL指针,所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。指针正常初始化,曾指向过一个正常的对象,但是对象销毁了,该指针未置空,就成了悬空指针。
它没有被正确的初始化于是指向一个随机的内存地址。存在野指针是一个严重的错误。悬空指针存在并没有什么问题,除非你尝试通过这个指针访问指向的地址空间。
int main() {
    int *p; // 指针未初始化,此时 p 为野指针 
    int *pi = nullptr;

    {
        int i = 6;
        pi = &i; // 此时 pi 指向一个正常的地址
        *pi = 8; // ok
    }   

    *pi = 6; // 由于 pi 指向的变量 i 已经销毁,此时 pi 即成了悬空指针

    return 0;
}

关于new失败

  1. new失败了会怎么样?抛异常bad_alloc,抛了异常就需要捕获。所以对new操作符的返回值进行判断是毫无意义的。
  2. 不让new抛异常怎么办?
 int* p = new (std::nothrow) int; // 这样如果 new 失败了,就不会抛出异常,而是返回空指针
 if ( p == 0 ) // 如此这般,这个判断就有意义了
 	return -1;
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值