泰安做网站网络公司,做网站广告经营者,电商小程序需要什么资质,wordpress实现ajax评论文章目录 4. 几个经典的笔试题4.1 题目14.2 题目24.3 题目34.4 题目4 5. C/C程序的内存开辟6. 动态通讯录7. 柔性数组7.1 柔性数组的特点7.2 柔性数组的使用7.3 柔性数组的优势 4. 几个经典的笔试题
4.1 题目1
#include stdio.h
#include stdlib.h
#include … 文章目录 4. 几个经典的笔试题4.1 题目14.2 题目24.3 题目34.4 题目4 5. C/C程序的内存开辟6. 动态通讯录7. 柔性数组7.1 柔性数组的特点7.2 柔性数组的使用7.3 柔性数组的优势 4. 几个经典的笔试题
4.1 题目1
#include stdio.h
#include stdlib.h
#include string.hvoid GetMemory(char* p)
{p (char*)malloc(100);
}void Test(void)
{char* str NULL;GetMemory(str);strcpy(str, hello world);printf(str);
}int main()
{Test();return 0;
}在调用GetMemory函数时传的是str的值p是str的一份临时拷贝p里面放的也是NULL接着把malloc开辟空间的地址给了p但是str还是NULL那么strcpy中的str就是NULL就会对空指针进行解引用操作同时动态申请的内存空间没有释放存在内存泄漏的问题而且出了GetMemory函数之后想释放也释放不了因为p所在的那块内存空间已经被销毁了已经还给操作系统了。
注
传变量本身就是传值传变量的地址才叫传址printf(“hello world”)并不是把hello world这个字符串传给了printf这个函数而是传的’h’的地址所以printf(str)这个写法没有问题
可以这样修改
#include stdio.h
#include stdlib.h
#include string.hvoid GetMemory(char** p)
{*p (char*)malloc(100);
}void Test(void)
{char* str NULL;GetMemory(str);strcpy(str, hello world);printf(str);//释放free(str);str NULL;
}int main()
{Test();return 0;
}4.2 题目2
#include stdio.h
#include stdlib.hchar* GetMemory(void)
{char p[] hello world;return p;
}void Test(void)
{char* str NULL;str GetMemory();printf(str);
}int main()
{Test();return 0;
}这里的str确实存了数组首元素的地址但是p这个数组出了GetMemory这个函数就被销毁了str变成了野指针它指向的空间里的内容变成了随机值所以打印出来就是随机值这里也相当于是非法访问了
可以这样修改
#include stdio.h
#include stdlib.hchar* GetMemory(void)
{static char p[] hello world;//char* p hello world;//hello world是常量字符串放在代码段程序结束才会销毁p接收的是h的地址所以str里放的是h的地址出了作用域p被销毁了并不影响str找到hello world//以上两种写法都可以return p;
}void Test(void)
{char* str NULL;str GetMemory();printf(str);
}int main()
{Test();return 0;
}总结 这属于返回栈空间地址的问题
我们可以简化一下这个问题
#include stdio.hint* test()
{int a 10;return a;
}int main()
{int* p test();printf(%d\n, *p);return 0;
}这里的p就变成了野指针但是有可能还能打印出10这是因为可能这块空间还没有被用掉
如果改成这样
#include stdio.hint* test()
{int a 10;return a;
}int main()
{int* p test();printf(*p);printf(%d\n, *p);return 0;
}这样就打印不出来10了这里涉及到函数栈帧 当只有第二个printf语句时我在test函数返回后迅速先通过*p来找到10然后开辟了printf的函数栈帧来打印它所以还有可能打印出10但是我再前面再加了一个printf后第一个printf函数开辟的空间覆盖了原来test函数开辟的空间所以第二个printf就打印不出10了
4.3 题目3
#include stdio.h
#include stdlib.h
#include string.hvoid GetMemory(char** p, int num)
{*p (char*)malloc(num);
}void Test(void)
{char* str NULL;GetMemory(str, 100);strcpy(str, hello);printf(str);
}int main()
{Test();return 0;
}问题在于忘记释放
应该这样修改
#include stdio.h
#include stdlib.h
#include string.hvoid GetMemory(char** p, int num)
{*p (char*)malloc(num);
}void Test(void)
{char* str NULL;GetMemory(str, 100);strcpy(str, hello);printf(str);free(str);str NULL;
}int main()
{Test();return 0;
}4.4 题目4
#include stdio.h
#include stdlib.h
#include string.hvoid Test(void)
{char* str (char*)malloc(100);strcpy(str, hello);free(str);if (str ! NULL){strcpy(str, world);printf(str);}
}int main()
{Test();return 0;
}问题在于free完后没有把str置为空指针导致str变为野指针非法访问内存了
应该这样修改
#include stdio.h
#include stdlib.h
#include string.hvoid Test(void)
{char* str (char*)malloc(100);strcpy(str, hello);free(str);str NULL;if (str ! NULL){strcpy(str, world);printf(str);}
}int main()
{Test();return 0;
}5. C/C程序的内存开辟 注 数据段也就是静态区
从图中我们也可以得知一个全局变量和一个局部变量的地址其实离得是比较远的
#include stdio.hint d 200;int main()
{int a 10;int b 20;static int c 100;printf(a %p\n, a);//a 00CFFB6Cprintf(b %p\n, b);//b 00CFFB60printf(c %p\n, c);//c 0076A038printf(d %p\n, d);//d 0076A034return 0;
}C/C程序内存分配的几个区域 栈区stack在执行函数时函数内局部变量的存储单元都可以在栈上创建函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中效率很高但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。堆区heap一般由程序员分配释放 若程序员不释放程序结束时可能由OS(operate system)回收 分配方式类似于链表。数据段静态区static存放全局变量、静态数据程序结束后由系统释放。代码段存放函数体类成员函数和全局函数的二进制代码。 有了这幅图我们就可以更好的理解在初识C语言中讲的static关键字修饰局部变量的例子了 实际上普通的局部变量是在栈区分配空间的栈区的特点是在上面创建的变量出了作用域就销毁但是被static修饰的变量存放在数据段静态区数据段的特点是在上面创建的变量直到程序结束才销毁所以生命周期变长。 6. 动态通讯录
我们对之前写的通讯里进行一个改造 通讯录的空间不是固定的大小是可以调整的默认能放3个人的信息如果不够就每次增加2个人的信息 首先我们要改变一下通讯录这个结构体
//contact.htypedef struct Contact
{PeoInfo* data;//指向了存放数据的空间int sz;//记录的是当前放的有效元素的个数int capacity;//通讯录当前的最大容量
}Contact;接着是初始化通讯录
//contact.cvoid InitContact(Contact* pc)
{assert(pc);pc-data (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));//DEFAULT_SZ是我定义的默认大小3if (NULL pc-data){perror(InitContact);return;}pc-sz 0;pc-capacity DEFAULT_SZ;
}然后是增加联系人
//contact.cint CheckCapacity(Contact* pc)
{if (pc-sz pc-capacity){PeoInfo* ptr (PeoInfo*)realloc(pc-data, (pc-capacity INC_SZ) * sizeof(PeoInfo));if (NULL ptr){perror(CheckCapacity);return 0;}else{pc-data ptr;pc-capacity INC_SZ;printf(增容成功\n);return 1;}}return 1;
}void AddContact(Contact* pc)
{assert(pc);if (0 CheckCapacity(pc)){return;}printf(请输入名字:);scanf(%s, pc-data[pc-sz].name);printf(请输入年龄:);scanf(%d, (pc-data[pc-sz].age));printf(请输入性别:);scanf(%s, pc-data[pc-sz].sex);printf(请输入电话:);scanf(%s, pc-data[pc-sz].tele);printf(请输入地址:);scanf(%s, pc-data[pc-sz].addr);pc-sz;printf(成功增加联系人\n);
}最后用完通讯录要对它进行释放
//contact.cvoid DestroyContact(Contact* pc)
{free(pc-data);pc-data NULL;pc-capacity 0;pc-sz 0;
}其他通讯录的功能不需要改动完整代码如下
//contact.h#include string.h
#include assert.h
#include stdio.h
#include stdlib.h#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30#define DEFAULT_SZ 3
#define INC_SZ 2enum OPTION
{EXIT,//0ADD,DEL,SEARCH,MODIFY,SHOW,SORT
};enum SELECT
{NAME 1,AGE
};//类型的声明typedef struct PeoInfo
{char name[MAX_NAME];int age;char sex[MAX_SEX];char tele[MAX_TELE];char addr[MAX_ADDR];
}PeoInfo;//通讯录//动态版本
typedef struct Contact
{PeoInfo* data;//指向了存放数据的空间int sz;//记录的是当前放的有效元素的个数int capacity;//通讯录当前的最大容量
}Contact;//函数声明//初始化通讯录
void InitContact(Contact* pc);//增加联系人
void AddContact(Contact* pc);//显示所有联系人的信息
void ShowContact(const Contact* pc);//删除指定联系人
void DelContact(Contact* pc);//查找指定联系人
void SearchContact(const Contact* pc);//修改指定联系人
void ModifyContact(Contact* pc);//排序功能
void SortContact(Contact* pc);void DestroyContact(Contact* pc);//contact.c#include contact.h//动态版本
void InitContact(Contact* pc)
{assert(pc);pc-data (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));if (NULL pc-data){perror(InitContact);return;}pc-sz 0;pc-capacity DEFAULT_SZ;
}//动态版本
int CheckCapacity(Contact* pc)
{if (pc-sz pc-capacity){PeoInfo* ptr (PeoInfo*)realloc(pc-data, (pc-capacity INC_SZ) * sizeof(PeoInfo));if (NULL ptr){perror(CheckCapacity);return 0;}else{pc-data ptr;pc-capacity INC_SZ;printf(增容成功\n);return 1;}}return 1;
}void AddContact(Contact* pc)
{assert(pc);if (0 CheckCapacity(pc)){return;}printf(请输入名字:);scanf(%s, pc-data[pc-sz].name);printf(请输入年龄:);scanf(%d, (pc-data[pc-sz].age));printf(请输入性别:);scanf(%s, pc-data[pc-sz].sex);printf(请输入电话:);scanf(%s, pc-data[pc-sz].tele);printf(请输入地址:);scanf(%s, pc-data[pc-sz].addr);pc-sz;printf(成功增加联系人\n);
}void ShowContact(const Contact* pc)
{assert(pc);int i 0;//打印列标题printf(%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n, 名字, 年龄, 性别, 电话, 地址);//打印数据for (i 0; i pc-sz; i){printf(%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n,pc-data[i].name,pc-data[i].age,pc-data[i].sex,pc-data[i].tele,pc-data[i].addr);}}static int FindByName(const Contact* pc, char name[])
{int i 0;for (i 0; i pc-sz; i){if (0 strcmp(pc-data[i].name, name)){return i;//找到了}}return -1;//找不到
}void DelContact(Contact* pc)
{assert(pc);if (0 pc-sz){printf(通讯录为空无法删除\n);return;}char name[MAX_NAME] { 0 };//删除printf(请输入要删除的人的名字:);scanf(%s, name);//找到要删除的人int del FindByName(pc, name);if (-1 del){printf(要删除的人不存在\n);return;}int i 0;//删除坐标为del的联系人for (i del; i pc-sz - 1; i){pc-data[i] pc-data[i 1];}pc-sz--;printf(成功删除联系人\n);
}void SearchContact(const Contact* pc)
{assert(pc);char name[MAX_NAME] { 0 };printf(请输入要查找人的名字:);scanf(%s, name);int pos FindByName(pc, name);if (-1 pos){printf(要查找的人不存在\n);}else{//打印列标题printf(%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n, 名字, 年龄, 性别, 电话, 地址);//打印数据printf(%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n,pc-data[pos].name,pc-data[pos].age,pc-data[pos].sex,pc-data[pos].tele,pc-data[pos].addr);}
}void ModifyContact(Contact* pc)
{assert(pc);char name[MAX_NAME] { 0 };printf(请输入要修改人的名字:);scanf(%s, name);int pos FindByName(pc, name);if (-1 pos){printf(要修改的人不存在\n);}else{printf(请输入名字:);scanf(%s, pc-data[pos].name);printf(请输入年龄:);scanf(%d, (pc-data[pos].age));printf(请输入性别:);scanf(%s, pc-data[pos].sex);printf(请输入电话:);scanf(%s, pc-data[pos].tele);printf(请输入地址:);scanf(%s, pc-data[pos].addr);printf(修改成功\n);}
}void select()
{printf(********************************\n);printf(***** 1. name 2. age *****\n);printf(********************************\n);
}int cmp_by_name(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)-name, ((PeoInfo*)p2)-name);
}int cmp_by_age(const void* p1, const void* p2)
{return ((PeoInfo*)p1)-age - ((PeoInfo*)p2)-age;
}void SortContact(Contact* pc)
{assert(pc);if (0 pc-sz){printf(通讯录为空无法排序\n);return;}int input 0;do{select();printf(请选择按何种方式进行排序:);scanf(%d, input);switch (input){case NAME:qsort(pc-data, pc-sz, sizeof(pc-data[0]), cmp_by_name);printf(排序成功\n);break;case AGE:qsort(pc-data, pc-sz, sizeof(pc-data[0]), cmp_by_age);printf(排序成功\n);break;default:printf(选择错误重新选择\n);break;}} while (input ! NAME input ! AGE);
}void DestroyContact(Contact* pc)
{free(pc-data);pc-data NULL;pc-capacity 0;pc-sz 0;
}//test.c#include contact.hvoid menu()
{printf(********************************\n);printf(***** 1. add 2. del *****\n);printf(***** 3. search 4. modify *****\n);printf(***** 5. show 6. sort *****\n);printf(***** 0. exit *****\n);printf(********************************\n);
}void test()
{int input 0;//首先得有通讯录Contact con;InitContact(con);do{menu();printf(请选择:);scanf(%d, input);switch (input){case ADD:AddContact(con);break;case DEL:DelContact(con);break;case SEARCH:SearchContact(con);break;case MODIFY:ModifyContact(con);break;case SHOW:ShowContact(con);break;case SORT://排序//按照名字排序//按照年龄排序SortContact(con);break;case EXIT:DestroyContact(con);printf(退出通讯录\n);break;default:printf(选择错误重新选择\n);break;}} while (input);
}int main()
{test();return 0;
}7. 柔性数组
也许你从来没有听说过柔性数组flexible array这个概念但是它确实是存在的。C99 中结构中的最后一个元素允许是未知大小的数组这就叫做『柔性数组』成员。
7.1 柔性数组的特点
结构中的柔性数组成员前面必须至少一个其他成员。sizeof 返回的这种结构大小不包括柔性数组的内存。包含柔性数组成员的结构用malloc ()函数进行内存的动态分配并且分配的内存应该大于结构的大小以适应柔性数组的预期大小。
7.2 柔性数组的使用
#include stdio.h
#include stdlib.hstruct S
{int n;//int arr[0];//这两种写法都可以int arr[];//柔性数组
};int main()
{//printf(%d\n, sizeof(struct S));//4struct S* ps (struct S*)malloc(sizeof(struct S) 40);if (NULL ps){perror(malloc);return 1;}ps-n 100;int i 0;for (i 0; i 10; i){ps-arr[i] i 1;}//空间不够需要增容struct S* ptr (struct S*)realloc(ps, sizeof(struct S) 60);if (NULL ptr){perror(realloc);return 1;}ps-n 15;for (i 0; i 15; i){printf(%d\n, ps-arr[i]);}//释放free(ps);ps NULL;return 0;
}7.3 柔性数组的优势
我们不使用柔性数组也可以实现上述功能
#include stdio.h
#include stdlib.hstruct S
{int n;int* arr;
};int main()
{struct S* ps (struct S*)malloc(sizeof(struct S));if (NULL ps){perror(malloc-ps);return 1;}ps-n 100;ps-arr (int*)malloc(40);if (NULL ps-arr){perror(malloc-arr);return 1;}int i 0;for (i 0; i 10; i){ps-arr[i] i 1;//1 2 3 4 5 6 7 8 9 10}//调整int* ptr (int*)realloc(ps-arr, 60);if (ptr ! NULL){ps-arr ptr;}else{perror(realloc);return 1;}//打印for (i 0; i 15; i){printf(%d\n, ps-arr[i]);}//释放free(ps-arr);ps-arr NULL;free(ps);ps NULL;return 0;
}那么柔性数组的优势是什么呢
使用柔性数组只用了一次malloc就解决问题了方便内存释放。 如果我们的代码是在一个给别人用的函数中你在里面做了二次内存分配并把整个结构体返回给用户。用户调用free可以释放结构体但是用户并不知道这个结构体内的成员也需要free所以你不能指望用户来发现这个事。所以如果我们把结构体的内存以及其成员要的内存一次性分配好了并返回给用户一个结构体指针用户做一次free就可以把所有的内存也给释放掉。 如果你在内存空间中多次开辟空间内存碎片内存和内存之间留下的缝就越多这些内存碎片就可能不能被很好地利用内存的利用率就越低同时访问速度也会变低。 连续的内存有益于提高访问速度也有益于减少内存碎片。其实我个人觉得也没多高了反正你跑不了要用做偏移量的加法来寻址