本文主要是介绍随堂笔记 - 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!!!
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语言的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!