C/C++ 面试复习笔记(3)
一.valgrind工具
参考:
内存检查工具valgrind介绍、安装与使用-CSDN博客https://blog.csdn.net/mijichui2153/article/details/85240349
二.C语言中如何实现一个线程池?
答案: 线程池是通过预先创建固定数量的线程来处理多个任务,避免频繁的线程创建和销毁。
解析: 线程池通常使用任务队列来存放待执行的任务,通过多个线程并发地执行任务。使用互斥锁、条件变量等机制来同步访问任务队列。
#include <pthread.h> #include <queue> struct ThreadPool{pthread_t *threads;std::queue<Task> tasks;pthread_mutex_t lock;pthread_cond_t cond; }; void *worker(void* arg){while(true){pthread_mutex_lock(&lock);while(tasks.empty())pthread_cond_wait(&cond,&lock);Task task = task.front();tasks.pop();pthread_mutex_unlock(&lock);task.run();//执行任务} }
三.C语言中如何实现一个动态哈希表(扩展哈希表)?
答案:动态哈希表会根据负载因子自动调整哈希表的大小,并重新哈希所有元素。
解析:当哈希表的负载因子达到一定阈值时,哈希表会扩大并重新哈希所有元素,以减少哈希冲突和提高查询效率。
四.C语言中如何实现一个单例模式(Singleton Pattern)?
答案: 单例模式保证一个类只有一个实例,并提供全局访问该实例的方法。
解析:单例模式通过静态变量和静态函数实现实例的惰性创建,并确保多次调用时只返回 一个实例。
五.strtok函数
char *strtok(char *str, const char *delim)
参数
-
str: 要分割的字符串。在第一次调用时,传入要分割的字符串;后续调用时,传入
NULL
,表示继续分割同一个字符串。 -
delim: 分隔符字符串。
strtok()
会根据这个字符串中的任意一个字符来分割str
。
返回值
返回指向下一个标记的指针。如果没有更多的标记,则返回 NULL。
实例
#include <string.h> #include <stdio.h>int main () {char str[80] = "This is - www.runoob.com - website";const char s[2] = "-";char *token;/* 获取第一个子字符串 */token = strtok(str, s);/* 继续获取其他的子字符串 */while( token != NULL ) {printf( "%s\n", token );token = strtok(NULL, s);}return(0); }
注意事项
-
修改原字符串:
strtok()
会修改传入的字符串,将分隔符替换为\0
(空字符)。因此,原始字符串会被破坏。 -
不可重入:
strtok()
使用静态缓冲区来保存状态,因此它不是线程安全的。如果在多线程环境中使用,可以考虑使用strtok_r()
(可重入版本)。 -
连续分隔符: 如果字符串中有连续的分隔符,
strtok()
会忽略它们,并返回下一个有效的标记。
可重入版本:strtok_r()
strtok_r() 是 strtok() 的可重入版本,它允许你在多线程环境中安全地使用。它的原型如下:
char *strtok_r(char *str, const char *delim, char **saveptr); //char **saveptr是一个供内部使用的指针,用于保存上次分割剩下的字串。
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() {char pSrc[] = "this/is/jason;/i/am/from/xi'an/china";char* pToken = NULL;char* pSave = NULL;char* pDelimiter = "/";pToken = strtok_r(pSrc, pDelimiter, &pSave);printf("Begin:\n");while(pToken){printf(" pToken[%s]; pSave[%s]\n",pToken,pSave);pToken = strtok_r(NULL, pDelimiter, &pSave);}printf("End!\n");return 0; }
其输出结果
Begin:pToken[this]; pSave[is/jason;/i/am/from/xi'an/china]pToken[is]; pSave[jason;/i/am/from/xi'an/china]pToken[jason;]; pSave[i/am/from/xi'an/china]pToken[i]; pSave[am/from/xi'an/china]pToken[am]; pSave[from/xi'an/china]pToken[from]; pSave[xi'an/china]pToken[xi'an]; pSave[china]pToken[china]; pSave[] End!
六.C语言中如何实现一个带有缓存的数据库查询系统?
答案:通过使用缓存来存储常用查询的结果,以减少数据库的访问次数。可以采用LRU缓存策略,确保缓存中的数据为最近使用的数据。 解析:使用哈希表或者LRU缓存来存储查询结果,减少对数据库的频繁访问。
七.C语言中的memcpy函数的作用是什么?
答案:memcpy函数用于将源内存块中的内容复制到目标内存块中。
解析:memcpy是一种内存操作函数,用于按字节复制数据。使用时需要确保目标内存有 足够的空间,否则会导致内存溢出。
void *memcpy(void *str1, const void *str2, size_t n);
八. memmove() 和 memcpy() 的主要区别是什么?
memcpy() 不能处理内存重叠,直接拷贝可能会覆盖原数据。
memmove() 通过缓冲区确保数据不被破坏,适用于内存重叠情况。
memcpy() 不会自动添加 \0,仅拷贝指定字节数。
九.union易错点
union 的大小等于最大成员的大小向上取整到最大成员对齐值的整数倍。 (即:union_size = max(最大成员大小, 最大对齐值的整数倍)
)
十.C语言的存储类型
一般形式:
存储类型说明符 数据类型说明符 变量名,变量名...
在C语言中,对变量的存储类型说明有以下四种:
auto 自动变量 (动态存储)
register 寄存器变量(动态存储)
extern 外部变量(静态存储)
static 静态变量(静态存储)
所谓存储类型是指变量占用内存空间的方式,也称为存储方式。
1.auto 在函数体中定义的变量缺省是auto. 当进入代码块(block)时,系统为自动变量分配内存.在块内,这些变量被定义,并被认为他们是局部于本块的.当退出块时,系统释放分配给自动变量的内存,因此,变量值就丢失了.重新进入块,系统会为自动变量再次分配内存,原先的值已经没有了. 2.extern 在函数的外部声明变量,就为变量永久的分配存储,它的存储类型是extern. 定义在函数外部的变量的存储类型都是extern,既不使用关键字extern. 使用extern关键字是告诉,编译器”在本文件或其他文件中寻找它”.既a文件中int a =1在b文件中则可以使用extern int a;来调用它. 外部变量从不会消失,因为外部变量在整个程序执行期间都是存在的. 两种方式向函数传递信息:1.使用外部变量.2.使用参数.当函数从其内部而不是通过参数表改变全局变量时,会产生副作用.这样的构造容易引发错误.正确的做法是通过参数和返回机制影响全局变量.
3.register register高速编译器应该把有关的变量存储在高速的内存寄存器中.使用存储于类型register是要试图改善执行速度.当关系速度时,可选择一些最经常访问的变量,并把他们的存储类型声明为register. register i;等价于register int i;,register默认是int类型.
4.static 1.允许局部变量保存它的原有值,以便再进入块时使用. 当第一次调用函数时,把变量cnt初始化为0,在退出函数时,cnt的值被保存在内存中.当再次调用函数时,就不用再对cnt进行初始化,cnt扔保留着上次调用函数时的值. 2.在函数外部声明static,则次变量是本文件可访问(私有privacy),外部文件不能访问.
十一.题目:下面代码的输出是什么?
#include <stdio.h> int main() { int a = 10; printf("%d %d", a++, ++a); //未定义行为return 0; }
序列点相关知识:序列点标志着一个执行点,在该点之前的所有副作用都会被处理完毕,之后的副作用才会开始。在 C 语言中,函数调用时参数的求值顺序是没有规定的,而且在函数调用(printf
)前并没有序列点来明确副作用的执行顺序。
十二.在C语言中,printf和scanf函数的格式化字符串中,如果使用了错误的格式说明符,程序会产生编译错误。
答案: 错误
解析: printf 和scanf 的格式化字符串中使用错误的格式说明符不会导致编译错误,而 是会在运行时产生未定义行为或运行时错误。编译器不会检查这些格式符号是否与传入的参数类型匹配。
printf
和 scanf
属于变参函数,也就是它们可以接收数量和类型都不固定的参数。在 C 语言中,编译器在编译阶段没办法对变参函数的参数类型进行完整的类型检查。所以,像下面这样的代码虽然在逻辑上是错误的,但编译器通常不会报错