从零开始的嵌入式系统开发学习Day6(linux C)

目录

一、函数指针

二、回调函数

练习:编写函数,定义一个无符号4字节的整数,然后获取每一个字节的内容,然后返回相加之和

面试题:

三、存储类型

四、内存管理

思考题1

思考题2

五、结构体

5.1 结构体的使用

5.1.1 结构体的定义和赋值

5.1.2 定义结构体同时赋值

5.1.3 结构体数组

5.2 结构体定义的方式

5.2.1无名结构体

5.2.2 有名结构体

5.2.3 struct的特殊赋值方式

5.3 结构体占用字节大小

5.3.1 结构体的位域

六、共用体

6.1 基本用法

6.2 大小端的判断

6.3 结构体中定义联合体

七、枚举类型

练习:

实例一:

实例二


一、函数指针

        函数指针:本质是一个指针,指向一个函数。

        我们定义的函数名其实就是一个指针,保存当前代码区的首地址

        函数指针学习的目的就是想解决不能定义一个指针可以像函数名一样对当前函数进行调用

        函数指针定义格式:返回值类型(*变量名)(参数);

        函数指针的作用:将一个函数作为参数传递给另一个函数需要定义成函数指针,也称为回调函数

#include <stdio.h>

typedef unsigned char uchar;  //用uchar表示 unsigned char
typedef int (*T)(int,int);	//声明T为函数指针类型
void f1()
{
	printf("hello world\n");
}
int f2()
{
	printf("this is f2\n");
	return 0;
}
int main()
{
	void (*p1)();	//定义函数指针
	p1 = f1;	//不能写成p1 = f1();  ----->调用f1函数,将返回值赋值给p1
	p1();
	//  p1 = f2;  类型不兼容
	int (*p2)(int,int);
	p2 = f2;
	p2(1,2);
	T p3;	//等价于int(*p3)(int,int)
	p3 = f2;
	p3(1,2);
	return 0;
}

二、回调函数

#include <stdio.h>

int less(int x,int y)
{
	return(x < y) ? 1 : 0;
}
int greater(int x,int y)
{
	return(x > y) ? 1 : 0;
}
void Sort(int a[],int length,int (*p)(int,int))
{
	int i,j;
	for(i = 0;i < length - 1;i++)
	{
		for(j = 0;j < length - 1;j++)
		{
			if(p(a[j],a[j+1]))
			{
#if 0
				int t = a[j];
				a[j] = a[j+1];
				a[j+1] = t;
#endif
				a[j] = a[j] + a[j+1];
				a[j+1] = a[j] - a[j+1];
				a[j] = a[j] - a[j+1];
			}
		}
	}
}
void Printf(int a[],int length)
{
	int i;
	for(i = 0; i < length;i++)
	{
		printf("%d ",a[i]);
	}
	putchar(10);
}
int main()
{
	int a[10] = {0};
	printf("请输入10个数字:\n");
	int i;
	int length = sizeof(a)/sizeof(a[0]);
	for(i = 0;i < length;i++)
	{
		scanf("%d",&a[i]);
	}
	Sort(a,length,less);
	Printf(a,length);
	return 0;
}

练习:编写函数,定义一个无符号4字节的整数,然后获取每一个字节的内容,然后返回相加之和

        要求:这个整数需要传参,返回每一个字节之和

458963212 --->‭0001 1011 0101 1011 0011 1001 0000 1100‬

0000 1100‬ --->12

0011 1001 ---> 57

0101 1011 --->91

0001 1011 ---> 27

12+57+91+27 = 187

#include <stdio.h>

int GetByteSum(unsigned int num)
{
	int sum = 0;
	//将每8位拿出来
	int byte1,byte2,byte3,byte4;
	byte1 = num & 0xff;
	byte2 = (num >> 8) & 0xff;
	byte3 = (num >> 16) & 0xff;
	byte4 = (num >> 24) & 0xff;
	sum = byte1 + byte2 + byte3 + byte4;
#if 0 
	unsigned char *p = (unsigned char)&num;
	int i;
	for(i = 0;i < 4;i++)
	{
		sum += *p;
		p++;
	}
	p = NULL;
#endif
	return sum;
}
int main()
{
	unsigned int num = 45863212;
	int n = GetByteSum(num);
	printf("n = %d\n",n);
	return 0;
}	

面试题:

        defin和typedef的区别?

        1.#define是一个预处理指令,在预处理阶段进行字符串的简单替换,并不参与到编译阶段,而typedf是类型的重定义,在编译的时候进行的类型的重定义;

        2.当使用#define和typedef定义多个指针的时候,define定义的是第一个指针,后面全是变量,而typedef定义的全是指针;

        3.define一般用于表示常量还有函数的替换工作,typedef用于类型重定义。

#include <stdio.h>
#include <stdlib.h>
//方法1:({})
//方法2:do{}while(0)
int min;
#define MAX(a,b) ({int max;if(a > b) max = a;else max = b;max;})	//({})里面可以有多条语句最后一句就是宏的返回值
#define MIN(a,b) do{if(a < b) min = a;else min = b;}while(0)

#define PRINT_MSG(msg) do{printf("%s\n",msg);return -1;}while(0)
#define S(n) #n	//代表字符串化
int main()
{
	int *p = NULL;
	int hello = 100;
	printf("max = %d\n",MAX(100,200));
	MIN(100,200);
	printf("min = %d\n",min);
	p = (int *)malloc(4);
	if(p ==NULL)
	{
		PRINT_MSG("malloc failed!\n");
	}
	printf("%s\n",S(10));	//字符串化
	return 0;
}

三、存储类型

auto:
register:定义变量在寄存器上,运行速率快,但是芯片的寄存器个数有限制,不能无限使用
            寄存器变量不能取地址
extern:使用的变量或者文件在其他文件中定义
static:
        1.限定作用域;(修饰的变量或者函数只能在本文件中使用)
        2.延长声明周期
volatile:保证数据每次都从内存中取值,而不从缓存中取数据,防止编译器对代码进行优化
        1.多线程访问同一个变量的时候
        2.使用c语言操作硬件地址的时候
#include <stdio.h>

int a= 100;
void fun()
{
	static int i = 0;
	printf("i = %d\n",i++);
	printf("a = %d\n",a);
}
int main()
{
	int a = 200;
	printf("a = %d\n",a);

	{
		int a = 300;
		printf("a = %d\n",a);
	}
	int i;
	printf("i = %d\n",i);
	fun();
	fun();
	fun();
	return 0;
}

四、内存管理

----------------------------
栈区:局部变量,正在调用的函数,形参都在栈
	栈区由于在释放时不会清0,所以在定义变量的时候不初始化,他们都是随机值,
    栈区空间由系统自动申请,自动释放
----------------------------
堆区:使用malloc分配的内存都在堆区,手动申请,手动释放
----------------------------
静态区|.bss:使用static修饰的未初始化的变量和全局的未初始化的变量在.bss段
      |----------------------------
      |.data:使用static修饰的已初始化的变量和全局的已初始化的变量在.data段
      |----------------------------
	  |.text:代码段,文本段
      |----------------------------
      |.ro: 只读数据段
      |const int a;
      |char *p = "helloworld";
#include <stdio.h>
int a; //.bss段
static int w = 4; //.data
int b = 1234;   //.data
const int g = 789; //.ro
void func(void)
{
	const int y = 1234; //.ro
	int *p = &y;  //栈区
}
int main(int argc, const char *argv[])
{
	char ch; //.stack
	char ch1 = 'a';  //.stack
	char *p = "aaaa"; //P .stack  "aaaa":.ro

	return 0;
}

思考题1

#include <stdio.h>

int main(int argc, const char *argv[])
{
	char *p = NULL;
	char *tmp = "helloworld";
	p = (tmp + 4);
	//p[-1] = ? p[-5] = ?
	printf("p[-1] = %c,p[-5] = %c\n",p[-1],p[-5]);
	return 0;
}

思考题2

#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
	char *data = "12345678";
	short *tmp = NULL;
	char p[6] = {0};
	tmp = (short *)&p[2];
	*tmp = atoi(&data[4]);
	printf("*tmp = %d\n",*tmp);
	//p[0],p[1],p[2],p[3],p[4],p[5]?
	int i = 0 ; 
	for(i = 0 ; i < 5;i++)
	{
		printf("p[%d] = %#x\n",i,p[i]);
	}
	return 0;
}
*tmp = 5678
p[0] = 0
p[1] = 0
p[2] = 0x2e
p[3] = 0x16
p[4] = 0

五、结构体

结构体是一个构造类型,结构体的成员在内存上是连续的,
但是结构体的成员的类型可以是不相同的。
结构体的关键词用struct来标注。

格式:
struct 类型名{
    成员1;
    成员2;
    。。。
};
(1)结构体使用struct来标识
(2)struct后面跟的是结构体的类型名,而非变量名
(3)结构体的成员是被{}所包括着
(4){}后面要加;
(5)结构体的成员可以不尽相同
(6)结构体成员之间用分号隔开
(7)定义结构体的时候不能赋值
(8)在结构体里面不能写函数,因为函数占用的字节不固定,但是可以写函数指针
(9)结构体访问成员的方式 “变量.成员”
(10) 结构体指针访问成员的方式“变量->成员”

5.1 结构体的使用

5.1.1 结构体的定义和赋值

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct person{
	char name[32];
	char sex;
	int age;
	int high;
	float weight;
};
int main(int argc, const char *argv[])
{
	struct person p1;
	strcpy(p1.name,"张三");
	p1.sex = 'm';
	p1.age = 18;
	p1.high = 189;
	p1.weight = 80;

	struct person *p2 = NULL;
	p2 = (struct person *)malloc(sizeof(*p2));
	if(p2 == NULL)
	{
		printf("malloc failure");
	}
	strcpy(p2->name,"张三");
	p2->sex = 'm';
	p2->age = 18;
	p2->high = 189;
	p2->weight = 80;

	return 0;
}

5.1.2 定义结构体同时赋值

#include <stdio.h>
struct person{
	char name[32];
	char sex;
	int age;
	int high;
	float weight;
};
int main(int argc, const char *argv[])
{
	struct person p1 = {"zhangsan",'m',20,186,80}; //给所有成员初始化
	struct person p2 = {             //选择成员初始化
		.name = "zhangsan",
		.weight = 80
	};


	return 0;
}

5.1.3 结构体数组

        struct person p1[3]; //这个就是结构体数组,数组中有三个strcut person

5.2 结构体定义的方式

5.2.1无名结构体

struct{
  char name[32];
  int age;
  float weight;  
}变量1,变量2;
struct 变量1;-----》这个是错误的写法

typedef struct{
    char name[32];
    int age;
    float weight;
}student_t;  //类型名
student_t 变量1,变量2;

注意:结构体可以整体赋值,数组在定义后不允许整体赋值

5.2.2 有名结构体

typedef struct student{
    char name[32];
    int age;
    float weight;
}student_t;  //类型名
student_t 变量1,变量2;

5.2.3 struct的特殊赋值方式

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct person{
	char name[32];
	char sex;
	int age;
	int high;
	float weight;
}person;
int main(int argc, const char *argv[])
{
	person p;
	p = (person){"张安",'m',20,189,80};
	printf("name = %s,sex = %c age = %d high = %d weight = %f\n",p.name,p.sex,p.age,p.high,p.weight);
//结构体数组赋值
//方法1
	person p1[2] = {
	{
		.name = "zhangsan",
		.age = 19
	},
	{
		.name = "zhangsi",
		.age = 20
	}
	};
//方法2:
person p2[3] = {
	[0] = {
		.name = "zhangsan",
		.age = 19
	},
	[2] = {
		.name = "zhangsi",
		.age = 20
	}
	};
	return 0;
}
//方法3:
//person p3[3];
//	p3[0].name

5.3 结构体占用字节大小

规则:

结构体占用字节大小的口诀是:
1.如果结构体内部的变量类型最大成员小于等于4字节,就按照最大的类型进行内存对齐
2.如果结构体内部变量类型大于四字节,就按照四字节对齐
3.结构体最终大小和结构体中成员最大类型对齐(32位和4字节对齐,64位和8字节对齐)

 

 

#include <stdio.h>
struct aa{
	char a;  //1个字节
};
struct bb{
	short a;  //2个字节
};
struct cc{
	int a;  //4个字节
};
struct dd{
	char a;  // 2个字节
	char b;
};
struct ee{
	int a;  // 8个字节
	char b;
};
struct ff{
	char a;  // 6个字节
	short b;
	char c;
};
struct gg{
	char a;  // 8个字节
	short b;
	int c;
};
struct hh{
	short a;  // 8个字节
	char b;
	int c;
};
struct ii{
	char a[3];  // 12个字节
	int b;
	char c;
	short d;
};
struct jj{
	double a;  // 24个字节
	char b;
	int c;
	short d;
	float e;
};
int main(int argc, const char *argv[])
{
	struct ff a;
	struct gg b;
	struct jj c;
	struct ii d;
	printf("aa = %ld\n",sizeof(struct aa));
	printf("bb = %ld\n",sizeof(struct bb));
	printf("cc = %ld\n",sizeof(struct cc));
	printf("dd = %ld\n",sizeof(struct dd));
	printf("ee = %ld\n",sizeof(struct ee));
	printf("ff = %ld\n",sizeof(struct ff));
	printf("gg = %ld\n",sizeof(struct gg));
	printf("hh = %ld\n",sizeof(struct hh));
	printf("ii = %ld\n",sizeof(struct ii));
	printf("jj = %ld\n",sizeof(struct jj));
	printf("ff.a = %p\nff.b = %p\nff.c = %p\n",&a.a,&a.b,&a.c);
	printf("gg.a = %p\ngg.b = %p\ngg.c = %p\n",&b.a,&b.b,&b.c);
	printf("jj.a = %p\njj.b = %p\njj.c = %p\njj.d = %p\njj.e = %p\n\n",&c.a,&c.b,&c.c,&c.d,&c.e);
	printf("ii.a = %p\nii.b = %p\nii.c = %p\nii.d = %p\n",d.a,&d.b,&d.c,&d.d);
	return 0;
}

5.3.1 结构体的位域

        压缩结构体大小的一种方式

#include <stdio.h>
struct aa{
    char a:1;
    char b:5;
    char c:3;
};
int main()
{
    printf("sizeof(aa) = %ld\n",sizeof(struct aa));
    return 0;
}
1.在结构体内部进行位域操作的时候,是从内存最低位置开始站位,如果这个变量是有符号的,
   那么它的最高位就是符号位,若果这个位域只有一位,它既是符号位又是数据位
2.结构体位域的成员不能够取地址

六、共用体

6.1 基本用法

union内部的成员公用同一块内存空间,共用体的大小就是内部最大成员的内存空间
不管union有多少个成员,union的内存空间都是这个最大成员的内存空间
格式:
union 类型名{
    char a;
    short b;
    int c;    
};

6.2 大小端的判断

字节序:

        大端序:高字节存放在地地址,低字节存放在高地址(网络序)

        小端序:高字节存放在高地址,低字节存放在低地址

 

#include <stdio.h>
typedef union aa{
    unsigned char a;
    int c;
}aa_t;
int main()
{
    aa_t a;
    a.c = 0x12345678;
    if(a.a == 0x12)
    {
        printf("这是大端机器\n");    
    }
    else if(a.a == 0x78)
    {
        printf("这是小端机器\n");    
    }
    return 0;
}

 

6.3 结构体中定义联合体

        可以让结构体的通用性更强

#include <stdio.h>
struct aa
{
    char a;
    union{
        int b;
        char c;    
    };
};
int main()
{
    struct aa a;
    a.a = 10;
    a.b = 0xaabb;
    printf("size = %ld\n",sizeof(a));
    printf("c = %#x\n",a.c);
    return 0;
}

七、枚举类型

枚举就是有限数据的罗列,比如一周有七天,一年有12个月,
这些都是有限的罗列,适合用枚举
格式:
enum 类型名{
    成员1,
    成员2,
    成员3,
    成员4 = 10,
    成员5,
    ……
    成员n,
};
1.枚举类型的关键是enum
2.枚举的成员没有变量类型
3.成员间通过逗号来区分
4.枚举的成员的值依次向下递增1
5.枚举中的成员访问不是通过.或者->来访问,直接拿成员使用
6.枚举类型的大小是4个字节
7.枚举类型如果不赋初值,默认从0开始

练习:

实例一:

#include <stdio.h>
enum week{
    MON = 1,
    TUS,
    WED,
    THU,
    FRI,
    SAT,
    SUN
};
int main()
{
    enum week day;
    printf("day size = %ld\n",sizeof(day));
    day = TUS;
    printf("day = %d\n",day);
    day = 10;  //虽然编译器不会报错,但是不能这样赋值
    		//给枚举类型变量赋值
    printf("day = %d\n",day);
    return 0;
}

实例二

#include <stdio.h>
#define S(n) case n:return #n
typedef enum leds{
    RED,
    GREEN,
    BLUE,
}leds;
char *led_type_to_str(enum leds led)
{
    switch(led)
    {
        S(RED);
        S(GREEN);
        S(BLUE);
	    default:
        	return "不存在的";    
    }
}
leds led_open(leds led)
{
    printf("打开%s灯\n",led_type_to_str(led));
    return led;
}
int main()
{
#if 0
    char *p = NULL;
    enum leds led;
    led = led_open(RED);
    p = led_type_to_str(led);
    printf("打开%s-ok!\n",p);
#endif
    printf("打开%s-ok!\n",led_type_to_str(led_open(RED)));
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值