一、指针与引用
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货物当前文件指针位置
ferror 和 feof :
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状态(第二次主动等待,发送关闭请求;请求关闭)
第三次挥手 :
·