基于c语言的linux嵌入式开发入门

Posted studyer_domi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于c语言的linux嵌入式开发入门相关的知识,希望对你有一定的参考价值。

前言

本文主要包含,c语言基本结构与语法、make及makefile的使用、main函数参数与返回值的说明、标准输入、输出、错误流的介绍以及linux管道的应用。

语言数据类型

联合体也有翻译为共用体的,结构类型,也有翻译为结构体的,仅仅是翻译问题,类型是一样的

每个变量都会占用一定的存储单元。

符号常量

100 // 直接常量
"Hello"

#define PI 3.14 //符号常量

自动类型转换

强制类型转换

在使用强制转换时应注意以下问题:

1、数据类型和表达式都必须加括号,如把(int)(x/2+y)写成(int)x/2+y则成了把x转换成int型之后再除2再与y相加了。

2、转换后不会改变原数据的类型及变量值,只在本次运算中临时性转换。

3、强制转换后的运算结果不遵循四舍五入原则。

计算顺序

int a = 3; b = 4;计算a+b%(a+b)/a+a*b-b该算式的运算结果

计算顺序
1、a+b
2、b%(a+b)/a和ab
3、a+b%(a+b)/a+a
b-b

小的数字mod 大的数字,余数还是其本身。

运算符优先级

爆破彗星是个什么样的游戏?

C语言变量存储类别

C语言根据变量的生存周期来划分,可以分为静态存储方式和动态存储方式。

静态存储方式:是指在程序运行期间分配固定的存储空间的方式。静态存储区中存放了在整个程序执行过程中都存在的变量,如全局变量。

动态存储方式:是指在程序运行期间根据需要进行动态的分配存储空间的方式。动态存储区中存放的变量是根据程序运行的需要而建立和释放的,通常包括:函数形式参数;自动变量;函数调用时的现场保护和返回地址等。

C语言中存储类别又分为四类:自动(auto)、静态(static)、寄存器的(register)和外部的(extern)。

1、用关键字auto定义的变量为自动变量,auto可以省略,auto不写则隐含定为“自动存储类别”,属于动态存储方式。
2、用static修饰的为静态变量,如果定义在函数内部的,称之为静态局部变量;如果定义在函数外部,称之为静态外部变量。函数的形式参数不可以说明为satic类型。

注意:静态局部变量属于静态存储类别,在静态存储区内分配存储单元,在程序整个运行期间都不释放;静态局部变量在编译时赋初值,即只赋初值一次;如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)。

3、为了提高效率,C语言允许将局部变量得值放在CPU中的寄存器中,这种变量叫“寄存器变量”,用关键字register作声明。

注意:只有局部自动变量和形式参数可以作为寄存器变量;一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量;局部静态变量不能定义为寄存器变量。

4、用extern声明的的变量是外部变量,外部变量的意义是某函数可以调用在该函数之后定义的变量。

#include <stdio.h>
void fn()
{
    static int x = 1;   //定义静态局部变量
    x*=2;
    printf("x=%d\\n",x);     
}

int main()
{
    int i;
    for(i=0;i<5;i++)
    {
        fn();
    }
    extern int x;      //调用外部变量
    printf("x=%d\\n",x);
    return 0;
}
int x=100;

内部函数与外部函数

人本身是有自己的特定方法的,比如当你说话的时候,不希望是别人让你怎么说你就怎么说吧,那么这种不能被外人调用的方法称为人的内部方法。人本身还有一些可以调配的方法,比如当你家人跟你说,家里没有盐了,你去买袋盐,去买盐就是他人调用你的方法,那么能被外人调用的方法称谓外部方法。

在C语言中不能被其他源文件调用的函数称谓内部函数 ,内部函数由static关键字来定义,因此又被称谓静态函数,形式为:

static [数据类型] 函数名([参数])

这里的static是对函数的作用范围的一个限定,限定该函数只能在其所处的源文件中使用,因此在不同文件中出现相同的函数名称的内部函数是没有问题的。

在C语言中能被其他源文件调用的函数称谓外部函数 ,外部函数由extern关键字来定义,形式为:

extern [数据类型] 函数名([参数])

C语言规定,在没有指定函数的作用范围时,系统会默认认为是外部函数,因此当需要定义外部函数时extern也可以省略。

字符串函数

注:C语言中不存在字符串变量,字符串只能存在字符数组中。使用这个工具类#include

多维数组的定义

数组:是一块连续的,大小固定并且里面的数据类型一致的内存空间,在声明数组后没有进行初始化的时候,静态(static)和外部(extern)类型的数组元素初始化元素为0,自动(auto)类型的数组的元素初始化值不确定。另外,二维数组一定要指定列数。

int x[][3] = {{0},{1},{1,2,3}};

int x[4][3] = {{1,2,3},{1,2,3},{1,2,3},{1,2,3}};
int x[][3] = {1,2,3,4};

linux嵌入式开发

linux嵌入式开发,其实就是用C语言开发的小工具。(例如cd、ls啥的都是linux嵌入式程序),另外,还可以用C语言开发与硬件打交道的程序(例如os(win、linux、iosandroid)、arm嵌入式、单片机、arduino),这是因为,C语言有指针,可以直接操纵内存,因此,可以做硬件编程。另外,C语言开发的程序很搞效,nginx就是c写的,apache是c++写的,nginx更加高效。

开发环境

mac、linux(ubuntu、centos),ubuntu之所以有amd64,是因为,amd是最早推出64版本的cpu的,因此,沿用了这个说法,现在intel的cpu也是支持ubuntu的。

1.检查编译器:cc -v = gcc -v,gcc name1.c name2.c -o xxx.out
2.编译 gcc -c 文件名 -o 文件名.o
3.gcc max.o hello.c
2.创建文件:touch 文件名

-rwxrwxr-x

其中第一组表示,当前用户,第二组表示当前组,第三组表示不是当前用户,也不是当前组

vim

1.shift + i 行首
2.shift + a 行尾
3.o换行
4.shift + o上一行插入
4.dd整行删除
5.x删除
6.space * 4 = tab
7.sp打开多个vim界面
8.ctrl + w+下箭头,就跳转到了下边的窗口
9.set nu打开行号

全部格式化 : gg=G
对当前行格式化(缩进): ==
对以下多行格式化(倍数操作): [count] ==
选择多行后, 执行 等号命令 =

开发

helloworld.c

gcc max.o min.o helloworld.c -o helloworld.o

#include <stdio.h> // 标准的输入输出流
#include "max.h"
#include "min.h"

// 或者把头文件里的定义放到一个文件中 

int main(int argv,char* argc[]) // 不要写 void了,返回int有意义
{
        int leftnum = 14;
        int rightnum = 57;
        int maxnum = max(leftnum ,rightnum );
        int minnum = min(leftnum ,rightnum );
        printf("Hello World\\n");
        printf("max num is %d\\n",maxnum);
        printf("max num is %d\\n",minnum );
        return 0;
}

main函数的返回值

在linux下,可以通过echo $?,来查看之前执行的程序的返回值,只有返回值为0,才表示执行成功,否则,就是执行失败。

echo $?
0

因此,return 0 ,部署随便写的,是有意义的。

main函数的参数

通过参数,可以赋值给不同的参数选项

#include <stdio.h> // 标准的输入输出流
  
int main(int argv,char* argc[]) // 不要写 void了,返回int有意义
{
        printf("argv is %d\\n",argv);
        int i;
        for(i=0;i<argv;i++){

                printf("argc[%d] is %s\\n",i,argc[i] );

        }
        return 0;
}


gcc arytes.c -o arykill.o
 ./arykill.o -8 // 操作系统向程序传递参数
argv is 2
argc[0] is ./arykill.o
argc[1] is -8

max.c

gcc -c max.c -o max.o

int max(int a,int b){
        if(a>b){
                return a;
        }else{
                return b;
        }
}

min.c

gcc -c min.c -o min.o

int min(int a,int b){
        if(a<b){
                return a;
        }else{
                return b;
        }
}

max.h

int max(int a,int b);

min.h

int min(int a,int b);

结果

Hello World
max num is 57
max num is 14

C语言编译、组合多文件,自建头文件

只需要编译 带有main函数的那个文件,把不会再修改的函数,放到公共框架内,公共类内,并生成静态库,打包放在一起。

makeFile

用make进行编译,make内部其实使用的也是gcc。通过make把大型开发项目分成若干个模块。但是,一般情况下,都是使用makefile文件,来进行结构化构建。

对于上方的c语言代码,我们来进行makefile的构建

# this is make file
testmake.o:max.o min.o helloworld.c
                     gcc max.o min.o helloworld.c -o testall.o
max.o:max.c
                    gcc -c max.c
min.o:min.c
                    gcc -c min.c

结果

make && ls
gcc -c max.c
gcc -c min.c
gcc max.o min.o helloworld.c -o testall.out
Makefile  all.h  helloworld.c  ji.c  max.c  max.h  max.o  min.c  min.h  min.o  testall.c  testall.out
./testall.out 
Hello World
max num is 57
max num is 14

linux嵌入式标准流

linux介文件

启动每一个c语言程序,只要include ,就会自动开启三个处理程序的文件工具stdin(标准输入流,读)、stdout(标准输出流,写,默认指向显示器)、stderr(标准错误流,报错)几个文件。

stdin、stdout、stderr都是文件,其实输入、输出、报错,就是看操作的文件是啥就可以了,都有默认的指向。

#include <stdio.h>
  
int main(){

        printf("hello world!\\n");
        fprintf(stdout,"please input the value a:\\n");
        int a;
        scanf("%d",&a);//&a表示取a的地址。
        fscanf(stdin,"%d",&a);
        printf("input value is : %d\\n",a);
        if(a<0){
                fprintf(stderr,"the value must > 0\\n");
                return 500;
        }
        return 0;
}

流的重定向

重定向到文件,可以多次写入到该文件中
./a.out 1>> a.txt
ls /etc >> etc.txt
重定向到文件,覆盖写入到该文件中
ls /etc > etc.txt
重定向到文件,1输出流,2代表错误输出流
./a.out 1>t.txt 2>f.txt
重定向文件,输入流<input.txt
./a.out 1>t.txt 2>f.txt <input.txt

管道

|表示一个管道
grep 从输入(流)的文本文档中,查询包含指定字符的行,并显示到输出流中

ls /etc/ | grep aa
ps -e | grep ssh

通过管道,将前一个结果的输出流,作为后一个程序的输入流。

编写应用程序并使用管道

自己可以编写两个程序,让其中一个的输入作为下一个的输出

#include <stdio.h>
  

int main(){

        int count =0;
        int s;
        int w = 0;
        int flag = 1;

        while(flag){

                scanf("%d",&s);

                if(0==s)break;

                count++;
                w+=s;
//              printf("%d,%d\\n",s,count); 切记,只能有一个输出,否则记录的是一次的输出,并将其传到管道中


        }

        printf("%d,%d",w,count);


        return 0;

}

#include <stdio.h>
  
int main(){

        int s,n;
        scanf("%d,%d",&s,&n);
        float result = s/n;
        printf("%d个人的平均值是%f\\n",n,result);
        return 0;

}

结果

./f.o | ./a.out 
3000
5000
9000
1234
5678
9999
0
6个人的平均值是5651.000000

附录

闰年

#include <stdio.h>
int main() 
{
    int year = 2014; //今年是2014年
    //补全一下代码
    
    if(year%4==0&&(year%100!=0||year%400==0)){
        
        printf("今年是闰年");
        
    }else{
        printf("今年是平年");
    }
    
    return 0;
}

4年一闰,百年不闰,400年再闰(对于数值很大的年份,如果这年能整除3200并且能整除172800则是闰年)

水仙花数

一个三位数,其各位数字立方和等于该数

#include <stdio.h>
int main()
{
    //定义三位数num,个位数sd,十位数td,百位数hd
    int num, sd, td, hd;
    //循环所有三位数
    for(   num =100 ;  num<1000  ; num++  )
    {
        //获取三位数字num百位上的数字
        hd =    num /100             ;
        //获取三位数字num十位上的数字
        td =            (num - hd*100)/10     ;
        //获取三位数字num个位上的数字
        sd =    num -hd*100 -td*10              ;
        //水仙花数的条件是什么?
        if(     hd*hd*hd + td*td*td + sd*sd*sd==num             ) 
        {
            printf("水仙花数字:%d\\n", num);    
        }
    }
    return 0;    
}

输出个三角

#include <stdio.h>
int main()
{
    int i, j, k;
    for(i=1; i<5; i++)
    {
        /* 观察每行的空格数量,补全循环条件 */
        
        for(j=i;j<5;j++)  
        {
            printf(" ");    //输出空格
        }
        /* 观察每行*号的数量,补全循环条件 */
        for( k=0 ;  k<i*2-1 ;  k++   ) 
        {
            printf("*");   //每行输出的*号
        }
        printf("\\n");     //每次循环换行
    }
    return 0;
}

    *
   ***
  *****
 *******

乘法梯形

#include <stdio.h>
int main() 
{ 

    int i, j, result;

    
   
        for(j=9;j>0;j--){
             for(i=1;i<=j;i++)
         {
            printf("%d*%d=%d",j,i,j*i);
            printf(" ");
            
        }
        printf("\\n");        
        
    }
    
    return 0;
}

9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81 
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64 
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49 
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36 
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25 
4*1=4 4*2=8 4*3=12 4*4=16 
3*1=3 3*2=6 3*3=9 
2*1=2 2*2=4 
1*1=1 

找出素数(质数)

#include <stdio.h>
int main()
{
    int m, n;
    for(m=2; m<=50; m++)
    {
        for(n=2; n<m; n++)
        {
            if(  m%n==0  )       //什么条件下跳出当前循环
              break;               //这里应该退出当前循环了
        }   
        if(m == n)   //n循环结束后,如果m=n的话就输出m
            printf("%d  ", m);
    }
    return 0;    
}

递归

递归就是一个函数在它的函数体内调用它自身。

计算n的阶乘

#include <stdio.h>
int factorial(int n){
      int result;
if(n<0)
{

printf("输入错误!\\n");
return 0;
}else if(0==n||1==n){
return 1;
}else{

result =factorial(n-1)*n;
}

return result;
}

int main(){
int n=6;
printf("%d的阶乘=%d\\n",n,factorial(n));
return 0;

}

递归函数特点

每一级函数调用时都有自己的变量,但是函数代码并不会得到复制,如计算5的阶乘时每递推一次变量都不同;
每次调用都会有一次返回,如计算5的阶乘时每递推一次都返回进行下一次;
递归函数中,位于递归调用前的语句和各级被调用函数具有相同的执行顺序;
递归函数中,位于递归调用后的语句的执行顺序和各个被调用函数的顺序相反;
递归函数中必须有终止语句。
一句话总结递归:自我调用且有完成状态。

例如:

猴子第一天摘下N个桃子,当时就吃了一半,还不过瘾,就又多吃了一个。第二天又将剩下的桃子吃掉一半,又多吃了一个。以后每天都吃前一天剩下的一半零一个。到第10天在想吃的时候就剩一个桃子了,问第一天共摘下来多少个桃子?并反向打印每天所剩桃子数。

#include <stdio.h>

int getPeachNumber(int n)
{
    int num;    //定义所剩桃子数
    if(n==10)
    {
       return 1;      //递归结束条件
    } 
    else
    {
        num = (getPeachNumber(n+1)+1)*2;   //这里是不应该用递归呢?
        printf("第%d天所剩桃子%d个\\n", n, num); //天数,所剩桃子个数
    }
    
    return num;
}

int main()
{
    int num = getPeachNumber(1);
    printf("猴子第一天摘了:%d个桃子。\\n", num);
    return 0;
}

第9天所剩桃子4个
第8天所剩桃子10个
第7天所剩桃子22个
第6天所剩桃子46个
第5天所剩桃子94个
第4天所剩桃子190个
第3天所剩桃子382个
第2天所剩桃子766个
第1天所剩桃子1534个
猴子第一天摘了:1534个桃子。

做递归的问题,关键是找到突破口。再比如:

有5个人坐在一起,问第5个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第3个人,又说比第2人大两岁。问第2个人,说比第1个人大两岁。最后 问第1个人,他说是10岁。请问第5个人多大?

#include <stdio.h> 

int guessAge(int n){
    
    
    int _age;
    
    
    if(n==1){
        return 10;
    }else{
        
        _age = guessAge(n-1) + 2;
        
    }
    
    return _age;
    
}

int main() 
{
    
    int _age = guessAge(5);
    printf("第5个人的年龄是%d岁", _age); 
    return 0;
}

每日学习单词

#include <stdio.h>
/* 定义获取单词数量的函数 */
int getWordNumber(int n)
{   
    if(n == 1)
    {
        return 1;    //第一天只会1个单词
    }
    else{
        return getWordNumber(n-1)+n ;       //到第天会的单词数量
    }
}
int main()
{
    int num = getWordNumber(10);     //获取会了的单词数量
    printf("小明第10天记了:%d个单词。\\n", num);
    return 0;
}

获取数组长度

#include <stdio.h>
int main()
{
    int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

   int i = 0;
   int length = sizeof(arr)/sizeof(arr[0]);
   for(i = 0;i<length;i++)
   {
       
       printf("%d\\n",arr[i]);
   }
   
    return 0;
}

字符串数组

使用%c和%s的技巧不一样,需要注意。

#include <stdio.h>
/* 定义say函数 */
void say(char string[])       //数组参数应该怎么写呢?
{
    printf("%s\\n",string);
}

int main()
{
    //定义字符串数组
    char string[] = "我在慕课网上学习IT技能!";
    int i = 0;
    //for(;i<sizeof(string)/sizeof(string[0]);i++)
    //{
         say(string);              //调用say函数输出字符串
    //}
   
    return 0;
}

做个排序的题

在一个长度为10的整型数组里面,保存了班级10个学生的考试成绩。要求编写5个函数,分别实现计算考试的总分,最高分,最低分,平均分和考试成绩降序排序。

排序请用多种排序方式,此处,没有实现c语言的数组返回,需要继续深入学习,后续补充这个文章。

#include <stdio.h>

double _Avg(int score[],int _length){
    
    int i = 0;
    double sum = 0;
    for(;i<_length;i++){
        sum+=score[i];
    }
    
    if(_length!=0){
         return sum/_length;
    }else{
         return 0.0;
    }
}

int* _Desc(int score[],int _length){
    
    //  int i = 0;
    //   int _desc_score[];
   
    // for(;i<_length;i++){
    //     _desc_score[i]=score[i];
    // }
    

    
    return score;
}

int main()
{
    int score[]={67,98,75,63,82,79,81,91,66,84};
    int _length = sizeof(score)/sizeof(score[0]);
    
    
    printf("总分是:%d\\n",_Total(score,_length));
    printf("最高分是:%d\\n",_Max(score,_length));
    printf("最低分是:%d\\n",_Min(score,_length));
    printf("平均分是:%f\\n",_Avg(score,_length));
    printf("NULL-Avg是:%d\\n",_Avg(score,NULL));
    
    
    int _desc_score[] =_Desc(score,_length);
    printf("考试成绩降序排序是:");
    int j = 0;
    
    for(;j<_length;j++){
        printf("%d,",_desc_score[j]);
    }
    
    printf("\\n");
    
    
    
    return 0;
}


int _Total(int score[],int _length){
    
    int i = 0;
    int sum = 0;
    for(;i<_length;i++){
        sum+=score[i];
    }
    
    return sum;
}

int _Max(int score[],int _length){
    int i = 0;
    int _max = 0;
    for(;i<_length;i++){
        if(_max<=score[i]){
        _max = score[i];
        }
        
        
    }
    
    return _max;
}

int _Min(int score[],int _length){
        int i = 0;
    int _min = score[0];
    for(;i<_length;i++){
        if(_min>=score[i]){
        _min = score[i];
        }
        
    }
    
    return _min;
}




计算一年中的第几天

#include <stdio.h>
int main() 
{ 
    /* 定义需要计算的日期 */
    int year = 2008;
    int month = 8;
    int day = 8;
    /*
     * 请使用switch语句,if...else语句完成本题
     * 如有想看小编思路的,可以点击左侧任务中的“不会了怎么办”
     * 小编还是希望大家独立完成哦~
     */
    
    int sum = 0;
    int i=0;
    for(i = 0;i<month;i++){
        
 
    switch(i){
        case 1:sum+=31;break;
        case 2:sum+=28;break;
        case 3:sum+=31;break;
        case 4:sum+=30;break;
        case 5:sum+=31;break;
        case 6:sum+=30;break;
        case 7:sum+=31;break;
        case 8:sum+=31;break;
        case 9:sum+=30;break;
        case 10:sum+=31;break;
        case 11:sum+=30;break;
        case 12:sum+=31;break;
    }
    
    
    }  
    
    
    if(year%4==0&&(year%100!=0||year%400==0)){
        
        sum+=1;
        
    }
    

printf("2008年8月8日是该年的第%d天。",sum+day);



    return 0;
}

2008年8月8日是该年的第221天。

遗留问题

1.函数如何返回数组或者对象。
2.static静态的,在C语言中如何深入理解。
3.排序算法和查找算法。

以上是关于基于c语言的linux嵌入式开发入门的主要内容,如果未能解决你的问题,请参考以下文章

使用Go语言交叉编译开发嵌入式Linux应用程序

嵌入式开发如何入门

嵌入式入门书籍推荐

linux_docker入门

学习嵌入式怎么入门和提高?嵌入式开发教程pdf

基于SSD202D芯片的最小嵌入式Linux开发板来了 仅需99元 入门嵌入式Linux必选