面试问答1

一、指针与引用

1、谈一谈你对指针的理解?(什么是指针?指针的作用?指针的运算?)

1、指针是一个变量,它保存的是内存地址。他在程序中主要用于间接访问变量和内存位置。指针的大小与系统的位数相关,32位系统中指针占四个字节,64位系统中指针占8个字节。指针的作用域与其他变量类似,局部指针变量放在栈上,全局指针变量存放在数据区

2、避免频繁强制类型转换、提高编程效率。可以动态的分配和释放内存。

3、指针运算时,必须遵循相同类型的指针变量之间赋值。指针可以进行加减法运算,指针减法运算可以获取两个地址之间的元素个数

注意事项:使用指针必须保证其已经初始化。指针运算时不要越界访问内存,动态内存分配后要手动释放。指针运算时要注意运算符的优先级

2、指针变量的大小为什么时固定的?

指针变量存储的时内存地址,内存地址是由计算机系统的架构决定的。指针变量的大小之取决与系统的位数,32位系统4个字节,64位系统8个字节。

3、为什么会有不同类型的指针?

为了确保类型的安全和内存访问的正确性,避免非法类型转换和越界访问

4、万能指针的作用及注意事项?

万能指针可以接收任何指针变量的值,函数的返回值和形参使用哇能指针可以提高通配性

使用时不能通过 * 来获取指向空间

5、什么是野指针?如何避免野指针?如何检测野指针?

1、①声明后未被初始化的指针,系统会默认初始化一个随机地址,成为野指针,②释放一个指针变量指向空间是时,没有置空,也会成为野指针。野指针的使用会产生器内存泄漏

2、①定义一个指针时,要么设为空,要么指向合法内存,②向指针变量指向的空间赋值时,一定要开辟空间,③开辟空间后要检查是否分配成功,④分配成功后要进行初始化,⑤使用时要注意不要越界访问,⑥结束时要释放内存空间,⑦释放之后指针去变量要置为空

3、gcc自带的工具ASAN,还有valgrind工具

具体使用过程:
valgrind:过于庞大,输出信息复杂。 valgrind --tool=memcheck --leak-check=full ./xxx
ASAN:强大工具:内存检测:gcc xxx.c -fsanitize=leak -o demo -g
                             内存检查:gcc xxx.c -fsanitize=address -o demo -g
                             检测越界:gcc xxx.c -fsanitize=address -o demo -g -fno-omit-frame-pointer

6、指针减法运算的意义?(指针-指针 : 指针- 数值的意义)

1、指针减指针适用于两个指针都指向同一数组,相减结果为两指针之间的元素数量

2、指针加减数值表示的意义时指针在数组中位置的移动

7、什么时const指针和指向const的指针?

1、const指针:指针去本申请是常量,不能修改指向地址

2、指向const的指针:指针指向的值是常量,不能通过指针修改指针指向的值

8、什么是函数指针?如何使用它?

函数指针是指向函数的值,用于动态调用函数

9、指针与引用的区别?

指针是一个变量,引用是变量的别名;指针可以为空,引用定义时必须初始化;指针在初始化之后可以改变指向,引用在初始化之后不可以改变指向;指针可以有多级,引用最多两级;指针需要动态分配内存空间,指针不需要动态分配内存空间,指针间接访问,效率低,引用直接访问,效率高;做函数形参时,指针要考虑传值还是传址,引用不需要考虑,它既可以访问也可以修改,函数返回值时引用可以做左值;指针和引用都占8个字节

10、左值引用与右值引用?

左值引用:通过&来获得左值,左值就是在内存有确定存储地址、有变量名,表达式结束依然存在的值,左值引用方便运算符重载

右值引用:通过&&来获取右值,右值就是没有确定存储地址,没有变量名,表达式结束就会销毁的值。右值引用可以延长临时对象的生命周期,减少对象的构造与析构,右值引用主要体现为两大作用:实现移动语义和完美转发

万能引用:万能引用既可以绑定左值又可以绑定右值。万能引用必须是模板函数、必须发生模板类型推断

二、内存管理

1、C语言内存空间分布?存储空间

1、假设进程的虚拟空间有四个G,那么内存空间中有一个G属于内核空间,3个G属于用户空间,用户空间分为栈空间、堆空间、数据区和代码段

①栈空间用于存储局部变量、函数参数和函数调用的返回地址,栈是后进先出结构

②堆空间用于动态分配内存,堆的大小可以在程序运行时通过malloc、calloc、realloc、free动态的去调整

③数据段:bss段、or段、静态数据区;bss段保存未初始化的全局变量,or段保存只读数据,静态数据区保存的是静态变量

④代码段保存可执行文件代码,在运行期间是不可以修改的

2、局部变量和全局变量的区别?作用域?

1、存储空间:局部变量在栈上。全局变量在数据段(bss段)

2、作用域:局部变量在他的函数或代码块内。全局变量作用域覆盖整个源文件,如果使用extern关键字,可以在其他文件中访问

3、未初始化的局部变量是一个垃圾值,未初始化的全局变量是0;局部变量和全局变量重名时,局部变量会屏蔽全局变量

3、extern关键字的作用

extern起到外部声明的作用,声明全局变量在其他文件中或者其他位置中已经被定义。当前函数或当前文件可以访问该全局变量

4、为什么需要内存管理?内存管理的方式有哪些?优缺点?

1、内存属于稀缺资源,要使内存高效化合理化,内存管理是为了避免运行时因内存导致错误

2、内存的管理方式分为用户管理和系统管理

3、用户管理的优点:效率高,缺点:容易内存泄漏;系统管理的本质是垃圾回收(GC)机制,

优点:避免内存泄漏,缺点:浪费资源

5、C语言分类内存的方式有哪些?

栈上静态内存分配和堆上动态内存分配

静态内存分配:局部变量、数组(大小编译时确定)、alloca函数(动态分配,函数返回时释放)

动态内存分配:可以分配大块内存,通过指针分配,容易造成内存泄漏

6、alloca、malloc/aclloc/relloca的特点

alloca 是栈上动态分配内存,函数返回时自动释放;栈空间大小有限

malloc / calloc / realloc 函数都是在堆上分配内存的

malloc 只分配,不初始化

clalloc 分配指定数量的内存块,并初始化未0;

realloc 重新调整之前分配之前内存的大小,扩展空间时,原地址会发生改变,原空间的值不变;当缩小空间时,原地址不发生改变,但容易引发越界访问

7、常见的内存错误有哪些?如何调试?

内存越界:数组索引超过范围或realloc函数重新分配的内存空间缩小

内存泄漏:一般由野指针造成的

内存碎片:内存频繁分配和释放,导致内存快分散

解决内存错误:养成良好的编码习惯;使用系统调用函数call malloc_stats()call malloc_info(0, stdout)

使用内存检测工具, 

 Valgrind :缺点工具庞大,输出信息复杂  valgrind --tool=memcheck --leak-check=full ./xxx  ;

ASan : gcc编译器自带的     -g(生成调试信息)

开启内存泄漏检测:-fsanitize=leak             gcc xxx.c -fsanitize=leak -o demo - g(具体到行)
开启内存检查:-fsanitize=address
开 启 越 界 详 细 错 误 : -fno-omit-frame-pointer
gcc xxx.c - fsanitize=address -o xxx -g -fno-omit-frame-pointer(只检测越界)

8、内存池的作用?

不用频繁分配内存空间,减少系统调用次数,避免空间浪费,减少开销

9、new和malloc的区别是什么?

①new具有类型安全性。malloc没有类型安全性,

②new在分配内存的同时还会调用对象的构造函数,对对象进行初始化。malloc仅分配内存

③new分配失败会抛出bad_alloc异常,从而被捕获和处理。malloc分配失败需要手动检查并处理

④new可以被重载,以自定义内存分配行为。malloc无法重载

10、什么是内存对齐?

①内存对齐是指在计算机系统中,将数据存储在符合特定对齐规则的内存地址上,数据类型的大小通常是其大小的整数倍

②对齐能够提高内存访问的效率,未对齐可能需要多次多次读取才能获取完整的数据

③结构体的总大小通常是其最大成员对齐大小的倍数

11、C++内存管理方式?

内存管理的方式分为用户管理和系统管理,其中用户管理无法避免内存泄漏,需要用户自己去分配释放,而系统管理,也就是GC机制,他是由系统自动分配释放,他引入第三方机制,导致开销比较大

C++没有引入GC机制,原因有三点。①C++它没有共同基类。②C++是面向性能的语言,引入GC机制会增大开销。③C++有自己的内存管理机制RAII,它可以自动构造和析构函对象的方式来管理资源,从而引入智能指针,智能指针的原理就是利用对象在出作用域的时候会自动析构,在析构时自动释放堆内存,通过引用计数达到对堆内存空间的共享操作

三、文件编程

1、文件指针的作用?

文件指针就是 FILE 类型指针,指向文件类型的结构,结构里包含该文件的各种属性,可以对它指向的文件进行各种操作。可以随机访问文件,可以把 I/O 操作抽象为文件操作

2、文件指针相关函数及其注意事项?

fopen  : FILE *fopen(const char *filename, const char *mode); 

作用:打开文件并返回文件指针,mode 打开模式  : r  :只读  ;w : 写入; a  : 追加 ;  rb :二进制读 ; wb : 二进制写

fclose  : int fclose(FILE *stream);    刷新缓冲区, 关闭成功返回 0; 失败返回EOF

 fread  和   fwrite  : 二进制文件的输入输出操作,打开文件应该使用二进制的形式打开

size_t fread(void *ptr, size_t size, size_t count, FILE *stream);                 读取成功会返回nmemb

fread 从文中读取数据到内存
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);        成功返回nmemb

fwrite 将内存数据写入文件

fseek  和  ftell 

int fseek(FILE *stream, long offset, int whence);       fseek 移动文件指针 ,返回 0 成功,其他失败
long ftell(FILE *stream);        ftell货物当前文件指针位置

ferrorfeof : 

int ferror(FILE *stream);          检查文件中的错误
int feof(FILE *stream);           作用: 检查文件是否达到末尾; 实现原理:通过内部标准记录流是否达到文件末尾;  常用于判断文件是否为空 

对于一个空文件来说,当程序打开时,他的光标会停在文件的开头,此时使用 feof 遍历文
件,如果返回值不为 0,则文件为空。
getc(fp);
if(feof(fp) != 0)
{
printf("文件为空")
}
30
else
{
//rewind(fp);
fseek(fp,0,SEEK_SET);
}

3,fgets、fputs、fgetc、fputc的函数原型及使用注意事项?

fgets : 

char *fgets(char *str, int n, FILE *stream);      缓冲行,遇到\n并将其读入目标地址中,限制输入个数,不易造成溢出

fgets 读取到换行符停止读取,并带回换行符,若读到 n-1 个字符还没有遇到换行符时,在
结尾加上一个空字符,返回 n 个字符。用作输入时,需要去换行符。

fputs

int fputs(const char *str, FILE *stream);      把字符串写入到指定的文件流stream中,但不包括终止字符 \0

fgetc

int fgetc(FILE *stream);          返回值是int 形,打印字符要 %c

fputc  : 

int fputc(int c, FILE *stream);        将字符C写入指定文件流当中, 返回写入字符,写入失败返回 EOF

fgetc()  和  fputc()  是函数; getc()  和  putc() 是宏函数

4、fprintf 、 fscanf 的函数原型及使用注意事项?

int fprintf(FILE *stream, const char *format, ...); 根据指定格式的将数据写入到文件流中,

int fscanf(FILE *stream, const char *format, ...);根据指定格式从文件流中读取数据

5、如何获取文件大小?

int fseek(FILE *stream, long int offset, int whence)
long int ftell(FILE *stream); 如果发生错误,则返回 -1L,

将指针指向最后,并用 ftell 获得文件流的读取位置

if(fseek(fp,0,SEEK_END) == -1)
{
perror("fseek error");
goto err;
}
file_size = ftell(fp);
printf("%d",file_size);

6、文本读写和随机读写的区别

文本读写 : 字符输入输出 fgetc() 和 fputc() ; 行输入输出 fgets()  和 fputs()  ;格式化输入输出  fscanf()  和 fprintf()  ; 二进制读写 fread()  和  fwrite()  ;

随机读写 : fseek()  : 将文件指针移动到指定位置 : SEEK_SET从文件开头开始引动  : SEEK_CUR  从当前指针位置开始移动   : SEEK_END  从文件末尾开始移动 

ftell : 返回当前文件指针位置 

 rewind() : 文件指针移动到文件开头

文本读写 : 用于顺序处理文本文件 ,适合处理纯文本数据

二进制读写·:用于顺序处理二进制文件, 如图像、音频等

随机读写 : 灵活的在文件任意位置进行读写操作,处理随机访问的场景

7、fflush 的作用

刷新输出缓冲区 写入文件或打印调试信息,后面加发flush();可提高代码规范

刷新标准输出缓冲区 : fflush(stdout); 把输出缓冲区的内容打印到标准输出设备上

刷新标准输入缓冲区: fflush(stdin) ;  把输入缓冲区的内容丢弃

8、特殊文件指针

程序运行会生成三个文件指针:
键盘文件(标准输入) = stdin(行缓冲)
终端(标准输出) = stdout(行缓冲)
终端(标准错误) = stderr(无缓冲)
打印错误信息,用 stderr,避免缓冲区影响。

四、网络编程

1、三次握手

第一次握手 :客户端向服务器发送连接请求SYN报文(SYN = 1,seq = x),发送完毕,客户端进入SYN_SENT(连接请求状态)状态

第二次握手 : 服务器接收到客户端的SYN报文后,确认链接,并发送一个ACK标志和SYN标志的报文(SYN = 1, ACK = 1, seq = y,  ACKnum = x + 1),服务器进入SYN_RCVD(连接请再次确认)状态

第三次握手 : 客户端收到服务器deSYN_ACK报文后,发送一个ACK报文作为响,发送完毕客户端进入ESTABLISHED(建立连接,可传输数据)状态

当服务器收到这个包时,也进入ESTABLISHED状态

2、四次挥手

第一次挥手 : 客户端发送FIN标志的报文,释放连接(FIN = 1, seq = u),客户端进入FIN_WAIT_1状态(第一次主动等待;发送关闭请求)

第二次挥手 : 服务器收到客户端FIN报文后,发送确认ACK报文(ACK = 1, ack = u + 1 , seq = u + 1),发送之后,服务器进入CLOSE_WAIT状态(被动关闭,等待确认关闭),客户端收到这个确认包之后,进入FIN_WAIT_2状态(第二次主动等待,发送关闭请求;请求关闭)

第三次挥手  : 

·


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值