C语言中比较常见的问题的汇总

时间:2024-03-29 14:30:22

C语言常见问题汇总

1、在不开辟新的内存空间的情况下,如何交换a,b两个变量中的值?

void swap1(int &a,int &b)

{

a = a + b;

b = a - b;

a = a - b;

}

void swap2(int &a,int &b)

{

a = a * b;

b = a / b;

a = a / b;

}

前两种方法是采用一种简单的加减法、乘除法算法来达到交换两个值的目的。

这种方法的缺点是做加法、减法、乘法的时候可能会导致数据溢出

void swap3(int &a,int &b) //最安全效率最高

{

a = a ^ b;

b = a ^ b;

a = a ^ b;

}

第三种方法是通过按位异或的方式交换两个值。按位异或运算符"^"的功能是将参与运算符的两个数各对应的二进制位相异或,如果对应的二进制位相同,则结果为0,否则结果为1。这样运算3次即可交换两个数。如:3(0011)和4(0100),异或一次后为7(0111),再和4异或得3(0011),7和3再异或得4(0100)。

2、全局变量和局部变量在内存中是否有区别?如果有是什么区别?

有。

(1)生存周期不同

全局变量的生命周期和整个程序相同,只有程序运行结束全局变量才会被撤销,其内存空间才会被释放;

而局部变量在程序运行期间不是一直存在,而是只在其所在函数执行期间存在函数的一次调用执行结束后,局部变量就会被撤销,其所占的内存也被释放。

(2)作用范围不同

全局变量具有全局作用域,全局变量只需在一个源文件中定义,就可以作用于所有的源文件

局部变量只有局部作用域,其作用范围只是在它所在的函数或者循环内

(3)在内存中的位置不同

全局变量存放在内存中的全局区(静态区);

而局部变量是在内存中的栈区。

3、队列和栈有什么区别?

队列是先进先出(First In First Out),从rear进队从front出队。

栈是先进后出(First In Last Out),只有一个口供进出。

4、 局部变量能否和全局变量重名?

能。

因为局部变量会屏蔽全局变量。若要使用全局变量,需要使用 域解析符“::”

5、如何引用一个已经定义过的全局变量?

(1)用引用头文件的方式。

可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时链接不会出错

(2)用extern关键字。

6、全局变量可不可以定义在可被多个.C文件包含的头文件中?

可以。但引用该变量的地方要先用extern声明。但是不推荐这种写法。

(不同的编译器处理结构不同,Dev C++中编译运行没问题)

头文件尽量只有声明,不要有定义。这么做不仅仅可以减弱文件间的编译依存关系,减少编译带来的时间性能消耗,更重要的是可以防止重复定义现象的发生,防止程序崩溃。

变量的定义只能出现一次,否则会导致重复定义。但却可以声明多次。因此全局变量不建议定义在头文件中。因为当该头文件被多个c文件包含的话,会导致重复定义。因此一般做法是在某个特定的头文件中用extern声明,而在另外一个特定的.c文件中定义。需要使用就包含前者。

//可以这样定义的例子

https://blog.csdn.net/qq_39490500/article/details/79402320

//对如此定义可能编译或链接出错的例子

https://blog.csdn.net/yuqian123455/article/details/102856728

7、**do···while **和while···do有什么区别?

do-while是一种“后判定”循环结构,它先执行循环体,后判断条件,因此无论条件是否成立,将至少执行一次循环

而while语句则是先判断条件,后执行循环体,因此可能一次循环也不执行。

8、static 全局变量、局部变量、函数与普通全局变量、普通的局部变量、普通函数有什么区别?

  1. 全局变量的说明之前再加static 就构成了静态的全局变量。

全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同

这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。(若 未初始化则缺省为 0

  1. 把局部变量改变为静态变量后,改变了它的存储方式即改变了它的生存期,但是作用域没有变化。

把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围

  1. static函数与普通函数作用域不同,仅在本文件。只在当前源文件中使用的函数应该说明为static函数,static函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件。

综上所述:

static全局变量与普通的全局变量有什么区别:

static全局变量只初始化一次,防止在其他文件单元中被引用;

static局部变量和普通局部变量有什么区别:

static局部变量只被初始化一次,下一次依据上一次结果值;

static函数与普通函数有什么区别:

static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝

//摘自

https://blog.csdn.net/subo86/article/details/4814874?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

9、程序的内存分配?

可执行程序主要分为:

数据段、代码段(text)、bss段(Block Started by Symbol)

(1) 数据段存放已初始化的全局变量和静态变量,数据段属于静态内存分配

(2) 代码段用来存放程序的代码内存空间。它的大小在程序运行前就已经确定了,并且该区域只能读不能写。在代码段中,也有可能包含了一些只读的常数变量,例如字符串常量等。

(3) BSS段(Block Started by Symbol)存放未初始化的全局变量和静态变量。BSS段的数据是可读写的,链接器从可执行文件中得到BSS段的大小,然后申请得到这块内存空间,这块内存空间紧跟在数据段的后面。由此可知BSS段并不占用可执行文件的大小。在使用BSS段之前,BSS段会自动初始化为0。所以,未初始的全局变量和静态变量在程序执行之前已经是0了。BSS段属于静态内存分配

C语言中比较常见的问题的汇总

C语言中比较常见的问题的汇总

.text段

代码段也称正文段或文本段,通常用于存放程序执行代码(即CPU执行的机器指令)。一般C语言执行语句都编译成机器代码保存在代码段。通常代码段是可共享的,因此频繁执行的程序只需要在内存中拥有一份拷贝即可。代码段通常属于只读,以防止其他程序意外地修改其指令(对该段的写操作将导致段错误)。某些架构也允许代码段为可写,即允许修改程序。

.data段

数据段通常用于存放程序中已初始化且初值不为0的全局变量、静态全局变量和静态局部变量。数据段属于静态内存分配(静态存储区),可读可写。其中有一个**.rodata段**,一般用于存放常量字符串和只读变量

.bss段

BSS(Block Started by Symbol)段中通常存放程序中以下符号:
1.未初始化的全局变量和静态局部变量
2.初始值为0的全局变量和静态局部变量(依赖于编译器实现)
bss可以理解为better save space,请思考:节省的这个空间是什么空间?物理空间,但一样会占内存空间。

C语言中,未显式初始化的静态变量被初始化为0(算术类型)或空指针(指针类型)。由于程序加载时,bss会被操作系统清零,所以未赋初值或初值为0的全局变量都在bss段中。bss段仅为这些变量预留位置,它们在目标文件中并不占据空间,这样可减少目标文件体积。但程序运行时需为变量分配内存空间,故目标文件必须记录所有未初始化的变量大小总和)。当加载器(loader)加载程序时,将为bss段分配的内存初始化为0。

注意:尽管未初始化和初始化为0的全局变量和静态局部变量均在bss段,但初始值为0的全局变量是强符号,而未初始化的全局变量是弱符号。若其他文件已定义同名的强符号(初值可能非0),则弱符号与之链接时不会引起重定义错误,但运行时的初值可能并非期望值(弱符号会被强符号覆盖)。因此,定义全局变量时,若只有本文件使用,则尽量使用static关键字修饰;否则需要为全局变量定义赋初值(哪怕0值),保证该变量为强符号,以便链接时发现变量名冲突,而不是被未知值覆盖。
某些编译器将未初始化的全局变量保存在common段,链接时,等符号确定后,再放入.bss段。在编译阶段可通过-fno-common选项来禁止将未初始化的全局变量放入common段。

bss段和data段有什么区别?

1.bss段不占用物理文件尺寸,但占用内存空间数据段占用物理文件,也占用内存空间
对于大型数组如int arr1[10000] = {1, 2, 3, …} 和 int arr2[10000],arr1要记录每个数据1,2,3…,位于.data段。arr2位于.bss段,只需要记录共有10000*4个字节需要初始化为0。此时bss为目标文件所节省的磁盘空间相当可观。
2.当程序读取数据段的数据时,系统会触发缺页故障,从而分配相应的物理内存;

当程序读取.bss段的数据时,内核会将其转到一个全零页面,不会发生缺页故障,也不会为其分配相应的物理内存。

堆用于存放进程运行时动态分配的内存,可动态扩张或缩减。堆中内容是匿名的,不能按名字直接访问,只能通过指针解引用间接访问。当进程调用malloc©/new(C++)等函数分配内存时,新分配的内存动态添加到堆上;当调用free©/delete(C++)等函数释放内存时,被释放的内存从堆中剔除。

分配的堆内存是经过字节对齐的空间以适合原子操作。堆管理器通过链表管理每个申请的内存,由于堆申请和释放是无序的,最终会产生内存碎片。堆内存一般由应用程序分配释放,回收的内存可供重新使用。若程序员不释放,程序结束时操作系统可能会自动回收。
堆的末端由break指针标识,当堆管理器需要更多内存时,可通过系统调用brk()和sbrk()来移动break指针以扩张堆,一般由系统自动调用。
使用堆时经常出现两种问题:
1.释放或改写仍在使用的内存(“内存破坏”);
2.未释放不再使用的内存(“内存泄漏”)。
释放次数少于申请次数时,可能已造成内存泄漏

泄漏的内存往往比忘记释放的数据结构更大,因为实际分配的内存通常会为下个大于申请数量的2的幂次(如申128B,实际申请256B)。

注:堆向高地址方向增长

栈又称堆栈,由编译器自动分配释放,行为类似数据结构中的栈(先进后出)。
堆栈主要有三个用途:
1.为函数内部声明的非静态局部变量(C语言中称“自动变量”)提供存储空间。
2.记录函数调用过程相关的维护性信息,称为栈帧(Stack Frame))。它包括函数返回地址,不适合装入寄存器的函数参数及一些寄存器值的保存。除递归调用外,堆栈并非必需。因为编译时可获知局部变量,参数和返回地址所需空间,并将其分配于bss段。
3.临时存储区,用于暂存长算术表达式部分计算结果或alloca()函数分配的栈内内存。

Linux中ulimit -s命令可查看和设置堆栈最大值,当程序使用的堆栈超过该值时, 发生栈溢出(Stack Overflow),程序收到一个段错误(Segmentation Fault)。注意,调高堆栈容量可能会增加内存开销和启动时间
注: 栈向低地址方向增长

共享库

程序运行时,编译器会自动链接libc.so/libc++.so动态链接库,程序中用到的库函数就被加载到共享库这部分内存上。共享库位于栈和堆之间

内核空间

内核空间分为3部分,ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEME。
ZONE_DMA,0-16M,直接内存访问。该区域的物理页面专门供I/O设备的DMA使用。之所以需要单独管理DMA的物理页面,是因为DMA使用物理地址访问内存,不经过MMU,并且需要连续的缓冲区,所以为了能够提供物理上连续的缓冲区,必须从物理地址空间专门划分一段区域用于DMA。这部分的数据可以直接访问,目的在于加快磁盘和内存之间交换数据,数据不需要经过总线流向CPU的PC寄存器,再流向物理内存,直接通过总线就可到达物理内存。
ZONR_NORMAL,16M-896M,内核最重要的部分,该区域的物理页面是内核能够直接使用的。
ZONE_HIGHMEM,896M-结束,共128M,高端内存。主要用于32位Linux系统中,映射高于1G的物理内存。64位不需要高端内存
————————————————
版权声明:本文为CSDN博主「wendy_keeping」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wendy_keeping/article/details/73359352

10、描述内存分配方式以及它们的区别?

(1) 从静态存储区分配内存。如全局变量和静态变量,内存在程序编译的时候就分配好,且这些内存在程序运行期间都存在。

(2) 从栈区分配内存。在执行函数时,函数内部局部变量的存储单元都在栈上创建,函数结束时这些内存会被自动释放。栈内存分配运算内置于处理器的指令集。

(3) 从堆区分配内存,也叫动态分配内存。程序运行时用malloc函数申请任意大小的内存,在不用时由程序员free释放内存。

11、简述数组和指针的区别

1.概念

数组:存储连续多个相同类型的数据;

指针:变量,存的是地址

2.赋值

同类型的指针变量可以相互赋值;

数组不行,只能一个一个元素的赋值或拷贝

3.存储方式

数组:连续内存空间。

指针:灵活,可以指向任意类型的数据。指向的是地址空间的内存。

4.所占内存的大小(用sizeof)

用运算符sizeof可以计算出数组的容量(字节数)。

但sizeof(p),p为指针得到的是一个指针变量的字节数,而不是p所指的内存容量。

在32位平台下,无论指针的类型是什么,sizeof(指针名)都是4,在64位平台下,无论指针的类型是什么,sizeof(指针名)都是8。c++/c语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。

5.传参

作为参数时,数组名退化为常量指针

当数组作为函数进行传递时,该数组自动退化为同类型的指针

12、关键字static的作用是什么?

static主要用于定义全局静态变量、定义局部静态变量、定义静态函数。

(1)修饰局部变量

static修饰局部变量时,使得被修饰的变量成为静态变量,存储在静态区。

存储在静态区的数据生命周期与程序相同,在main函数之前初始化,在程序退出时销毁。

(2)修饰全局变量

全局变量本来就存储在静态区,因此static并不能改变其存储位置。

但是,static限制了其链接属性。被static修饰的全局变量只能被该包含该定义的文件访问(即改变了作用域)。

即,

全局的静态变量的作用范围和生命周期都是从文件的定义开始到整个文件结束;

而局部的静态变量生命周期是从文件的定义开始到整个文件结束,作用范围是从该语句块的定义开始到该语句块结束。

(3)修饰函数

static修饰函数使得函数只能在包含该函数定义的文件中被调用。

对于静态函数,声明和定义需要放在同一个文件夹中。

​ https://blog.csdn.net/zzyzgg/article/details/89842744 //可参考

13、引用与指针的区别是什么?

本质区别:指针是地址,是一个实体,需要分配内存空间

引用是变量的别名,不需要分配内存空间

(1) 指针在定义的时候不一定要初始化,并且指向的空间可变

​ 引用在定义的时候必须进行初始化,并且不能够改变

(2) 指针和引用的自增运算结果不一样

​ 指针是指向下一个内存空间,而引用是引用的变量值加1。

(3) 引用求sizeof得到的是所指向的变量(对象)的大小

​ 而指针求sizeof得到的是指针本身的大小

(4) 引用访问一个变量是直接访问

​ 而指针访问一个变量是间接访问

引用的用途主要是:用来作 函数的参数 或者 函数的返回值

//可参考

https://blog.csdn.net/u012611644/article/details/89055038

https://blog.csdn.net/zhengqijun_/article/details/54980769?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

14、malloc 和 calloc的区别

C语言的标准内存分配函数:malloc,calloc,realloc,free等。

它们都是动态分配内存,先看看它们的原型:

void *malloc( size_t size ); //分配的大小

void *calloc( size_t numElements, size_t sizeOfElement ); // 分配元素的个数和每个元素的大小

共同点就是:

​ 它们返回的是 void * 类型,也就是说如果我们要为int或者其他类型的数据分配空间必须显式强制转换;

不同点是:

​ 用malloc分配存储空间时,必须由我们计算需要的字节数。如果想要分配5个int型的空间,

​ 那就是说需要 5*sizeof(int)的内存空间:

int * ip_a;

ip_a = (int*)malloc( sizeof (int) * 5 );

​ 而用calloc就不需要这么计算了,直接:

ip_a = ( int* )calloc( 5, sizeof(int) );

这样,就分配了相应的空间,而他们之间最大的区别就是:

malloc只分配空间不初始化,也就是依然保留着这段内存里的数据,
而calloc则进行了初始化,calloc分配的空间全部初始化为0,这样就避免了可能的一些数据错误。

malloc与calloc的区别为1块与n块的区别:

malloc调用形式为 (类型*)malloc(size):*

在内存的动态存储区中分配一块长度为“size”字节的连续区域*,返回该区域的首地址。

**

calloc调用形式为 (类型*)calloc(n,size):

在内存的动态存储区中分配n块长度为“size”字节的连续区域,返回首地址。

realloc调用形式为(类型*)realloc(*ptr,size):将ptr内存大小增大到size。

free的调用形式为free(void*ptr):释放ptr所指向的一块内存空间。

C++中为new/delete函数。