嵌入式C语言

111

1.创建C程序

(1)创建C语言程序文件。

vim 001.c

(2)进入后是" REPLACE "模式,需要键盘输入" I "进入" INSERT "模式。

(3)程序写完后按" ESC "退出输入模式。

(4)输入" :wq "。

(5)接下来通过gcc编译。

gcc -o 001 001.c  //链接文件
./001 //执行,可执行目标文件

2.编译过程

(1)预处理:去掉注释、加载头文件、替换宏定义,不会进行语法检查。

gcc -E -o a.i 001.c

(2)编译。

gcc -S -o a.s a.i
gcc -S -o a.s 001.c //一步到位

(3)汇编。

gcc -c -o a.o a.s
gcc -c -o a.o 001.c //一步到位

(4)链接。

gcc -o build a.o
gcc -o build 001.c //一步到位

3.编译错误

(1)预处理错误

#include "name" :自己定义的头文件,当前目录下找,即与" 001.c "在同一目录下。

 #include <name> :系统目录下找。

若新建的头文件 "abc.h" 在文件夹 " inc " 中,即不与" 001.c "在同一目录下,可用下面命令:

gcc -I ./inc -o build 001.c 

(2)语法错误

(3)链接错误:原材料不够。例如:

#include "stdio.h"
#include "abc.h"

void fun(void);

int main()
{
    int a = ABC;
    printf("Hello World! \n");
   
    fun();
    return 0;
}

error:undefined reference to 'fun'

fun函数在abc.c中:

void fun(void)
{

}

我们可以这样做:

gcc  -I ./inc -o build 001.c abc.c //不建议
/* 生成汇编文件 */
gcc -I./inc -o a.o 001.c
gcc -I./inc -o b.o abc.c

/* 链接 */
gcc -o build a.o b.o 

寻找标签(即函数)是否实现了,链接时是否一起加入链接。

多了。

error:multiple definition of 'fun'

多次实现了标签,只保留一个标签实现。

4.预处理使用

(1)包含类

#include 包含头文件 -- 头文件包含进来在当前的位置进行展开 -- 展开的最终目的是把头文件进行一次编译

(2)宏(所有宏名全都是以大写字母为标识符)

#define 宏 -- 替换(不进行语法检查)(语法检查在编译时进行)

#define  宏名  (宏体)(在宏名和宏体之间至少一个空格)(加括号)

例如:

#define ABC 5 + 3
printf(" %d \n",ABC * 5); // 5 + 3 * 5

#define ABC (5 + 3)
printf(" %d \n",ABC * 5); // (5 + 3) * 5
/* 宏函数 */
#define ABC(x) (5 + (x)) // 参数加括号,整体加括号

(3)条件编译

#ifdef

#else

#endif

(4)预定义宏

系统定义好的,C语言编译器定义好的,跟具体操作系统关系不大,在工程调试应用众多。

这些宏名全部都由两个下划线组成,首位相组合,又称其为系统定义宏。

__FUNCTION__ : 函数名
__LINE__ :行号 
__FILE__ :文件名

1 #include <stdio.h>
2
3 int main()
4 {
5    printf("the %s,%s,%d\n",__FUNCTION__,__FILE__,__LINE__);
6
7    return 0;
8 }

/* 输出 */
the main , 002.c , 5

1 #include <stdio.h>
2
3 int fun()
4 {
5    int a;
6    
7    printf("the %s,%s,%d\n",__FUNCTION__,__FILE__,__LINE__);
8
9    return 0;
10 }
11
12 int main()
13 {
14    fun();
15
16    return 0;
17 }

/*  输出结果 */
the fun, 002.c, 7

我们定义宏名时尽量不要用这种方法,因为系统已经定义好了。

条件预处理

方式一:

#include <stdio.h>

#define ABC //开关打开,只需要这一个开关,在debug和release之间切换

int main()
{
    #ifdef ABC
        printf("%s \n",__FILE__);
    #endif                         //预处理时是否屏蔽

    printf("Hello World! \n");

    return 0;

}
gcc -D宏名 //预处理之前通过编译器人为增加的一个宏名

//注:D后面直接跟宏名

e.g.
gcc -DABC  == #define ABC

方式二:

003.c

#include <stdio.h>

int main()
{
    #ifdef ABC
        printf(" %s \n",__FILE__);
    #endif                         //预处理时是否屏蔽
                                     如果定义了"ABC",就打印文件名,即"003.c";
                                     如果没有,就屏蔽。

    printf("Hello World! \n");

    return 0;
}

----------------------------------------------------

gcc -DABC -o build 003.c
./build

----------------------------------------------------
/* 输出 */
 003.c
Hello World!

5.宏展开下的#、##

多用于内核或驱动

#      字符串化

##    连接符号 

使用场合:宏体中实现

e.g.

#include <stdio.h>

#define ABC(x)  #x
#define DAY(x)  myday##x

int main()
{
    int myday1 = 10;
    int myday2 = 20;

    printf(ABC(ab\n));                   //相当于 "ab\n"
    printf("The day is %d \n",DAY(1));   //myday1

    return 0;
}
-----------------------------------------------------------
/* 输出 */
ab
10

内核中应用例如:

6.关键字

关键字没有什么神秘,它其实就是一串字符串,只是这样的字符串被编译器赋予了一定的物理意义。

(1)sizeof

#include <stdio.h>

int main()
{
    int a;
    
    printf("The a is %lu \n",sizeof a);  // %lu  无符号长整型 unsigned long

    return 0;

}
--------------------------------------------------------------------------
/* 输出 */
The a is 4

sizeof(不是函数):编译器给我们查看内存空间容量的一个工具。

嵌入式开发中没有操作系统,裸板操作的话不能调用printf函数,但可以调用sizeof判断大小。

-----------------------------------------------------------------------------------------------------------------------------

C操作对象:资源(内存)(内存类型的资源:LCD缓存、LED灯)

C语言如何描述这些资源的属性?

我们需要考虑资源的大小,而数据类型帮我们解决资源大小限制的问题。

------------------------------------------------------------------------------------------------------------------------------

硬件操作的最小单位:bit(位) 1/0

软件操作的最小单位:8 bit(位) == 1 Byte(字节)

------------------------------------------------------------------------------------------------------------------------------

(2)char:一个字节

应用场景:硬件处理的最小单位

ASCII 码表      8bit

8 bit = 256      范围0~255

(3)int

大小:根据编译器来决定。

编译器最优的处理大小:系统一个周期所能接受的最大处理单位:int

32 bit        4 B             int

16 bit        2 B             int(比如单片机系统)

一个int可能是2个字节,也可能是4个字节。

2 Byte    65535

----------------------------------------------------------------------------------------------------

注:在C语言中,需要用特定的前缀来表示使用哪种进制。

0前缀表示八进制,0x/0X前缀表示十六进制,所以十进制的16表示成八进制是"020",表示成十六进制是"0x10"。

%d  以十进制显示数字

%o  以八进制显示数字

%x  以十六进制显示数字

把各进制前缀显示出来得加点修饰

八进制:%#o

十六进制:%#x

----------------------------------------------------------------------------------------------------------

(signed) int 右移

我们看到的数都是以二进制的形式在计算机下操作的,并且位运算符的操作对象是补码。

数的正负如何表示?

通常采用的方法是在二进制数的前面增加一位符号位。符号位为0表示这个数是正数,符号位为1表示这个数是负数。

这种形式的数称为原码。

正数:原码 = 反码 = 补码

负数:原码

           反码:保留其符号位,将原码的数值位按位取反得到。

           补码:反码 + 1。

e.g.

int a = 10;

0000 0000 0000 0000 0000 0000 0000 1010    ----    原码

正数直接右移。

#include <stdio.h>

int main()
{
    int a = 10;
    a = a >> 2;
    
    printf("a = %d \n");
  
    return 0; 
}
-----------------------------
/* 输出 */
a = 2

e.g.

int a = -10

1000 0000 0000 0000 0000 0000 0000 1010  ----- 原码

1111 1111 1111 1111 1111 1111 1111 0101 ---- 反码

1111 1111 1111 1111 1111 1111 1111 0110 ---- 补码

右移两位(在左边正数补0,负数补1)

1111 1111 1111 1111 1111 1111 1111 1101 ----右移两位后

接下来再算回去(取反,+1)或(-1,取反)

取反

1000 0000 0000 0000 0000 0000 0000 0010

+1

1000 0000 0000 0000 0000 0000 0000 0011

这个二进制表示:-3。

#include <stdio.h>

int main()
{
    int a = -10;
    a = a >> 2;
    
    printf("a = %d \n");
  
    return 0; 
}
-----------------------------
/* 输出 */
a = -3

-----------------------------------------------------------------------------------------------------------------------------

unsigned、signed

内存空间的最高位是符号位还是数据

无符号:数据,显卡产生的数据。

有符号:数字,作为加减乘除使用的。

注:正数在内存中是以原码的形式储存,负数在内存中是以补码的形式存储。

如果是数据,尽量用unsigned int定义。

---------------------------------------------------------------------------------------------------------------------------------

(4)float、double

float:4个字节

double:8个字节

浮点型常量

1.0  1.1 double类型,占8个字节,非常耗费空间

1.0     float 类型,占4个字节

(5)void

void fun();函数不返回

7.自定义数据类型

自定义 = 基本元素的集合

(1)struct

元素之间的和

struct myabc{
    unsigned int a;
    unsigned int b;      告诉编译器这是我们自己定义的一块东西,即告知编译器我们具备了
                         myabc这样内存空间定义的能力。
    unsigned int c;
    unsigned int d;
};
stuct myabc mybuf;

结构体顺序是有要求的。

(2)union:共用体

共用起始地址的一段内存,技巧型代码。

union myabc{
    char a;
    int b;
}

(3)enum:常量的集合。

8.类型修饰符:对资源属性中位置的限定

(1)auto

可读可写中分配的一个区域,普通内存。

auto int a;

区域如果在{ }中,栈空间。

(2)register

限制变量定义在寄存器上的修饰符。

register int a;

内存(存储器)    寄存器

0x10                      R0,R2

a放在寄存器上比放在内存中访问的效率要高很多,要快的多。

定义一些快速访问的变量,编译器会尽量安排CPU的寄存器去存放这个a,如果寄存器不足时,a还是放在存储器中。

&这个符号对register不起作用。

#include <stdio.h>

int main()
{
    register int a;

    a = 0x10; //十进制:16

    printf("The a is %d \n",a);

    return 0;
}
--------------------------------------
/* 输出 */
The a is 16

(3)static

静态

应用场景:

修饰3种数据:

(1)函数内的变量

int fun()
{
    int a;  ==>  static int a;
}

(2)函数外部的变量:全局变量

int a; ==> static int a;
int fun()
{

}

(3)函数的修饰符

int fun(); ==> static int fun();

在内核源代码中会大量看到,在多文件工程中用的比较多。

(4)const

常量的定义(中看不中用)

只读的变量,在R/W区。

const int a = 100

-----------------------------------------------------------------------------------------------------

内存泄漏

-----------------------------------------------------------------------------------------------------

(5)volatile:告知编译器编译方法的关键字,不优化编译。

修饰变量的值的修改,不仅仅可以通过软件,也可以通过其他方式(硬件外部的用户)

int a = 100; 它的修改可能是外部来修改

while( a == 100);

mylcd();

-------------------------
[a]:a的地址

f1:LDR R0,[a]
f2:CMP R0,#100
f3:JMPEQ f1    ==> JMPEQ f2 /* 优化后 */
f4:mylcd()

----------------------------------------------------------------------------------------------------------------------------

汇编

[LDR]:从存储器中将一个32位的字数据传送到目的寄存器中。该指令通常用于从存储器中读取32位的数据到通用寄存器,然后对数据进行处理。

LDR R0,[R1]   //将存储器地址为R1的字数据读入寄存器R0

9.常用运算符

(1)* /

CPU

int a = b * 10;    //CPU可能多个周期,甚至要利用软件的模拟方法去实现乘法
int a = b + 10;    //CPU一个周期可以处理

(2)%

0 % 3 = 0     1% 3 = 1    2 % 3 = 2    3 % 3 = 0   4 % 3 = 1  ... ...

n % m = res [0,m-1]

1.取一个范围的数:

e.g.  给一个任意的数字,得到一个1到100以内的数字

(m % 100) + 1 => res

2.得到m进制的一个个位数

3.循环数据结构的下标

(3)逻辑运算符

真    假

返回结果就是 1  0

int a = -1;
if(a)

|| 、&&

A || B === B || A   //不等

A && B

A || B

在C语言编译器中,它是这样考虑的,根据我们的翻译顺序,它首先看到A,假设A已经为真,那么B就不执行了,同样,在 (A && B)中,如果A已经为假,那么B就不执行了。它按先后顺序预判断,然后再决定是否执行B。

#include <stdio.h>

int main()
{
    int a = 10;
    int res;

    res = (a==10) || printf("====\n");   //A 和 B 可以指向任何东西

    printf("The res is %d\n");
    return 0;
}
--------------------------------------------------------------------
/* 输出 */
The res is 1

--------------------------------------------------------------------

#include <stdio.h>

int main()
{
    int a = 10;
    int res;

    res = (a!=10) || printf("====\n");   //A 和 B 可以指向任何东西

    printf("The res is %d\n");
    return 0;
}

---------------------------------------------------------------------
/* 输出 */
====
The res is 1

---------------------------------------------------------------------

!

对比位运算中取反

e.g.

int a = 0x0;

!a  if(!a){}

~a 0xffff ffff

(4)位运算

<< 、>>

左移:乘法  ====>   *2    二进制下的移位

如:m << 1;====>  m * 2

       m << n;  ====>  m * 2^n 

4:  00100

8:01000

int a = b * 32;====>  b << 5 (一个周期) 

[数据、数字]

-1 * 2  -2:

8 bit

10000001                                      1000 0010

11111110                                       1111 1101

11111111 === -1                            1111 1110

右移:除以2,m >> n,m/2^n

符号变量有关

int a;                       a >> n

unsigned int a;      a >> n

-----------------------------------------------------------------------------------------------------

&、|、^

A & 0 = 0                                    

&:屏蔽

e.g.

int a = 0x1234;

a & 0xff00;//屏蔽低8位

A & 1 = A

&:取出

&:清零器

|:

A | 0 = A

保留

A | 1 = 1

设置为高电平的方法

e.g.

设置一个资源的bit5为高电平,其他位不变

int a;

a = (a | (0x1 << 5)) =====>   a | (0x1 << n)

清除第五位

int a;

a = a & ( ~(0x1 << 5));     ====>  a = a & (~(0x1 << n));

-------------------------------------------------------------------------------------------------

^、~

^:

1 ^ 1 = 0  0 ^ 0 = 0

1 ^ 0 = 1

算法

int fun()
{
    int a = 20;
    int b = 30;
    
    int c;     方法一
    c = a;
    a = b;
    b = c;
    -----------------
    a = a ^ b; 方法二
    b = a ^ b;
    a = a ^ b;

    a = 30; b = 20;
}

~:

0xf0   ~  -> 0xffff ff0f 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值