目录
前言
嵌入式C进阶,干货满满,收货满满!
数组部分:
数组的定义:
定义一个空间
1、大小
2、读取方式
定义数组: 数据类型 数组名[size] size的作用域是在申请的时候;
数组名是一个常量符号,一定不要放到“=”的左边。
错误:
char buf[100];
buf="hello world";
数组空间的初始化:空间的赋值,按照标签逐一处理
int a[10];
a[0]=xxx;
a[1]=xxx;
空间定义时,就告知编译器的初始化情况,空间的第一次赋值,初始化操作
int a[10]=空间;
C语言本身,cpu内部本身一般不支持空间和空间的拷贝
int a[10]={10,20,30};
数组空间的初始化和变量的初始化本质不同,尤其在嵌入式的逻辑开发中,空间初始化往往需要库函数的辅助。
char buf[10] ={'a','b','c'};
buf当成普通内存来看,没有问题
buf当成一个字符串来看,最后加上一个'\0',字符串的重要属性,结尾一定有个'\0'
char buf[10]={"abc"};
char buf[10]="abc";
char buf[]="abcd"; //大小为5
字符拷贝函数的原则:
内存空间和内存空间的逐一赋值的功能的一个封装体
一旦空间中出现了0这个特殊值,函数就即将结束
strcpy,strncpy:
一块空间,当成字符空间,提供了一套字符拷贝函数
char buf[10];
strcpy(buf,"hello world");
字符空间:
ASCLL码编码来解码的空间---->给人看
%s
'\0'作为结束标志
非字符串空间:
数据存储一般定义unsigned xxx;
数据采集
开辟一个存储这些数据盒子
char buf[10];----->string
unsigned char buf[10]; --->data
拷贝三要素:
1、src
2、dest
3、个数
memcpy:
int buf[10];
int sensor_buf[100];
memcpy(buf,sensor_buf,10*sizeof(int));
unsigned char buf1[10];
unsigned char sensor_buf[100];
memcpy(buf,sensor_buf,10*sizeof(unsigned char));
指针与数组:
int *a[100]
char* a[100]; //数组里存放了一堆地址
sizeof(a)=100*4; //一个指针大小4个字节
定义一个指针,指向int a[10]的首地址
int *p=a;
定义一个指针,指向int b[5][6]的首地址--->
int (*p)[6];------>一次读六个int大小
int *p[5];
int (*p)[5];
二维数组和二级指针是没有关系的,二维数组读内存是一行一行读。
字节对齐:
效率,希望牺牲一点空间换取时间的效率;最终结构体的大小一定是4的倍数
结构体里成员变量的顺序不一致,也会影响它的大小。
#include<stdio.h>
struct abc{
char a;
int b;
};
int main()
{
struct abc buf;
printf("the buf is %lu\n",sizeof(buf)); //8
return 0;
}
结构体里成员变量的顺序不一致,也会影响它的大小:
#include<stdio.h>
struct abc{
分配内存时,先分配char一个字节大小,还剩3个字节,把后两个字节分配给short,然后再分配四个字节给int
char a; //占一个字节
short e; //short 占两个字节
int b;
};
struct demo{
分配内存时,先分配char一个字节大小,还剩3个字节,但是int 占四个字节,不够分配,char占一个字节剩下3个字节,int 占四个字节,short占两个字节,还剩两个字节,
char a;
int b;
short e;
};
int main()
{
struct abc buf;
struct demo demo1;
printf("the buf is %lu:%lu\n",sizeof(buf),sizeof(demo1)); //8,12
return 0;
}
指针部分:
指针变量:存放指针这个概念的盒子
C语言编译器对指针这个特殊的概念
1、分配一个盒子,盒子要多大?
在32位系统中,指针就4个字节
2、盒子里存放的地址所指向内存的读取方法是什么?
int *p;
char *p2;
指针指向内存空间,一定要保证合法性
#include<stdio.h>
int main()
{
int a=0x123456;
int *p;
//无论是大端还是小端,都是把a的最小值赋给p1
char *p1;
p1=&a;
p=&a;
printf("the p is %x\n",*p);//123456
printf("the p1 is %x\n",*p1);//56
}
const: 常量、只读(不能变)
const char *p //内容不能变
char* const p //指向固定地址,内容可变(与硬件资源有关)
Segmentation fault :段错误,指针指向的内容被非法访问了
内存属性:
1、内存操作的大小
2、内存的变化性,可读可写的问题
指针+运算符:
int *p=0x12;
p+1: 0x12+1*sizeof(*p) --->0x12+4 //加一个单位,指针读内存的方法
指针的加法、减法运算,实际上加或减的是一个单位,单位的大小可以使用sizeof(p[0])
p++ p--: p改变了,更新地址
非线性访问:
变量名[n]
n : ID 标签
地址内容的标签访问方式,取出标签里的内容
#include<stdio.h>
int main()
{
int a=0x12345678;
int b=0x99991199;
int *p1=&b;
char *p2=(char *)&b;
printf("the p1+1 is %x,%x,%x\n",*(p1+1),p1[1],*p1+1); // 12345678,12345678,9999119a
printf("the p2+1 is %x\n",p2[1]); //11
}
指针逻辑操作符:常用:== ,!=
1、跟一个特殊值进行比较 ,0x0:地址的无效值,结束标志
if(p == 0x0)
if(p == NULL)
2、指针必须是同类型的比较才有意义(编译时检测)
提示错误:comparison of distinct pointer types lacks a cast // 类型不匹配
多级指针:本质存放地址,存放地址的地址空间
指向一个连续空间的首地址
多级指针应用:将毫不相干的东西进行组合,把它们存放在一个连续的空间(线性关系)
#include<stdio.h>
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;
}
另一种写法:
#include<stdio.h>
int main(int argc, char **argv)
{
int i=0;
while(argv[i] != NULL)
{
printf("the argv is %s\n",argv[i]);
i++;
}
return 0;
}
函数部分:
一堆代码的集合,用一个标签(函数名或者地址)去描述它
函数3要素:
1、函数名(是地址)这个思想重要
2、输入参数
3、返回值
在定义函数时,必须将3要素告知编译器
如何用指针保存函数呢?
char *p;
char (*p) [10]
int (*p)(int ,double,char); // 往右看,右边的优先级高
某个内存的某一个段确实有一段代码可以拿来用,但这个名字没有标签,只有地址,就可以使用以下代码的思想:
#include<stdio.h>
int man()
{
//函数声明
int (*show)(const char *,...);
printf("\n");
show=printf; //printf的地址赋给show,printf和show读内存的方法是一致的
//把一个地址(你认为要使用的地址)强转为一个函数
show =(int (*)(const char *,...)) 0x123456;
show("-------------\n");
//数组存放函数地址
int (*p[7])(int,int);
return 0;
}
函数具有承上启下的功能:
调用者:
函数名(要传递的数据或者叫实参) //实参,真实数据
被调者:
函数的具体实现
函数的返回值 函数名 (接收的数据或者叫形参)
{
xxxxx;
}
实参 传递给 形参
传递的形式:拷贝
必须保证实参,形参的内存大小一致
值传递:
上层调用者保护自己空间值不被修改的能力
例如:
#include <stdio.h>
//当以后看到一个普通的值传递函数,当函数调用过后,调用者的值并不会改变
void swap(int a,int b)
{
int c;
c=a;
a=b;
b=c;
}
int main()
{
int a=24;
int b=63;
printf("the a is %d,the b is %d\n",a,b); //24,63
//函数结束过后,所有局部变量结束,内存没了
swap(a,b); //swap传的是a,b的备份,并没有对a,b原有的空间(main中的a,b)发生改变
printf("the a is %d,the b is %d\n",a,b); //24,63
return 0;
}
地址传递:
上层调用者让下层子函数修改自己空间值的方式
类似结构体这样的空间,函数与函数之间调用关系--->连续空间的传递,用指针处理,节约内存空间
int a=10;
scanf("%d",a) //键盘输入后,a 不变,还是10
int a;
scanf("%d",&a); //键盘输入后得到键盘输入的值
#include <stdio.h>
void swap(int *a,int *b)
{
int c;
c=*a;
*a=*b;
*b=c;
}
int main()
{
int a=24;
int b=63;
printf("the a is %d,the b is %d\n",a,b); //24,63
swap(&a,&b);
printf("the a is %d,the b is %d\n",a,b); //63,24
return 0;
}
连续空间的传递(使用地址传递)
1、数组
实参:int a[1024];
fun(a);
形参:
void fun(int *p)
void fun(int p[10]) //连续空间10个,p还是个地址
2、结构体
struct demo{ int a,int b,double c};
struct demo demo1;
实参:
fun(demo1); fun(&demo1) //地址传递
形参:
void fun(struct demo a)
void fun(struct demo *a2) //地址传递
空间的读写:
const char *p; //只读空间
char *p ; //空间可能被修改
空间分类:
字符空间:
字符空间与非字符空间的结束标志不同,空间首地址
结束标志:内存里面存放了0x00(1B字节),
非字符空间0x00,不能当做结束标志
void fun(char *p)
{
int i=0;
while(p[i])
{
p[i]操作
p[i]=x;
a=p[i]+
i++;
}
}
//具体的实现可根据cpu的特性来处理
int strlen(const char *p)
{
/* 错误处理,判断输入参数是否合法*/
if(p==NULL)
{
return ...
}
//内存处理,从头到尾逐一处理
while(p[i]{
函数体;
i++;
}
}
""---->初始化const char*
char buf[10] ---->初始化char*
非字符空间:
数据存储一般定义unsigned xxx;
unsigned char *p;
结束标志:数量
一般写法:
void fun(unsigned char *p,int len)
{
int i;
for(i=0;i<len;i++)
{
p[i]=...;
a=p[i];
i++
}
}
int main()
{
struct sensor_data s1;
int a[1024];
fun(&s1,sizeof(buf)*1);
}
void *p:数据空间标识符
大小
例如:
void *memcpy(void *dest,const void *src,size_t n);
--------------------------------------------------------------------
函数返回只能返回一个数据类型:
返回类型:
基本数据
指针类型(空间)
数组(不行)
struct student s(void); //返回类型结构体,不建议,空间冗余
--------------------------------------------------------------------
int fun1(void); int a=0; a=fun1();
void fun2(int *p); int a=0; fun2(&a);
fun1,fun2两者效果一样
--------------------------------------------------------------------
int *fun1(void); //返回值是一个地址
int main()
{
int *p;
p=fun1();
}
//二级指针:上层函数希望子函数帮我们更新一下地址空间
void fun2(int **p);
int main()
{
int *p;
fun2(&p);
}
fun1和fun2作用或者效果一样
--------------------------------------------------------------------
返回连续空间类型:
指针作为空间返回的唯一数据类型
int *fun();
地址:指向的合法性
作为函数的设计者,必须保证函数返回的地址所指向的空间是合法的(不是局部变量)
常量区:常量的地址不会因为函数的返回而改变,一直存在
#include <stdio.h>
错误写法:
/*
char *fun(void)
{
char buf[]="hello world!";
return buf; //buf是一个局部变量,函数执行完之后空间就释放了,main中的p指向了一个消失的空间
}
*/
char *fun(void)
{
return "hello world"; //此时hello world是一个常量,地址不会随着函数的周期消失而消失
}
int main()
{
char *p;
p=fun();
printf("the p is %s\n",p);
}
使用者:
int *fun();
int *p=fun();
返回连续空间类型---->函数内部实现:
1、静态区(static)
2、只读区(不太常用)
3、堆区(malloc,free函数)
例如:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *fun(void)
{
//static char buf[]="hello world"
char *=(char *)malloc(100); // 申请
strcpy(s,"hello world"); // 初始化
return s;
}
int main()
{
char *p;
p=fun();
printf("the p is %s\n",p);
free(p); //释放
return 0;
}