嵌入式C高级进阶篇

目录

数组部分:

数组的定义:

数组空间的初始化:

字符拷贝函数的原则:

字符空间: 

指针部分:

指针变量:

const: 

指针+运算符:

指针逻辑操作符:

函数部分:

函数3要素:

值传递:

地址传递:

连续空间的传递(使用地址传递):

空间分类:

函数返回只能返回一个数据类型:

返回连续空间类型:


前言

嵌入式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;

}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
高级进阶c语言教程 目录 1. C 语言中的指针和内存泄漏 5 2. C语言难点分析整理 10 3. C语言难点 18 4. C/C++实现冒泡排序算法 32 5. C++中指针和引用的区别 35 6. const char*, char const*, char*const的区别 36 7. C中可变参数函数实现 38 8. C程序内存中组成部分 41 9. C编程拾粹 42 10. C语言中实现数组的动态增长 44 11. C语言中的位运算 46 12. 浮点数的存储格式: 50 13. 位域 58 14. C语言函数二维数组传递方法 64 15. C语言复杂表达式的执行步骤 66 16. C语言字符串函数大全 68 17. C语言宏定义技巧 89 18. C语言实现动态数组 100 19. C语言笔试-运算符和表达式 104 20. C语言编程准则之稳定篇 107 21. C语言编程常见问题分析 108 22. C语言编程易犯毛病集合 112 23. C语言缺陷与陷阱(笔记) 119 24. C语言防止缓冲区溢出方法 126 25. C语言高效编程秘籍 128 26. C运算符优先级口诀 133 27. do/while(0)的妙用 134 28. exit()和return()的区别 140 29. exit子程序终止函数与return的差别 141 30. extern与static存储空间矛盾 145 31. PC-Lint与C\C++代码质量 147 32. spirntf函数使用大全 158 33. 二叉树的数据结构 167 34. 位运算应用口诀和实例 170 35. 内存对齐与ANSI C中struct内存布局 173 36. 冒泡和选择排序实现 180 37. 函数指针数组与返回数组指针的函数 186 38. 右左法则- 复杂指针解析 189 39. 回车和换行的区别 192 40. 堆和堆栈的区别 194 41. 堆和堆栈的区别 198 42. 如何写出专业的C头文件 202 43. 打造最快的Hash表 207 44. 指针与数组学习笔记 222 45. 数组不是指针 224 46. 标准C中字符串分割的方法 228 47. 汉诺塔源码 231 48. 洗牌算法 234 49. 深入理解C语言指针的奥秘 236 50. 游戏外挂的编写原理 254 51. 程序实例分析-为什么会陷入死循环 258 52. 空指针究竟指向了内存的哪个地方 260 53. 算术表达式的计算 265 54. 结构体对齐的具体含义 269 55. 连连看AI算法 274 56. 连连看寻路算法的思路 283 57. 重新认识:指向函数的指针 288 58. 链表的源码 291 59. 高质量的子程序 295 60. 高级C语言程序员测试必过的十六道最佳题目+答案详解 297 61. C语言常见错误 320 62. 超强的指针学习笔记 325 63. 程序员之路──关于代码风格 343 64. 指针、结构体、联合体的安全规范 346 65. C指针讲解 352 66. 关于指向指针的指针 368 67. C/C++ 误区一:void main() 373 68. C/C++ 误区二:fflush(stdin) 376 69. C/C++ 误区三:强制转换 malloc() 的返回值 380 70. C/C++ 误区四:char c = getchar(); 381 71. C/C++ 误区五:检查 new 的返回值 383 72. C 是 C++ 的子集吗? 384 73. C和C++的区别是什么? 387 74. 无条件循环 388 75. 产生随机数的方法 389 76. 顺序表及其操作 390 77. 单链表的实现及其操作 391 78. 双向链表 395 79. 程序员数据结构笔记 399 80. Hashtable和HashMap的区别 408 81. hash 表学习笔记 410 82. C程序设计常用算法源代码 412 83. C语言有头结点链表的经典实现 419 84. C语言惠通面试题 428 85. C语言常用宏定义 450
很多同学对咱们C语言的课程、学习存在着很多误解,而且很多同学还不知道《C语言高级教程》后面的课程安排是什么,因此这里一并做一个说明。有同学问“别人都说开发数据库系统、Web系统还是Java、C#等最流行,咱们用C语言学了开发也不是浪费吗?”、“C语言不是做嵌入式开发、操作系统等底层的东西吗?”、“我们为什么不讲C语言嵌入式开发?”、“人家都学Web开发,咱们这学C语言开发C/S的程序不是落伍了吗?”。 确实在实际工作中,由于C语言的门槛比较高,很少有实际项目用C语言进行数据库系统、Web系统等的开发的。但是我不止一次强调“学习时学东西和工作时学东西是不一样的”。 工作以后选用的技术、语言一定是选择做合适、最方便做所从事方面的,比如开发Web程序肯定首选PHP、Java、.net,开发底层系统肯定首选C/C++,开发桌面系统肯定首选VB、Delphi,也就是“用合适的语言做合适的事情”; 但是对于在校生来说则是“用最熟悉的语言做所有事情”。初学编程的人最容易在语言的表层陷入 太长时间,如果要学数据库开发了就要去学Delphi、PB,又要学Web开发了就又去学Java、.net,又要学底层开发了就又去学C/C++, 可是每门语言都没深入,最后真正要学的数据库开发、Web开发、底层 开发等等没怎么学会,倒是把大量的时间浪费在学这些基础语法上,浪费了宝贵的时间, 这也是我痛 恨目前很多大学课程安排的一个原因。因此我的倡导就是对于在校生来说则是“用最熟悉的语言做所 有事情”,我甚至建议大学只学一门C语言就够了,然后就教大家用C语言做所有的方面。
### 回答1: 嵌入式C面试题目PDF是一份嵌入式C方面的面试题目集合,其中包含了丰富的题目,旨在测试面试者的嵌入式C编程技能。该题目集合从基础的变量类型、运算符、条件语句、循环语句等开始,逐渐深入到数组、指针、结构体、位运算、宏等高级主题,最终涵盖了面向对象编程、RTOS、嵌入式软件设计等领域。在回答这些问题时,面试者需要对C语言的语法和结构有深刻的理解,且需要能够根据场景进行合理的问题解决和优化。此外,在面试过程中,也需要具备良好的沟通和表达能力,能够清晰、简洁地回答问题,并能够充分展示自己的思考能力和编程实践经验。作为准备面试的一个重要工具,嵌入式C面试题目PDF可以帮助面试者了解自己的缺陷,找到自己需要提升的地方,从而更好地应对面试,以期能够顺利得到心仪的嵌入式C编程岗位。 ### 回答2: 嵌入式C面试题目PDF是一份面试嵌入式C开发人员的参考资料。该文档包含了多个嵌入式C面试题目,旨在测试面试者的嵌入式C开发技能和经验。 在这份资料中,涉及到了嵌入式系统的基础知识,嵌入式C语言的语法和使用方法,常见的编译器和调试工具,以及一些嵌入式开发中常用的通信接口和协议等。 此外,该文档还包括了一些实际案例和代码示例,用于测试和评估面试者的项目经验和解决问题的能力。这些示例涵盖了多个嵌入式开发领域,例如嵌入式控制器、传感器、通信模块、多任务处理等。 如果你正在寻求嵌入式C开发方面的工作机会,这份面试题目PDF将会是一份非常有帮助的参考资料。通过自己的努力和解答这些题目,你可以提升自己的技能和经验,进而在面试中获得更好的成绩和机会。 ### 回答3: 嵌入式C面试题目PDF是一份针对嵌入式C开发者的面试题目集合。这份PDF包含了很多实践性的问题,涵盖了C语言的各方面,从基础知识到实际开发项目应用,都有所涉及。 这份PDF的主要目的是为了测试求职者的嵌入式C编程知识和技能。这些问题可以帮助面试官了解求职者的技能水平和编码经验,以便他们可以做出更明智的招聘决策。同时,这些问题也为求职者提供了一个学习嵌入式C编程知识的机会,使他们更好地准备面试。 这份PDF的题目非常实用,它们反映了实际的嵌入式开发环境和问题。其中包括了在实际开发中常见的问题,如内存管理、多线程编程、IPC、驱动程序设计、网络编程等等。这些问题涵盖了很多C语言基础和进阶知识,对于学习嵌入式C编程非常有帮助。 此外,这份PDF还要求求职者在回答这些问题时,能够充分阐述自己的想法和思路。面试官并不只是关心你是否会回答问题,还关心你是否能够清晰地表达自己的想法和思路。因此,准备这些问题时,也要注意练习你的表达能力。 总而言之,这份PDF对于希望进入嵌入式行业的人来说非常有价值。它提供了一个学习和测试嵌入式C编程知识的机会,也是评估求职者技能和经验的一种有效方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值