嵌入式C大杂烩
此博客着重于在C基础上的知识点,基础不好的小伙伴打好基础再来哦(有部分是对经典面试题的分析)
1.关键字 static 的作用:
1). static修饰的函数只在该文件中使用。
2). static修饰的局部变量,只初始化一次,第二次使用时“继承”第一次使用后的结果,而不是初始化的值。
void PRINT(){ static int a = 3; ++a; printf("%d\n", a);}int main(){ PRINT(); PRINT(); return 0;}
结果为:4,5
3)static修饰的全局变量限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源文件中引起错误。
从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。
2.#include 与 #include "c.h"的区别
答:前者是从 Standard Library 的路径寻找和引用 c.h,而后者是从当前工作路径搜寻并引用 c.h。
3.编译
我们平时所说的编译其实分为四个阶段,分别为 预处理-编译-汇编-连接。
预处理:C源文件中以“#”开头的命令为预处理命令,例如“#include”、“#define、条件编译”#if“等。预处理就是将要包含的文件插入原文件中,将宏定义展开根据条件编译命令选择要使用的代码。经过此步后,.c文件变成.i文件。
编译:编译就是将上一步的.i“翻译”成汇编代码。经过此步后,.i文件变成.s文件。
汇编:汇编就是将.s文件翻译成一定格式的机器代码。经过此步后,.s文件变成OBJ文件(在Linux中)。
连接:连接就是将上一步的OBJ文件和系统库的OBJ文件、库文件连接起来,最终生成可以在特定平台运行的可执行文件。
4.数组与指针的区别
数组作为函数参数进行传递时,该数组自动转化成同类型的指针。
计算数组和指针的内存容量
void Func(char a[100]) { printf("%d",sizeof(a)); // 4 字节而不是 100 字节 }
5.typedef与define
#define dPS struct s *
typedef struct s * tPS;
以上两种哪个更好?
答:typedef更好。
dPS p1,p2;
tPS p3,p4;
dPS p1,p2 相当于 struct s * p1, p2;
上面的代码定义 p1 为一个指向结构的指针,p2 为一个实际的结构。第二个例子正确地定义了 p3 和 p4 两个指针。
6.下面的代码输出是什么,为什么?
**void kkk(void){unsigned int a = 6;int b = -20;(a+b > 6) ?printf("> 6") : printf("<= 6");}**
答案: “>6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20 变成了一个非常大的正整数,所以该表达式计算出的结果大于 6。
7.倒序输出"hello world"
int main(){char* src = "hello,world";int len = strlen(src);char* dest = (char*)malloc(len+1);//要为\0 分配一个空间char* d = dest;char* s = &src[len-1];//指向最后一个字符while( len-- != 0 )*d++=*s--;*d = 0;//尾部要加\0printf("%s\n",dest);free(dest);// 使用完,应当释放空间,以免造成内存汇泄露return 0;}
8.内存碎片
之所以产生内存碎片是因为malloc开辟内存空间。
内存碎片又分为外部碎片与内部碎片。外部碎片是指所有的内存中被分配后剩下的小内存块,无法被分配(比如你有五颗糖,最少一次分出去两颗,剩下的那一颗就是外部碎片)。内部碎片是指已分配的内存中没使用的内存(分给你两颗糖,只吃了一颗,剩下的那颗就是内部碎片)。
9.sizeof与strlen
sizeof是运算符,strlen是函数。
sizeof计算变量所占内存数,strlen它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符’\0’为止,然后返回计数器值(长度不包含’\0’)。
char arr[] = "hello"; printf("%d\n",sizeof(arr));//结果为6 printf("%d\n",strlen(arr));//结果为5
10.指针数组与数组指针
char(*arr)[3];//是一个指针数组,其元素为指针型数据
char *arr[3];//是一个数组指针,即指向数组的指针
第一个是指针数组,就是有一个数组,三个元素都是指针。
第二个为数组指针,就是指向数组首地址的指针。数组指针可以用来存放多维数组地址。
11.结构体内存对齐规则
struct A{ char a; int b; double c;};int main(){ printf("%d",sizeof(struct A));//结果为16 return 0;}
char占1字节,int占4字节,double占8字节,但为什么结果是16呢?
根据字节对齐,如图。
12.unsigned int 与 int
负数是用补码来表示的。
int a = 1;
//存储形式为0000 0000 0000 0000 0000 0000 0000 0001
int b = -1;
//存储形式为1111 1111 1111 1111 1111 1111 1111 1111
-1的存储形式就是1的存储形式的补码。之所以这样存储,可以方便变量a、b之间进行运算。注意,此时最高位的1是负数
。
printf("%u\n", b);
那么如果将b转换成unsigned int 输出结果是多少呢?
结果为2^32-1,即1111 1111 1111 1111 1111 1111 1111 1111转换成十进制的值减1(因为从0开始算)。注意,此时最高位的1是正数
。