[置顶] 嵌入式C语言实战开发详解(一)

时间:2022-03-23 19:12:01

一、概述

1、嵌入式开发中为什么选择C语言?

因为操作系统的内核都是使用的C语言,而且C语言也有如下几个优点:

(1)出色的移植性,能在多种不同体系结构的软/硬平台上运行(修改量越小,移植性越好);

(2)简洁紧凑,使用灵活的语法机制,并能直接访问硬件(效率高);

(3)很高的运行效率

拓展:

什么时候使用汇编什么时候使用C语言呢?(C VS 汇编)

汇编是低级语言,不能实现复杂的功能,所以:

当对硬件做初始化——汇编

当对硬件做复杂操作——C语言


面向过程处理机制 VS 面向对象处理机制(详情戳网址)

http://blog.csdn.net/wzhcalex/article/details/51878170


2、嵌入式开发中的地位:

(1)嵌入式Linux应用软件开发工程设计;

(2)嵌入式Linux驱动开发工程师;

(3)嵌入式BSP开发工程师;

(4)嵌入式Kernel(内核)开发工程师;


3、精通C语言考核标准:

(1)企业笔试题;

(2)累积的代码量(强化编程训练)

(3)良好的编码规范(华为的编码规范要求);

(4)行业应用的项目经验;


4、如何学习C语言(外功与内功兼修)

(1)零基础学习经历过程(菜鸟如何修炼成老鸟)

(2)算法在C语言开发

(3)《C和指针》《C语言专家编程》《程序员的自我修养》《高质量C/C++编程》《编程之美》


5、C语言的标准有哪些?

K&RC、C89、C99、C11

注:

gcc支持的C89,部分兼容C99

不同的编译器标准不一样

很多编译器支持的是C89


二、数据类型

1、什么是数据类型?

数据集合的划分,不同的数据类型对CPU的意义是不一样的。


2、数据类型有哪些?

[置顶]        嵌入式C语言实战开发详解(一)


3、左右法则

 右左法则:首先从最里面的圆括号内未定义的标识符开始阅读看起,然后往右看,再往左看。每当遇到圆括号时,就应该掉转阅读方向。一旦解析完圆括号里面所有的东西,就跳出圆括号。重复这个过程直到整个声明解析完毕。

企业笔试题:

1、用变量a给出下列定义

a) 一个整型数(An integer):int a;

b) 一个指向整型数的指针(A pointer to an integer):int  *a;

c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a

pointer to an integer):int  **a;

d) 一个有10个整型数的数组(An array of 10 integers):int a[10];

e) 一个有 10 个指针的数组,该指针是指向一个整型数的(An array of 10

pointers to integers):int  *a[10];

f) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers):

int  (*a)[10];

g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer

to a function that takes an integer as an argument and returns an integer):

int  (*a)(int);

h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返

回一个整型数( An array of ten pointers to functions that take an integer

argument and return an integer ):  int   (*a[10])(int).


2、int  *(*(*fpl)(int))[10];

fpl:函数指针变量,该函数指针指向一个形参为int,返回值为数组指针,该数组指针指向一个整型指针;

   

     int  *(*(*arr[5])())();

arr:函数指针数组,该数组里的元素指向一个形参为空,返回值为函数指针的函数,该函数指针指向一个形参为空,返回值为整型的指针。


     float (*(*b())[ ])();

b:函数,形参为空,返回值为数组指针,该指针指向一个函数指针数组,该数组里的元素指向一个形参为空,返回值为float的函数。


     void* (*c)(char, int(*)());

c:函数指针变量,该函数指针指向一个形参为char 、函数指针,返回值为void*,该函数指针的形参为空,返回值为int。


    void** (*d)(int *,char **(*)(char *,char **));

d:函数指针变量,指向函数形参为char,函数指针,返回值为void **,该函数指针形参为 char*,char**,返回值为char**;


    float(*(*e[10])(int *))[5];

e:函数指针数组,指向函数形参为 int *,返回值为数组指针,该数组元素指向float;


4、隐式类型转化与强制类型转化:

char < int < float < double  隐式类型转化(自动)


代码示例:

[置顶]        嵌入式C语言实战开发详解(一)

[置顶]        嵌入式C语言实战开发详解(一)

这就是隐式类型转换;


强制类型转化:

代码示例:

[置顶]        嵌入式C语言实战开发详解(一)

[置顶]        嵌入式C语言实战开发详解(一)


上述代码中的char*就是强制类型转化


5、数据类型的重要知识点:

字节长度:

bit;

字节 = 8bit;

半字 = 2个字节 = 16bit;

字 = 4个字节 = 32;


6、基本数据类型

char                1个字节;

short              2个字节;

int                  4个字节;

long               4个字节;

float               4个字节;

double           8个字节;

long long       8个字节;

代码查询:

[置顶]        嵌入式C语言实战开发详解(一)

[置顶]        嵌入式C语言实战开发详解(一)

拓展:

为什么任何类型的指针都是4字节?

因为指针指向地址,地址长度都是固定的。而地址长度是由操作系统决定,如果操作系统是32位的,地址长度为4字节;如果操作系统是64位的,则地址长度为8字节。


char 取值范围:

无符号:0~255(2^8-1)

有符号:-128~127

有符号时:第一个为符号位:

0为整数:0 000 0000 = 0; 0 111 1111 = 127;

1为负数:负数时计算机保存补码

                1 000 0000  取反加1  =  -128

                1 111 1111  取反加1  = -1

补码取反加1成原码


例如:

打印~2:常量都是有符号的:0 000 0010 

                                    取反:1 111 1101 负值

                              取反加1:0 000 0011   =   -3


代码举例:

[置顶]        嵌入式C语言实战开发详解(一)

[置顶]        嵌入式C语言实战开发详解(一)

分析:-128   1 000 0000

        取反   1 111 1111 

        加1     0 111 1111   =  127


企业笔试题:

[置顶]        嵌入式C语言实战开发详解(一)

[置顶]        嵌入式C语言实战开发详解(一)

输出结果分析:

有符号:i = 127  a[127] = -128

              i = 128  a[128] = 127

              ...

              i = 255  a[255] = 0; 0 相当于‘\0’

              strlen(a)统计到‘\0’跳出,共255个。

无符号:i = 0   a[0] = 255;

              i = 1   a[1] = 254;

              ...

              i = 255  a[255] = 0;

              strlen(a)统计到‘\0’跳出,共255个。


有符号与无符号数据的区别:

实例解析:

[置顶]        嵌入式C语言实战开发详解(一)

[置顶]        嵌入式C语言实战开发详解(一)

分析:

有符号数和无符号数进行比较运算时(==、<、>、<=、>=)有符号数隐式转换成了无符号数(即底层的补码)但是此数从有符号数变成了无符号数。


7、sizeof与strlen区别(详情请戳网址):

http://blog.csdn.net/wzhcalex/article/details/51852632

                                                 

8、变量与常量

变量三大特点:

变量的数据类型:主要说明变量占用的内存空间的大小,如 int型;

变量的作用域:变量的有效性范围,如变量的使用范围;

变量的存储类别:变量在内存中的存储方式,不同的存储方式影响变量在内存中的生存周期。


[置顶]        嵌入式C语言实战开发详解(一)

MMU:虚拟地址单元 解决内存资源稀缺问题

(打印一个地址:打印的都是虚拟的地址)


为了保护数据的安全,操作系统会对空间做划分:

[置顶]        嵌入式C语言实战开发详解(一)

栈空间:局部变量、函数形参、自动变量(调用后释放)

    特点:先进后出,管理权限:系统

堆空间:malloc、ralloc、calloc 分配空间

    特点:先进先出,管理权限:用户

数据段:bss:保存未初始化的全局变量

              rodata:常量

              .data(静态数据区):全局变量(程序结束后释放)、static 修饰变量

全局变量与局部变量的区别:

(主要从空间的分配,没有初始化的值 什么时候释放等角度入手)


9、声明与定义:

声明:不分配内存空间,可以声明多次;

定义:分配内存空间,只可以定义一次。


变量的声明有两种情况:

定义性声明:需要建立存储空间的,例如:int a在声明时就已经建立了存储空间;

引用性声明:不需要建立存储空间, 例如:extern int a 其中变量a是在别的文件中定义的。


10、格式化输出与输入:

首先,通过代码来示例以下不同的数据类型的不同的输入输出方法:

#include<stdio.h>

int main()
{
int i;

int num;

char ch;

float f_num;

double d_num;

int a[3];

char src[100];

scanf("%d",&num);
printf("num = %d\n",num);

getchar();
scanf("%c",&ch);
printf("ch = %c\n",ch);

scanf("%f",&f_num);
printf("f_num = %f\n",f_num);

scanf("%lf",&d_num);
printf("d_num = %lf\n",d_num);

printf("please input a[3]:\n");
for(i = 0; i < 3; i++)
{
scanf("%d",&a[i]);
}
for(i = 0; i < 3; i++)
{
printf("a[%d] = %d\n",i,a[i]);
}

scanf("%s",src);
printf("src[100] = %s\n",src);

return 0;
}
运行结果:

[置顶]        嵌入式C语言实战开发详解(一)

在获取字符是用一个getchar()清空缓存区

以下是getchar()的作用(转自网络)

1.从缓冲区读走一个字符,相当于清除缓冲区  

2.前面的scanf()在读取输入时会在缓冲区中留下一个字符'\n'(输入完s[i]的值后按回车键所致),所以如果不在此加一个getchar()把

这个回车符取走的话,gets()就不会等待从键盘键入字符,而是会直接取走这个“无用的”回车符,从而导致读取有误 

3. getchar()是在输入缓冲区顺序读入一个字符(包括空格、回车和Tab) getchar()使用不方便,解决方法:  

(1)使用下面的语句清除回车:  while(getchar()!='\n');  

2)用getche()或getch()代替getchar(),其作用是从键盘读入一个字符(不用按回车),注意要包含头文件<conio.h>


三种获得字符串的方法:scanf    gets     getchar

scanf 与 gets 的区别(以下转自网络):

gets(s)函数与 scanf("%s",&s) 相似,但不完全相同,使用scanf("%s",&s) 函数输入字符串时存在一个问题,就是如果输入了空格会认

为字符串结束,空格后的字符将作为下一个输入项处理,但gets()函数将接收输入的整个字符串直到遇到换行为止。  

 1.scanf()  所在头文件:stdio.h  

语法:scanf("格式控制字符串",变量地址列表);  

接受字符串时:scanf("%s",字符数组名或指针);  

2.gets()  所在头文件:stdio.h  

语法:gets(字符数组名或指针); 

 两者在接受字符串时:  

1.不同点:  scanf不能接受空格、制表符Tab、回车等;  而gets能够接受空格、制表符Tab和回车等;  

2.相同点:  字符串接受结束后自动加'\0'。


使用gets获取字符串代码:

#include<stdio.h>

int main()
{
char src[100];

printf("input a string:");
gets(src);

printf("src = %s\n",src);


return 0;
}


使用getchar()获取字符串:

#include<stdio.h>

int main()
{
char ch;
char src[10];

int i = 0;

while((ch = getchar()) != '\n')
{
src[i] = ch;
i++;

if(i == 9)
{
printf("error!\n");
exit(1);
}

src[i] = '\0';

}

return 0;
}



如何让scanf不遇到空格结束呢?

在%前面加空格 

详情请戳网址:http://blog.csdn.net/mishifangxiangdefeng/article/details/7163002

(技术大牛写的帖)


获取数组方法:

1、

#include<stdio.h>

int main()
{
int a[3];
int i;
int *p = a;

for(i = 0; i <= 2; i++)
{
scanf("%d",p++);
}

p = a;

for(i = 0;i < 3; i++)
{
printf("a[%d] = %d\n",i,*(p + i));
}

return 0;

}
</pre><pre name="code" class="html">2、
#include<stdio.h>int main(){    int a[3];    int i;        for(i = 0; i <= 2; i++)    {        scanf("%d",&a[i]);    }    p = a;    for(i = 0;i < 3; i++)    {        printf("a[%d] = %d\n",i,a[i]);    }        return 0;}
3、

#include<stdio.h>

int main()
{
int a[3];
int i;

for(i = 0; i <= 2; i++)
{
scanf("%d",a+i);
}

p = a;

for(i = 0;i < 3; i++)
{
printf("a[%d] = %d\n",i,*(a + i));
}

return 0;

}

第一版本代码中
scanf("%d",p++);

不可以用a++ 是因为a是常量不可以自加,*p = a 指针可以自加 p++


printf是行缓冲,我们用以下代码验证:

1、

[置顶]        嵌入式C语言实战开发详解(一)

[置顶]        嵌入式C语言实战开发详解(一)


2、

[置顶]        嵌入式C语言实战开发详解(一)

[置顶]        嵌入式C语言实战开发详解(一)


3、

[置顶]        嵌入式C语言实战开发详解(一)

[置顶]        嵌入式C语言实战开发详解(一)


4、

[置顶]        嵌入式C语言实战开发详解(一)