1、 Sizeof 和strlen的区别:
1本质不同
sizeof是运算符。
Strlen是函数。
2处理的阶段不同
Sizeof在编译时就已经完成了计算。
Stelen是在运行时进行计算。
3、计算的内容不同
sizeof可以计算一个字符串的长度也可以计算一个数据变量所占内存的大小。
Strlen只能计算字符串的长度。
4、对\0的处理方式不同
Sizeof会把\0也计算在内。
Strlen遇到\0停止,但是不会把\0计算在内。
5、对于指针的处理不同
Sizeof是求这个指针的的大小
Strlen是求这个指针所指向的内容的大小
6、对括号的要求不同
Sizeof求数据类型所占内存大小时可以不加括号;求变量的长度时必须加括号
Strlen必须加括号
2、 系统调用和库函数的区别
库函数是语言本身的一部分,而系统函数是内核提供给应用程序的接口,属于系统的一部分。
函数库调用是语言或应用程序的一部分,而系统调用是操作系统的一部分。
用户应用程序访问并使用内核所提供的各种服务的途径即是系统调用。在内核和用户应用程序相交界的地方,内核提供了一组系统调用接口,通过这组接口,应用程序可以访问系统硬件和各种操作系统资源。
1.系统调用是为了方便应用使用操作系统的接口,而库函数是为了方便人们编写应用程序而引出的,比如你自己编写一个函数其实也可以说就是一个库函数。
2.系统调用可以理解为内核提供给我们在用户态用的接口函数,可以认为是某种内核的库函数。
3.read就是系统调用,而fread就是C标准库函数.
3、 c语言分配内存的方式有哪些?c语言中常见的内存错误有哪些
分配方式有三种:
1、从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
2、在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
3、从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由程序员决定,使用非常灵活,但如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,频繁地分配和释放不同大小的堆空间将会产生堆内碎块。
常见的错误
关于内存的一些知识已在内存分配中提及,现记录与分享常见的内存错误与对策。
类型 1:内存未分配成功,却使用了它。
方 法:在使用之前检查指针是否为NULL。
1)当指针p是函数的参数时,在函数入口处用语句assert(p!=NULL)进行断言检查。
2)当使用malloc或new来申请内存时,应该用if(p !=NULL)进行防错检查。
类型 2:引用了尚未初始化的指针
原 因:内存的缺省初始值究竟是什么并没有统一的标准,在使用之前都进行初始化。
1)没有初始化的观念。
2)内存的缺省值是未定义,即垃圾值。
类型 3:越界操作内存
原 因:内存分配成功且初始了,但越界操作是不允许的。
类型 4:忘记释放内存,造成内存泄漏。
原 因:含有这种类型错误的函数,每被调用一次,就丢失一块内存。当内存充足时看不到这种错误带来的影响,当内存耗尽时系统提示:“内存耗尽”。因此,动态内存的申请与释放必须配对,程序中malloc与free的使用次数要相同。
类型 5:释放了内存却继续使用它
原 因:对应的情况有2种
1)返回了“栈内存的指针或引用”,因为堆栈中的变量在函数结束后自动销毁。
2)某块内存被free后,没有将指向该内存的指针设置为NULL,导致产生“野指针”。
4、 什么是野指针?如何避免野指针?
野指针:指向不确定地址的指针变量。(即没有初始化) (随机指向一块内存的指针)
使用野指针易因内存泄露出现段错误。因为它随机指向的地址可能被分配了内存,不一定每次都产生段错误.
而造成内存泄露的原因有两个:
1.访问了没有权限的内存(平时我们正确使用指针的时候,系统应经将相应的内存分配给用户,但是如果指向没有分配的内存,系统会判定我们没有权限)
2.访问了已经释放了的内存。
如何解决野指针,养成一下编码习惯:
1,当一个指针没有指向时,置为NULL(空),
NULL宏:#define NULL(void * )0,0地址对应的空间不允许进行任何操作。
2,当往一个指针赋值的时候,一定要给指针分配空间,
int *p =malloc(100);返回首地址,int *p = malloc(sizeof(int)):提高代码的移植性。
3,当给一个指针分配空间,一定压迫检查是否分配成功。
if(p ==NULL)
{
printf("mallocerror!\n");
exit(1);
}
4,分配空间成功之后,要先初始化,
memset(p,0,sizeof(int));
5,释放 free(p);
6,再次置空:p = NULL;
因为野指针主要是因为我们平时编程习惯造成的,因此我们只能避免野指针的出现,而不能杜绝。(请注意用词)我们在编程时,做到以下几点可以有效地避免野指针的出现。
第一,当一个指针没有指向时,我们一般默认指向NULL。(NULL代表内存的0地址,并且NULL是不允许做任何操作的)
第二,使用malloc分配内存。(在堆空间里分配内存)
#difine MAX_SIZE 1024;
char *ptr= (char *) maollc (sizeof (char) * MAX_SIZE);
使用malloc也是有讲究的,我们应该依照下面的流程:
1.分配内存。(分配成功,返回内存的首地址;分配不成功,返回NULL)。
2.检查是否分配成功(若失败,则 exit(1) 退出程序)。
3.清空内存中的数据(malloc分配的空间里可能存在垃圾值,因此我们需要清空,可以用到memset或bzero 函数)。
4.使用内存。
5.释放内存(free,这时ptr又变成野指针)。
6.ptr指向改成NULL。
5、分别写出BOOL,int,float,指针类型的变量a 与“零”的比较语句。
答案:
BOOL : if ( !a ) or if(a)
int : if ( a == 0)
float : const EXPRESSION EXP= 0.000001
if ( a < EXP && a >-EXP)
pointer : if ( a != NULL) or if(a == NULL)
不可将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”此类形式。
EPSINON应该是一个很小的值吧 因为计算机在处理浮点数的时候是有误差的,所以判断两个浮点数是不是相同,是要判断是不是落在同一个区间的,这个区间就是 [-EPSINON,EPSINON] EPSINON一般很小,10的-6次方以下吧,具体的好像不确定的,和机器有关。
剖析:
考查对0值判断的“内功”,BOOL型变量的0判断完全可以写成if(var==0),而int型变量也可以写成if(!var),指针变量的判断也可以写成if(!var),上述写法虽然程序都能正确运行,但是未能清晰地表达程序的意思。
一般的,如果想让if判断一个变量的“真”、“假”,应直接使用if(var)、if(!var),表明其为“逻辑”判断;如果用if判断一个数值型变量(short、int、long等),应该用if(var==0),表明是与0进行“数值”上的比较;而判断指针则适宜用if(var==NULL),这是一种很好的编程习惯。
浮点型变量并不精确,所以不可将float变量用“==”或“!=”与数字比较,应该设法成“>=”或“<=”形式。如果写成if(x == 0.0),则判为错,得0分。
6、逻辑结构:
所谓逻辑结构就是数据与数据之间的关联关系,准确的说是数据元素之间的关联关系。
所有的数据都是由数据元素构成,数据元素是数据的基本构成单位。而数据元素由多个数据项构成。
逻辑结构有四种基本类型:集合结构、线性结构、树状结构和网络结构。也可以统一的分为线性结构和非线性结构。
7、物理结构:
数据的物理结构就是数据存储在磁盘中的方式。官方语言为:数据结构在计算机中的表示(又称映像)称为数据的物理结构,或称存储结构。它所研究的是数据结构在计算机中的实现方法,包括数据结构中元素的表示及元素间关系的表示。
而物理结构一般有四种:顺序存储,链式存储,散列,索引。
8、逻辑结构的物理表示:
线性表的顺序存储则可以分为静态和非静态:静态存储空间不可扩展,初始时就定义了存储空间的大小,故而容易造成内存问题。
线性表的链式存储:通过传递地址的方式存储数据。
单链表:节点存储下一个节点的地址-------------->单循环链表:尾节点存储头结点的地址
双链表:节点存储前一个和后一个节点的地址,存储两个地址。---------------->双循环链表:尾节点存储头结点的地址。
9、数据结构
数据结构:数据结构是为了实现大量数据的有效管理的机制,数据结构有很多种,最具代表性的有一下几种:
1.数组:数组有一维数组、二维数组、三维数组,在我们学习的过程中,很多地方都用到了数组,它可以帮我们很方便的整理同类型的数据。
2.链表:跟数组有些一样,都可以管理线性排列的数据,不过不同的是链表中的数据可以远距离存放,而且数据是在链表的箭头连接的结点中顺序管理的,就像用线把一个个珠子穿起来。单向链表跟数据存放的位置没有关系,数据可以自由移动,不像数组,数组中的各个元素必须紧密地放在一起不能远离。链表的各数据还能用链自由的添加和删除
3.栈:栈就像桌上摆放得书籍一样,先进后出(FILO),后进先出(LIFO),如果需要取得栈下面的数据,必须得先把它上面的数据取出来,最后输入的数据最先取出,数据输入叫做入栈(PUSH),数据输出叫做出栈(POP)
4.队列:与栈相反,先进先出(FIFO),就相当于过山洞一样,火车头先进入,出来时也是火车头先出来
5.树:像树枝一样可以分出多个树枝,而分出来的树枝还可以在分
10、链表
链表,一共有3种:单向链表、双向链表、循环链表。
单向链表是链表中最简单的一种形式,它只有一个后继指针(一般代码中是next)来维护结构。所以,单向链表只能沿着后继指针做向后操作,不能向前操作。单向链表也不能从中间插入或者删除一个元素,因为没有前向指针,所以无法获取前一个元素,从中间删除链表元素会引起断链。
双向链表:双向链表比单向链表复杂一些,它除了有一个后续指针next指向下一个元素之外还有一个前驱指针prev指向前一个元素。所以,双向链表不仅能向后遍历,还能向前遍历。同时,双向链表也能对链表中的任一元素进行操作,并且在任何位置都可以进行增加和删除操作,因为它不会断链。
循环链表:循环链表是双向链表的升级版。相比双向链表,循环列表首尾相连。