一.请描述下堆和栈的区别
1.内存方面:
栈在内存中一般由系统管理,按照先进后出的原则,栈的空间容量较小,访问速度相对较快。堆在内存中由程序员手动申请和释放,使用完毕一定要释放不然会造成内存泄漏,堆的空间容量相对较大,可以动态的扩展空间,由于堆访问需要通过指针寻址,所以访问速度相对较慢。
2.数据结构方面:
栈是顺序存储结构,在数据结构中一般分为顺序栈与链式栈,存储的数据一般先进后出。
堆在数据结构中一般用二叉树实现,分为大顶堆和小顶堆。
二.指针数组和数组指针的写法
1.指针数组
指针数组本质是数组,数组里面存放的是指针(变量的地址)。
例如: int a = 10;
int b = 20;
int c = 30;
int *arr[3] = {&a, &b, &c};
2.数组指针
数组指针本质是一个指针,指向的是一个数组的地址。
例如:int arr[3] = {10, 20, 30};
int (*p)[3] = &arr;
注意:在这里为什么要定义成这样,如果我们定义成int *p[3] = &arr;那么分析一下,由于[]的优先级比*高,所以在这里是定义了一个p数组,类型是int *型,所以要将二者括起来(*p),让*p是一个整体。
三.函数指针和指针函数的写法,函数指针一般用在什么地方?
1.函数指针
函数指针本质上是一个指针,指向一个函数。
写法:
int (*pFunc)(int, int)
2.指针函数
指针函数本质上是一个函数,返回的类型是一个指针类型
写法:
int *pfunc(int, int)
3.二者的用法
函数指针一般用在调用函数(需要调用函数大小不知道函数名,只知道函数地址的情况)和作为函数的参数,建立函数指针表(函数指针数组)管理多个函数。
指针函数一般用在根据条件动态选择需要使用的函数。
四.请描述C语言五种内存模型
1. BSS段(Block Started by Symbol): 用来存放程序中未初始化的全局变量和静态变量的内存区域,在程序执行前会被清零。
2. 数据段(data segment): 用来存放程序中已初始化的全局变量和静态变量的内存区域。
3. 代码段(text segment): 用来存放程序执行代码的内存区域。
4. 堆(heap):用来存放进程运行中被动态分配的内存段,大小不固定,可动态扩张或缩减。调用malloc分配内存时,新分配的内存就被动态添加到堆上,调用free释放内存时,会从堆中释放。
5. 栈(stack):存放程序中的局部变量,函数参数,返回地址(但不包括static声明的变量,static变量放在数据段中),以及函数调用的上下文的信息。
五.指针常量与常量指针的定义以及区别,分别用在什么地方?
int a = 10;
int b = 20;
int const *p = &a; //常量指针,指针是一个常量,指针p不能修改变量a的值,但是可以修
改p的指向。
const int *p = &a; //常量指针, *p = 20不可以,p = &b可以;
int* const p = &a; //指针常量,指向的是一个常量的地址,指针p不能修改指向但是可以修
改变量a的值。 p = &b不行, *p = b可以。
常量指针一般用值传递,可以确保传递的值不被修改。
指针常量可以确保指针指向的地址不会改变,不会意外指向其他的非法地址,减少段错误的出现概率。
六.strlen与sizeof的区别?
strlen是一个函数,主要计算字符串'\0'之前的长度。
sizeo是一个关键字,主要用于计算类型的大小。
char buf[64] = {"hello"};
char *p = buf;
char buf2[] = {"hello"};
strlen(buf); //5,字符串长度为5
sizeof(buf); //64, char占一个字节,64个char大小为64
strlen(p); //5, 字符串长度为5
sizeof(p); //32位系统中大小为4, 64位系统中大小为8
strlen(buf2); //5 字符串长度为5
sizeof(buf2); //6, 数组最后结尾包括了'\0',所以大小为6
7.define与typedef的区别?
1.用法不同:
typedef用来定义一种数据类型的别名,增强程序的可读性。
define主要用来定义常量,以及书写复杂使用频繁的宏。
2.执行时间不同:
typedef是编译过程的一部分,有类型检查的功能。
define是宏定义,是预处理阶段的部分,在编译前,只进行字符串的替换,没有类型的检查。
3.作用域不同:
typedef有作用域限定。
define不受作用域约束,只要是在define声明后的引用都可以使用。
8.const关键字的含义以及常见用法
1.const+类型:使该类型变成常量,也就是该类型的值为常量。
2.const+变量:使该变量成为常量。
3.const+指针:指针常量int const *p/const int *p,指针存放的值不能改变,指向的地址以改变。
常量指针int* const p,指针指向的地址不可改变,所存放的值可以改变。
使用const可以防止因为意外修改数据导致程序出现错误。
9.C语言程序文件的编译过程分为几个步骤?每个步骤都做什么事情?
1.预处理阶段(-E): 将C语言源程序预处理,生成.i文件。
2.编译阶段(-S): 预处理后的.i文件编译成为汇编语言,生成.s文件。
3.汇编阶段(-C): 将汇编语言文件经过汇编,生成目标文件.o文件。
4.链接阶段(-O): 将各个模块的.o文件链接起来生成一个可执行程序文件。
10.编写一个程序,判断当前是大端字节序还是小端字节序
1.用指针来写
unsigned char num = 0x12345678; //左边为高地址
char *p = (char *)#
if (*p == 0x78)
{
//小端(高对高,低对低)
}
else
{
//大端(高对低,低对高)
}
2.使用联合体
union
{
int num;
char c;
}port;
port.num = 0x12345678;
if (port.c == 0x78)
{
//小端(高地址存放低字节,低地址存放高字节)
}
else
{
//大端(高地址存放高字节,低地址存放低字节)
}
11.编写类似strcpy函数的实现,函数原型: char *strcpy(char *strDest, const char *strSrc);
char *strcpy(char *strDest, const char *strSrc)
{
assert((*strDest != NULL) && (*strSrc) != NULL);
char *p = strDest;
while ((*strDest++ = *strSrc++) != '\0')
{
return p;
}
}
12.编写类似atoi函数的实现,函数原型:int atoi(char *str);
这里的atoi函数实现了0-9字符串转整数0-9,通过遍历字符串的每一位,将每个字符转换成整数再将其移动到前面一位。
int aoti(char *str)
{
assert(str != NULL) //判断输入是否合法
int result = 0; //计算结果
int sign = 1; //标志位0和1 1表示正数 0表示负数
int index = 0; //记录下标
if (str[0] = '-')
{
sign = -1;
index = 1;
}
for (; str[i] != '\0'; ++i)
{
assert(str[i] >= '0' && str[i] <= '9'); //如果中间出现非数字字符则报错
assert(result <= INIT_MAX / 10); //判断是否越界 INIT_MAX是int的最大值
result = result * 10 + (str[i] - '0'); //将遍历到的字符串asii码减去'0'得到数字 然后
*10将当前位移到前面一位
}
return result * sign;
}
后记
本人是一个马上毕业的小白,这是我人生中的第一篇博客,秉持着将博客作为笔记本的理念,将近期学到的比较重要的东西归纳总结出来,俗话说万事开头难,加上本人文笔有限,所以本文中可能出现比较多的错误,希望各位大佬能够多多批评指导,也希望通过这个平台,加强自己的专业素养,未来也能够做到独挡一面的嵌入式工程师。