随堂笔记 - Linux嵌入式ARM开发教程 -C语言

2023-10-13 07:20

本文主要是介绍随堂笔记 - Linux嵌入式ARM开发教程 -C语言,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

预处理

define

#define adc (2+3)
define :替换 注意加括号

关键字

数据类型

char

char :硬件处理的最小单位

硬件芯片操作的最小单位
bit 1/0
软件芯片操作的最小单位
1B = 8bit
4M速度? 4Mbit?4MB? Kbit/s KB/s

be :char buff[xx]; not be :int buff[xx];

int

int :编译器最优的处理能力,系统一个周期所能接受的最大处理单位
16bit 2B int
32bit 4B int
int a = 10;十进制10
int a = 010; 八进制8
int a = 0x10;十六进制16

long、short

long、short :特殊长度的限制符

unsigned、signed

unsigned、signed :内存空间的最高字节是符号位还是数据
unsigned 数据 signed 数字

float、double

float、double :内存存在形式
大小:
float 4B ,double 8B
浮点型常量 1.1 => double
1.0f =>float

void

void :语义符 声明 申请一种变量名,其类型靠强制类型转换
void a;

自定义数据类型

C编译器默认定义的内存分配不符合实际资源的形式

struct

元素之间的和,顺序有要求

union

共用起始地址的一段内容,技巧性代码

enum

被命名的整形常数的集合
enum abc{MOD = 100, TUE, WED} 依次累加
enum abc a = 800;
printf("the a is %lu:%d\n",sizeof(a),a);
the a is 4:800 a是一个整数,其值是在enum中取,勿这样用

typedef

typedef :对数据类型取别名
typedef int len_t len_t是int类型的外号,xxx_t 用typedef取的别名

逻辑结构

CPU顺序执行程序,用PC指针指向CPU执行位置

if else

int a = -1;
if(a) //真(非零即真)

switch、case、default

swtich(整形数字)

do、while、for

for:次数
while:条件

continue、break、goto

控制符
goto 函数内部跳转 同一个函数

类型修饰符

对内存资源存放位置的限定,资源属性中位置的限定

auto

默认情况 分配的内存可读可写的区域
区域如果在{ },则是栈空间

register

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

注意:&取地址 对register不起作用

内存(存储器) 寄存器(快)
0x xxxxx R0,R2 …

static

应用场景:修饰三种数据:
1)、函数内部的变量
int fun{
int a; -->static int a;
}
2)、函数外部的变量
int a; -->static int a;
int fun{

}
3)、函数
int fun();–>static int fun();
printf(“the is %d\n”,fun);

const

常量的定义,只读的变量,但在RW中,可以用某种方法可写
const int a = 0x12345678;
int b = 0x11223344;
//a = 100; x
指针可以
int *p=&b;
p[1] = 100;
printf("the a is %s\n",a);
``
const char * const p;//第一个修饰*p 空间只读 指向字符串 【T】
char const *p const;//第二个修饰 p 地址只读 定义硬件资源

char *p = "xxxx"; // 默认const
*p = 'a'; // error!
char buf[] = {"xxx"}; // 可变

extern

外部声明

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();

char *p;
volatile char *p; // 硬件访问用,防优化

杂项

sizeof

printf("the a is %d\n",sizeof(a));
printf("the a is %d\n",sizeof a);
printf("the a is %lu\n",sizeof(a));sizeof默认lu类型
sizeof : 编译器给我们查看内存空间容量的一个工具
sizeof 并不是函数名而是关键字

return

return : 返回的概念

运算符

算术操作运算

+、-、*、/、%

+:一个周期可以执行
*:多个周期,甚至软件模拟实现,第三方实现
%:0%3=0 1%3=1 2%3=2 3%3=0 n%m=res[0 - m-1] (取一个范围的数)(循环数据结构的下标)

逻辑运算

||、&&

先后顺序运算 满足条件直接退出
int a = 10;
int res;
res = ((a == 10) || (printf("!"));

>、<=、<、<=

int a = 0x0;
!a if(!a){}
~a 0xffff

? :

位运算

<<、>>

一个周期
取反加一 -1 ->11111111
-2 ->11111110
左移 正数负数一样
右移 符号变量有关 回不到0

int a = xxx;
while(a){
a = a>>1;
}
printf("!!!\n");

&、|

&:0 屏蔽 1 取出 清零器
| : 0 保留 1 设置 设置器

^、~

int a = 20;
int b = 30;
a = a^b;
b = a^b;
a = a^b;

~0xf0 = 0xffff ff0f

赋值运算

=

+=、-=…

内存访问符号

()

限制符
函数访问

【】

数组
内存访问的ID符号

{}

函数体的符号

->、.

&、*

指针

以p开头 int* p_xxx;
printf(“the p is %lu\n”,p_xxx);16进制用%x;

1.指针地址 多大?2.读取方式?
char *p2 = (char *)&b;
在32bit系统中,指针就是4个字节。
2^10 1K
2^20 1M
2^30 1G

float a = 1.2;
int *p;//char // unsigned char
p=&a;
printf("the p is %x\n",*p); // int 3f99 999a // char ffff fff9a // unsigned char 9a

1.0x0:地址的无效值,结束标志
if(p == 0x0) // NULL
2.指针同类型的比较才有意义

char **p;
hello world!!!
输出hello world!!!

005.c
int main(int argc, char **argv){
int i;
for(i = 0; i < argc; i++){
printf("the argv[%d] is %s\n", i, argv[i]);
}
return 0;
}
Linux
gcc -o build 005.c
./build hello 123 456
the argv[0] is ./build
the argv[1] is hello
the argv[2] is 123
the argv[3] is 456

while(argv[i] != NULL){
printf("the argv is %s\n,argv[i]");
i++;
}

数组

数据大小 读取方式
xxx[m] m的作用域是在申请的时候 建议符 可越界 xxx是常量
初始化:int a[10] = {10,20,30};CPU一般不支持空间和空间的拷贝,编译器初始化处理。
数组空间的初始化和变量的初始化本质不同,尤其在嵌入式的裸机开发中,空间的初始化往往需要库函数的辅助

char buff[10] = {'a','b','c'};
char buff[10] = {"abc"}; // ""带'\0’   4个字节
char buff[10] = "abc";  // 有两个abc 一个常量一个变量  常量向变量拷贝  buff[2] = 'e' 可以
char *p = "abc";    // 仅一个  p指向abc p[2] = 'e' 不可以
buf = "hello world" ; // 错误 第二次初始化不能直接赋值,只能逐一处理

一块空间当成字符空间,提供了一套字符拷贝函数 strcpy strncpy
原则:内存空间和内存空间的逐一赋值的功能的一个封装体,一旦空间中出现了0这个特殊值,函数就即将结束。
strcpy(buf,“hello world”);
strcpy 会内存泄漏 超过10个字符的拷贝

字符空间
ASCII码编码来解码的空间 ‘\0’结束 %s
char buff[10]; -> string
非字符串空间
数据采集 0x00-0xff 开辟一个数据盒子存储
unsigned char buff[10]; -> data
strcpy拷贝不合理 函数根据’\0’结束
用memcpy来内存拷贝
int buf[10];
int sensor_buff[100];
memcpy(buff, sensor_buff, 10*sizeof(int));
strncpy(buf,sensor_buff,10); // 00 00 00 23 会拷不进来

指针数组

指针数组:char a[100]; sizeof(a) = 1004;
多级指针:char **a;

多维数组

定义一个指针,指向int a[5][6] 的首地址

int *p; int *p[5]; int (*p)[5];
int a;                  int a[5];	
int a[5][6];
int (*p)[6] = a; int [5](*p) = a;

字节对齐、位域

位域是指信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几 个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。字节(Byte)是计算机信息技术用于计量存储容量和传输容量的一种计量单位,一个字节等于8位二进制数,在UTF-8编码中,一个英文字符等于一个字节。字节按照一定规则在空间上排列就是字节对齐。

 struct abc{char a;int b;}struct abc buff; // 1+4 = 5sizeof(buff) =8; ???8

效率,希望牺牲一点空间换取时间的效率
32bit 4个字节
最终结构体的倍数一定是4的倍数,定义顺序不一样,大小也不同,如果两个定义4个字节可以放下就合并,如 char short 4个字节

内存

1.大小。2.在哪里。
编译===》 汇编 ===》链接
*.0 build

int main(){int a;a = 0x10;printf("the a is %p\n",&a);   //  比16进制%x 多个0xprintf("the a is %p\n",main);return 0;
}

放在不同地方叫放在不同段
main 全局变量内存放的位置很低 a 局部变量很高


内核空间 应用程序不许访问


3G


栈空间 局部变量


运行时堆地址 malloc


全局的数据空间 (初始化的,未初始化的) static RW date bss
只读数据段 “hello world”(字符串常量) R TEXT
代码段 code(只读) R TEXT


0x8048xxxx


0x0:


栈空间

局部空间
运行时,函数内部使用的变量,函数一旦返回就释放,生存周期是函数内

堆空间

运行时,可以自由,自我管理的分配和释放的空间,生存周期是由程序员来决定,分配释放必须配对。
分配:
malloc(),一旦成功返回分配好的地址给我们,只需要接受。对于读法由程序员灵活把握。输入参数指定分配的大小,单位是B。不随函数执行完释放!否则会内存泄漏。如:
void fun(){
char *p;
p = (char *)malloc(100);//未释放
return;
}
释放:
free§;

只读空间

程序+字符串+全局变量
静态空间,整个程序结束时释放内存,生存周期最长
main 只读空间
unsigned char *p;
p = (unsigned char *)main; // 告诉编译器仅赋值,
只读不可写,注意报段错误 Segmentation fault

size build//Linux 查看内存大小

TEXT(只读) data(已初始化) bss(未初始化) dec hex filename
977          264                         8                 1249 4el build

strings build // 只读数据段,字符串,“xxx”
加static 就会放到全局空间
nm bulid // 查看静态空间的段名
0804xxxx d a.1702 fun函数的static int a;
0804xxxx b a.1708 main函数的static int a;
0804xxxx T fun
0804xxxx T main

函数

用指针保存函数

int (*p)(int,int,char);  函数名即地址
int (*myshow)(const char *,...);
myshow = printf; // myshow = (char (*)(const char *,...))0x8048320; 
myshow("====\n");int (*p[7])(int,int};
p[0] = fun1;
......
p[day](10,20);switch(day){case 1:fun1(10,20);break;......
}

承上启下,返回回调;
调用者:
函数名(要传递的数据) // 实参
被调者:
函数的具体实现
函数的返回值 函数名(接受的数据){ // 形参
xxx;
}
实参传递给形参。传递的形式:拷贝。
值传递会拷贝,不会影响调用者。
地址传递。

空间的读写:只读空间加const 如:void fun(const char *p){}

int sprintf(char *str, const char *format, ...);   sprintf(buf,"%d",a);

空间的结束标志:分成字符空间和非字符空间
字符空间:0x00结束

void strlen(const char *p)
{int i = 0;if(p == NULL){//return ...}while(p[i]){//+++++++++++i++;}
}void strcpy(char *dest, const char *src);

非字符空间: 0x00不做结束标志,要有结束标志(数量)
unsigned char *p

void fun(unsigned char *p, int len){int i;for(i = 0; i < len; i++){//++++++}
}

void *:数据空间的标识符,可以是任意类型(非字符空间)

void *memcpy(void *dest, const void *src, size_t n);

printf("%s",buf);//00 00 12 54 // 00 会结束

int fun(void *buf,int len)
{
unsigned char *tmp = (unsigned char *)buf;
}
返回值:指针作为空间返回的唯一数据类型

 int *fun1(void);int main(){int *p;p = fun1();//fun2(p);//不变 fun3(&p); 可以
}

注意函数返回后内存回收,地址:指向的合法性,作为函数的设计者,必须保证函数返回的地址所指向的空间是合法的(不是局部变量)

char *fun(void){return "hello";   // char buf[] = "hello"; return buf; // 无效
}
int main (){char *p;p = fun();printf("the p is %s\n",p);return 0;
}

局部变量加static变静态区可以返回。只读区可以返回。堆区可以(malloc free)。

推荐:嵌入式程序员应知道的0x10个基本问题

#define ABC 123
#define ABC (100+23)UL // 如果值很大,可能越界,加L,无负值加UL
都是常量,编译器编译后都是123,都一样,程序调用时没有运算
2.
int a[10];
int *a[10];
int (*a)[10];
int fun(int);
int (*a)(int);
int (*a[10])(int);//一个有10个指针的数组,该指针指向一个函数
// 先右在左,右边是(则是函数名 ,所以(*a),要加括号
3.
在这里插入图片描述在这里插入图片描述
4.
在这里插入图片描述
5.
老师,再见。

这篇关于随堂笔记 - Linux嵌入式ARM开发教程 -C语言的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/201655

相关文章

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

IDEA自动生成注释模板的配置教程

《IDEA自动生成注释模板的配置教程》本文介绍了如何在IntelliJIDEA中配置类和方法的注释模板,包括自动生成项目名称、包名、日期和时间等内容,以及如何定制参数和返回值的注释格式,需要的朋友可以... 目录项目场景配置方法类注释模板定义类开头的注释步骤类注释效果方法注释模板定义方法开头的注释步骤方法注

kali linux 无法登录root的问题及解决方法

《kalilinux无法登录root的问题及解决方法》:本文主要介绍kalilinux无法登录root的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,... 目录kali linux 无法登录root1、问题描述1.1、本地登录root1.2、ssh远程登录root2、

使用Python开发一个带EPUB转换功能的Markdown编辑器

《使用Python开发一个带EPUB转换功能的Markdown编辑器》Markdown因其简单易用和强大的格式支持,成为了写作者、开发者及内容创作者的首选格式,本文将通过Python开发一个Markd... 目录应用概览代码结构与核心组件1. 初始化与布局 (__init__)2. 工具栏 (setup_t

Python虚拟环境终极(含PyCharm的使用教程)

《Python虚拟环境终极(含PyCharm的使用教程)》:本文主要介绍Python虚拟环境终极(含PyCharm的使用教程),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录一、为什么需要虚拟环境?二、虚拟环境创建方式对比三、命令行创建虚拟环境(venv)3.1 基础命令3

使用Node.js制作图片上传服务的详细教程

《使用Node.js制作图片上传服务的详细教程》在现代Web应用开发中,图片上传是一项常见且重要的功能,借助Node.js强大的生态系统,我们可以轻松搭建高效的图片上传服务,本文将深入探讨如何使用No... 目录准备工作搭建 Express 服务器配置 multer 进行图片上传处理图片上传请求完整代码示例

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S

Linux ls命令操作详解

《Linuxls命令操作详解》通过ls命令,我们可以查看指定目录下的文件和子目录,并结合不同的选项获取详细的文件信息,如权限、大小、修改时间等,:本文主要介绍Linuxls命令详解,需要的朋友可... 目录1. 命令简介2. 命令的基本语法和用法2.1 语法格式2.2 使用示例2.2.1 列出当前目录下的文

Go 语言中的select语句详解及工作原理

《Go语言中的select语句详解及工作原理》在Go语言中,select语句是用于处理多个通道(channel)操作的一种控制结构,它类似于switch语句,本文给大家介绍Go语言中的select语... 目录Go 语言中的 select 是做什么的基本功能语法工作原理示例示例 1:监听多个通道示例 2:带

利用Python快速搭建Markdown笔记发布系统

《利用Python快速搭建Markdown笔记发布系统》这篇文章主要为大家详细介绍了使用Python生态的成熟工具,在30分钟内搭建一个支持Markdown渲染、分类标签、全文搜索的私有化知识发布系统... 目录引言:为什么要自建知识博客一、技术选型:极简主义开发栈二、系统架构设计三、核心代码实现(分步解析