C语言软开面经
- malloc calloc realloc free动态分配内存
- malloc
- calloc函数:
- realloc 函数:
- free函数:
- 堆栈-内存分区
- 栈区(Stack):
- 堆区(Heap):
- 全局(静态)区:
- 常量区:
- 代码区:
- void* 大小
- float精度
- 虚函数和纯虚函数
- socket编程
- 阻塞、非阻塞、同步、异步
- 多线程
- 编写一个strcpy函数
malloc calloc realloc free动态分配内存
头文件:
#include<stdlib.h>
malloc
头文件:
#include<stdlib.h> 或者 #include<malloc.h>
malloc函数:
void * malloc(unsigned int num_byte);
功能:分配长度为num_byte字节的内存块
入参:需要分配的内存字节数
返回值:如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。
注意事项:
1、申请了内存空间后,必须检查是否分配成功。
2、当不需要再使用申请的内存时,记得free,否则会内存泄露;
3、虽然malloc()函数的类型是(void *),任何类型的指针都可以转换成(void *),但是最好还是在前面进行强制类型转换,因为这样可以躲过一些编译器的检查。
4、malloc只管分配内存,并不能对所得的内存进行初始化,所以得到的一片新内存中,其值将是随机的。如果需要都初始化为0可以用calloc
5、一般参数传递的形式为(sizeof(要开辟的类型如int)*要开辟的个数).也可以直接写字节数
malloc申请内存有8字节对齐不一定申请多少就分配多少是 分配>=申请
calloc函数:
void* calloc (size_t num, size_t size);
第一个参数的类型是无符号整型(size_t),它表示需要动态开辟的元素的个数.
第二个参数的类型是无符号整型(size_t),它表示需要开辟的每个元素的大小(以字节为单位).
返回值:与malloc相同,如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。
realloc 函数:
函数原型:
void* realloc(void* memblock, size_t size)
memblock 需要进行扩容的指针
size 扩容后的大小,字节单位
返回与malloc相同,如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。
realloc如何让扩容:
realloc可以扩大或缩小原来已经malloc的内存空间,realloc是从堆上分配内存的.当扩大一块内存空间时,realloc()试图直接从堆上现存的数据后面的那些字节中获得附加的字节,如果能够满足,原地扩容 这样扩容后指针首地址不变;
如果数据后面的字节不够,异地扩容,那么就使用堆上第一个有足够大小的自由块,现存的数据然后就被拷贝至新的位置,而老块则放回到堆上.这句话传递的一个重要的信息就是数据可能被移动.
关于free释放:内存经过malloc,再realloc后,如果是扩大内存,只需释放realloc的内存,不用再释放malloc的了。如果是缩小内存,则释放malloc的,不释放realloc的,
使用realloc进行缩容:
缩的仅仅只是使用权限,空间还是没有减少的
free函数:
void free( void * pointer);
功能:释放内存
参数:free函数的参数要么是NULL,要么是一个先前从malloc、calloc或realloc返回的值。向free传递一个NULL参数不会产生任何效果。所以一般释放malloc分配的指针后会将指针指向NULL,因为释放两次或出现错误释放空指针等于啥都没做。
返回值:无
堆栈-内存分区
堆和栈的区别-内存分区
栈区(Stack):
1.由编译器自动分配和释放
2.利用栈存储一些临时变量,包括函数形参、函数内部局部变量、返回值等
3.栈的操作遵循“后进先出”(LIFO)的原则
堆区(Heap):
1.由程序员手动分配和释放的储存区
2.堆在内存中位于bss区和栈区之间
3.通过调用函数如malloc()、calloc()或realloc()来从堆中分配内存
4.必须手动调用free()函数来释放堆内存,否则可能导致内存泄漏
全局(静态)区:
1.内存分配在程序编译之前完成,且在程序的整个运行期间都存在
2.存储全局变量和静态变量
3.静态内存区细分还可以分成:.data段和.bss段
data段:初始化的全局变量、静态变量和只读数据都存放在这个域。
bss段:未初始化的全局变量和静态变量。
未初始化的变量,是在程序中说明,在运行的初始化阶段,才会真正占用存储空间,它的大小不会影响目标文件的大小。总结,一些变量放在bss段,可以减小目标文件的占用空间。
常量区:
1.常量存储区用于存储常量数据,如字符串常量,const修饰的变量
2.常量存储区通常位于静态存储区内
代码区:
1.通常是用来存放程序执行代码的一块内存区域
2.该区域的大小在程序运行前就已经确定,并且内存区域通常属于只读
void* 大小
不管什么类型的指针都是
sizeof(int*)=sizeof(char*)=sizeof(void*)=8
32 位编译环境下,是4 字节
64 位编译环境下,是8 字节
float精度
Float的存储结构
一个浮点数(Floating Point Number)由三个基本成分构成:
符号位(Sign)(占1位)
偏移指数位(阶码、指数)(Exponent)(占8位)
尾数位(Mantissa)(占23位):就是小数点后面的那些小数
float只能保证至少7位(包括整数部分和小数部分)有效数字准确
C语言中的float(单精度浮点数)
float的精度和取值范围
虚函数和纯虚函数
C++相关 不过多阐述
定义一个函数为虚函数,不代表函数为不被实现的函数。
定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。
定义一个函数为纯虚函数,才代表函数没有被实现。
定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。
socket编程
套接字(socket) 是 Linux 下的一种进程间通信机制(socket IPC),使用 socket IPC 可以使得在不同主机上的应用程序之间进行通信(网络通信),也可以是同一台主机上的不同应用程序。socket IPC 通常使用客户端<—>服务器这种模式完成通信,多个客户端可以同时连接到服务器中,与服务器之间完成数据交互。
Socket 编程基础
阻塞、非阻塞、同步、异步
同步:
所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。
例如普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事
异步:
异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。
例如 ajax请求(异步): 请求通过事件触发->服务器处理(这时浏览器仍然可以做其他事情)->处理完毕
阻塞:
阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回。
有人也许会把阻塞调用和同步调用等同起来,实际上它们是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。 例如,我们在socket中调用recv函数,如果缓冲区中没有数据,这个函数就会一直等待,直到有数据才返回。而此时,当前线程还会继续处理各种各样的消息。
非阻塞:
非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
多线程
多线程
编写一个strcpy函数
函数原型:char* strcpy( char* destination , const char* source) //将source复制到destination
头文件:#include<string.h>
返回值:返回的是第一个目的数字的首地址,类型为char*
注意:
1.strcpy遇到 ‘\0’ 时停止,会将 ‘\0’ 复制到目的数组中,所以源数组里面要有 ‘\0’
2.目的数组的大小>=源数组,否则可能会造成缓冲溢出的错误情况
#include<assert.h>
#include<stdio.h>
char* my_strcpy(char* dest, const char* src)//const在* 前面,使不再有 *src被改变的风险
{
assert(dest != NULL);
assert(src != NULL);
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[] = "***************";
char arr2[] = "abcdefg";
my_strcpy(NULL, arr2);
printf("%s", arr1);
return 0;
}