面试官:什么是宏定义和内联函数?-腾讯云开发者社区-腾讯云 (tencent.com)
宏定义和内联函数
定义与区别
内联函数:在编译阶段把整个函数体搬过来,空间换时间,减少函数切换栈换来换去的时间消耗。
对于宏定义:宏只是文本替换,不是函数表达,先有宏定义再有内联函数,内联函数延续了宏定义的用法,同时可以避免一些错误。
比如宏定义不加括号得到的结果就有问题:
1、inline函数在第一次被调用前必须进行完整的定义,否则编译器无法知道应该插入什么代码。
2、在inline函数里一般不能含有复杂的控制语句,如for、switch等
3、inline函数是一种用空间换时间的措施,函数体不宜太长,否则反而会增大系统开销,一般为1~5条语句。
4、inline和宏定义相似,但不完全相同,宏定义只做简单的字符替换而不做语法检查,往往会出现意想不到的错误。
宏定义相关:
宏定义define与typedef作用域的的区别:
typedef:
如果放在所有函数之外,它的作用域就是从它定义开始直到文件尾
如果放在某个函数内,它的作用域就是从它定义开始直到该函数结尾
#define:
不管是在某个函数内,还是在所有函数之外,作用域都是从定义开始直到整个文件结尾(不管是typedef还是define,其作用域都不会扩展到别的文件,即使是同一个程序的不同文件,也不能互相使用)
这里说下题外话#define叫宏定义,但是在笔者的认识里对声明和定义的理解是:声明不分配内存,定义才分配内存,所以#define虽然名字里面有“定义”两个字,但并不占存储空间(为什么不叫宏声明···)
define 和undef(取消宏定义)使用案例
#include <stdio.h>
#define MAX 10
int main()
{
printf("%d", MAX);
#undef MAX
#define MAX 20
printf("%d", MAX);
}
指向数组的指针,访问数组
数组名本身就是指针,表示数组的第一个元素的地址
访问数组,有两种方式,使用指针p或者是直接访问数组。
1. balance 是一个指向 &balance[0] 的指针,即数组 balance 的第一个元素的地址。
2. 一旦您把第一个元素的地址存储在 p 中,就可以使用 *p、*(p+1)、*(p+2) 等来访问数组元素。
#include <stdio.h>
int main ()
{
/* 带有 5 个元素的整型数组 */
double balance[5] = {1000.0, 2.0, 3.4, 17.0, 50.0};
double *p;
int i;
p = balance;
/* 输出数组中每个元素的值 */
printf( "使用指针的数组值\n");
for ( i = 0; i < 5; i++ )
{
printf("*(p + %d) : %f\n", i, *(p + i) );
}
printf( "使用 balance 作为地址的数组值\n");
for ( i = 0; i < 5; i++ )
{
printf("*(balance + %d) : %f\n", i, *(balance + i) );
// %f 格式得到 double,输出是6位小数,%f和%lf是一样的
// %.10lf 就是输出 10 位小数
}
return 0;
}
#printfprintf() 只会看到双精度数,printf 的 %f 格式总是得到 double,所以在 printf() 中使用 %f 跟 %lf 的输出显示效果是一样的。但是对于变量来说,double 类型比 float 类型的精度要高。double 精度更高,是指它存储的小数位数更多,但是输出默认都是 6 位小数,如果你想输出更多小数,可以自己控制,比如 %.10lf 就输出 10 位小数。
静态数组和动态数组
区别是内存的大小可不可以变化,
在 C 语言中,有两种类型的数组:
- 静态数组:编译时分配内存,大小固定。
- 动态数组:运行时手动分配内存,大小可变。
静态数组的生命周期与作用域相关,而动态数组的生命周期由程序员控制。
在使用动态数组时,需要注意合理地分配和释放内存,以避免内存泄漏和访问无效内存的问题。
malloc动态内存使用
#include <stdio.h>
#include <stdlib.h>
int main() {
int size = 5;
int *dynamicArray = (int *)malloc(size * sizeof(int)); // 动态数组内存分配
if (dynamicArray == NULL) {
printf("Memory allocation failed.\n");
return 1;
}
printf("Enter %d elements: ", size);
for (int i = 0; i < size; i++) {
scanf("%d", &dynamicArray[i]);
}
printf("\n");
printf("Dynamic Array: ");
for (int i = 0; i < size; i++) {
printf("%d ", dynamicArray[i]);
}
printf("\n");
free(dynamicArray); // 动态数组内存释放
return 0;
}
动态数组和静态数组区别:
主要区别就是内存提前不提前分配,
- 静态数组:编译时分配内存,大小固定。
- 动态数组:运行时手动分配内存,大小可变。需要手动释放内存
静态内存就是一般的数组
动态内存
- 内存分配:动态数组的内存空间在运行时通过动态内存分配函数手动分配,并存储在 堆 上。需要使用
malloc
、calloc
等函数来申请内存,并使用free
函数来释放内存。 - 大小可变:动态数组的大小在运行时可以根据需要进行调整。可以使用
realloc
函数来重新分配内存,并改变数组的大小。 - 生命周期:动态数组的生命周期由程序员控制。需要在使用完数组后手动释放内存,以避免内存泄漏。
关于书上说的“编译的时候分配内存”_c语言编译期间分配内存,为什么编译后的文件大小不变-CSDN博客
要知道代码的本质:其实就是内存的分配,但是为了省事,不能内存都不固定,有些值不需要改就固定了,有些不固定就需要动态。
其实理解本质最好了,因为一个C语言的代码需要进行编译然后再运行:
编译干了什么:编译的时候会把之前的一些固定的(也就是之后不变,不需要用代码来维护)先分好内存地址(放在合适的位置),比如一些全局变量、静态变量等,提前做一些准备工作
运行干了什么:运行就是使用额外的代码维护,在运行期间动态申请的空间,调用这个那个的函数和变量,完成一些逻辑。
编译:就是扫描,进行词法语法检查,代码优化而已,编译程序越好,程序运行的时候越高效。
我想你说的“编译时分配内存”是指“编译时赋初值”,它只是形成一个文本,检查无错误,并没有分配内存空间。
当你运行时,系统才把程序导入内存。一个进程(即运行中的程序)在主要包括以下五个分区: 栈、堆、bss、data、code。
代码(编译后的二进制代码)放在code区,代码中生成的各种变量、常量按不同类型分别存放在其它四个区。系统依照代码顺序执行,然后依照代码方案改变或调用数据,这就是一个程序的运行过程。
编译时分配内存 --------------- 编译时是不分配内存的。此时只是根据声明时的类型进行占位,到以后程序执行时分配内存才会正确。所以声明是给编译器看的,聪明的编译器能根据声明帮你识别错误。
运行时分配内存 --------------- 这是对的,运行时程序是必须调到“内存”的。因为CPU(其中有多个寄存器)只与内存打交道的。程序在进入实际内存之前要首先分配物理内存。
- 编译过程 -------------- 只能简单说一下,因为如果要详细的话,就是一本书了《编译原理》。编译器能够识别语法,数据类型等等。然后逐行逐句检查编译成二进制数据的obj文件,然后再由链接程序将其链接成一个EXE文件。此时的程序是以EXE文件的形式存放在磁盘上。
- 运行过程 -------------- 当执行这个EXE文件以后,此程序就被加载到内存中,成为进程。此时一开始程序会初始化一些全局对象,然后找到入口函数(main()或者WinMain()),就开始按程序的执行语句开始执行。此时需要的内存只能在程序的堆上进行动态增加/释放了。
int main(){
int a =0; //全局初始化区
char*p1; //全局未初始化区
int main() {
int b; //栈
char s[] = \"abc\"; //栈
char*p2; //栈
char*p3 = \"123456\"; //123456\\0在常量区,p3在栈上。
staticint c =0;//全局(静态)初始化区
p1 =newchar[10];
p2 =newchar[20];
//分配得来得和字节的区域就在堆区
strcpy(p1, \"123456\");
//123456\\0放在常量区,
//编译器可能会将它与p3所指向的\"123456\"优化成一个地方。
}
枚举的定义
默认第一个是0,但是定义了以后,后边就是累加
#include <stdio.h>
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY11
{
MON11, TUE11=3, WED11, THU11, FRI11, SAT11, SUN11
};//默认第一个是0,这里是MON11=0,
int main()
{
enum DAY day;
day = WED;
printf("%d",day);
printf("\n");
enum DAY11 day11;
day11 = WED11;
printf("%d",day11);
return 0;
}
枚举+Switch
#include <stdio.h>
#include <stdlib.h>
int main()
{
enum color { red=1, green, blue };
enum color favorite_color;
/* 用户输入数字来选择颜色 */
printf("请输入你喜欢的颜色: (1. red, 2. green, 3. blue): ");
scanf("%u", &favorite_color);
/* 输出结果 */
switch (favorite_color)
{
case red:
printf("你喜欢的颜色是红色");
break;
case green:
printf("你喜欢的颜色是绿色");
break;
case blue:
printf("你喜欢的颜色是蓝色");
break;
default:
printf("你没有选择你喜欢的颜色");
}
return 0;
}