嵌入式c——学习笔记

嵌入式c——学习笔记4

运用结构体指针输出结构体中变量

#include<stdio.h>
struct Data{
	int year;
	int mounth;
	int day;
}data; 

int main(){
Data data={2020,11,7};
  struct Data *ptr;
  ptr=&data;
  printf("%d",ptr->year);
  printf("-%d",data.mounth);
  printf("-%d",ptr->day);
	return 0;
	}

一、结构体变量和结构体指针变量作为函数参数传递问题

#include <stdio.h>
#include <string.h>
 
struct Student {
	int age;
	char sex;
	char name[100];
};
 
void InputStudent(struct Student * pstu) {   //pstu只占四个字节
	(*pstu).age = 10;
	strcpy_s(pstu->name, "张三");
	pstu->sex = 'F';
}
void OutStudent(struct Student ss) {
	printf("%d  %c  %s", ss.age, ss.sex, ss.name);
}
 
int main(void) {
	struct Student st;
	InputStudent(&st);
	printf("%d  %c  %s\n",st.age,st.sex,st.name);
	OutStudent(st);
	while (true){}
}
void OutStudent(struct Student ss) {
	printf("%d  %c  %s", ss.age, ss.sex, ss.name);
}

此处传递的是一个变量,此变量占的字节空间大,我们可以利用指针,指针只占四个字节空间,而且只存变量st的第一个字节地址,然而指针指向的是整个变量。因为指针前面的类型是struct Student代表的是整个变量。修改为指针后速度变快,占的内存空间也减小:

#include <stdio.h>
#include <string.h>
 
struct Student {
	int age;
	char sex;
	char name[100];
};
 
void InputStudent(struct Student * pstu) {   //pstu只占四个字节
	(*pstu).age = 10;
	strcpy_s(pstu->name, "张三");
	pstu->sex = 'F';
}
void OutStudent(struct Student * stu) {
	printf("%d  %c  %s", stu->age, stu->sex, stu->name);
}
 
int main(void) {
	struct Student st;
	InputStudent(&st);
	printf("%d  %c  %s\n",st.age,st.sex,st.name);
	OutStudent(&st);
	while (true){}
}

二、文件的包含问题

#include操作是:若后面带的是<>,则文件在安装路径中找;
                            若后面带的是“”,则文件在源目录中找。

三、大小端和字节序

大端字节序:把一个数的低位字节序的内容存储到高地址处,高位字节序的内容存储到低地址处

小端字节序:把一个数的低位字节序的内容存储到低地址处,高位字节序的内容存储到高地址处。

大小字节序的判断:

方法一:

#include <stdio.h>
       
int main (int argc, char **argv)
{      
    unsigned int    a  = 0x12345678;
    unsigned char  *c  =  (unsigned char *)&a;
 
    if(*c == 0x78)
        printf("Little endian\n");
    else if(*c == 0x12)
        printf("Big endian\n");
    else         
        printf("Not know");
    return 0;  
}

方法二:

#include <stdio.h>
union u_is_lsb
{      
     unsigned int  a;
     unsigned char b;
}is_lsb;  
int main (int argc, char **argv)
{      
    is_lsb.a = 0x12345678;
    if(is_lsb.b == 0x78)
        printf("Little endian!\n");
    else if(is_lsb.b == 0x12)
        printf("Big endian!\n");
    else       
        printf("Not know!");
    return 0;  
} 

四、位域

在结构体定义时,我们可以指定某个成员变量所占用的二进制位数(Bit),这就是位域。

例如:

struct bs{
    unsigned m;
    unsigned n: 4;
    unsigned char ch: 6;
};

:后面的数字用来限定成员变量占用的位数。成员 m 没有限制,根据数据类型即可推算出它占用 4 个字节(Byte)的内存。成员 n、ch 被:后面的数字限制,不能再根据数据类型计算长度,它们分别占用 4、6 位(Bit)的内存。

n、ch 的取值范围非常有限,数据稍微大些就会发生溢出

例如:

#include <stdio.h>
int main(){
    struct bs{
        unsigned m;
        unsigned n: 4;
        unsigned char ch: 6;
    } a = { 0xad, 0xE, '$'};
    //第一次输出
    printf("%#x, %#x, %c\n", a.m, a.n, a.ch);
    //更改值后再次输出
    a.m = 0xb8901c;
    a.n = 0x2d;
    a.ch = 'z';
    printf("%#x, %#x, %c\n", a.m, a.n, a.ch);
    system("pause");
    return 0;
}

对于 n 和 ch,第一次输出的数据是完整的,第二次输出的数据是残缺的。

C语言标准规定,位域的宽度不能超过它所依附的数据类型的长度。通俗地讲,成员变量都是有类型的,这个类型限制了成员变量的最大长度,:后面的数字不能超过这个长度。

C语言标准还规定,只有有限的几种数据类型可以用于位域。在 ANSI C 中,这几种数据类型是 int、signed int 和 unsigned int(int 默认就是 signed int);到了 C99,_Bool 也被支持了。

位域的存储

位域的存储规则:

1、当相邻成员的类型相同时,如果它们的位宽之和小于类型的 sizeof 大小,那么后面的成员紧邻前一个成员存储,直到不能容纳为止;如果它们的位宽之和大于类型的 sizeof 大小,那么后面的成员将从新的存储单元开始,其偏移量为类型大小的整数倍。

例如位域bs:

#include <stdio.h>
int main(){
    struct bs{
        unsigned m: 6;
        unsigned n: 12;
        unsigned p: 4;
    };
    printf("%d\n", sizeof(struct bs));
    return 0;
}

m、n、p 的类型都是 unsigned int,sizeof 的结果为 4 个字节(Byte),也即 32 个位(Bit)。m、n、p 的位宽之和为6+12+4 = 22,小于 32,所以它们会挨着存储,中间没有缝隙。

sizeof(struct bs)的大小之所以为 4,而不是 3,是因为要将内存对齐到 4 个字节,以便提高存取效率。

如果将成员 m 的位宽改为 22,那么输出结果将会是 8,因为22+12 = 34,大于 32,n 会从新的位置开始存储,相对 m 的偏移量是 sizeof(unsigned int),也即 4 个字节。

如果再将成员 p 的位宽也改为 22,那么输出结果将会是 12,三个成员都不会挨着存储。

2、当相邻成员的类型不同时,不同的编译器有不同的实现方案,GCC 会压缩存储,而 VC/VS 不会。

例如位域bs:

#include <stdio.h>
int main(){
    struct bs{
        unsigned m: 12;
        unsigned char ch: 4;
        unsigned p: 4;
    };
    printf("%d\n", sizeof(struct bs));
    return 0;
}

在 GCC 下的运行结果为 4,三个成员挨着存储;在 VC/VS 下的运行结果为 12,三个成员按照各自的类型存储(与不指定位宽时的存储方式相同)。

m 、ch、p 的长度分别是 4、1、4 个字节,共计占用 9 个字节内存,为什么在 VC/VS 下的输出结果却是 12 呢?期待您的回复。

3、如果成员之间穿插着非位域成员,那么不会进行压缩。

例如位域bs:

struct bs{
    unsigned m: 12;
    unsigned ch;
    unsigned p: 4;
};

在各个编译器下 sizeof 的结果都是 12。

总结:位域成员往往不占用完整的字节,有时候也不处于字节的开头位置,因此使用&获取位域成员的地址是没有意义的,C语言也禁止这样做。地址是字节(Byte)的编号,而不是位(Bit)的编号。

五、函数指针

1、类型 (*指针名)(参数1,参数2...); 如:int(*p)(int ,float);

例如:

#include<stdio.h>

int func1(int a, float b)
{
	return a + b;
}
int func2(int c, float d)
{
	return c + d;
}
int (*p)(int,float);
int main()
{
	int k;
	p = func1;
	k = p(10, 30.0);
	printf("k = %d\n", k);

	return 0;
}

2、函数指针当参数,实现回调函数的调用方式

#include<stdio.h>

int func1(int a, float b)
{
	return a + b;
}
int func2(int c, float d)
{
	return c + d;
}
int (*p)(int,float);
int test(int (*p)(int, float))
{
	int k = 0;
	k = p(10,30.0);
	printf("k = %d\n",k);
	return 0;
}
int main()
{
	test(func1);

	return 0;
}

函数指针的运用:函数指针可以使代码更清晰

int i;             // 定义了一个int类型的变量i; 
typedef INT int;   // 表示用户自己定义了一个整型数据类型INT,实际上就等同于int 所以:INT ii;
                   // 表示定义了一个int类型的变量ii;  
void (*pFn)(void)  // 定义了一个函数指针,该函数指针指向类似于void Foo(void)函数的函数入口地址 
typedef void (*Fun)(void) 
                   // 表示用户自己定义了一个函数指针数据类型 
Fun pf;            // 表示定义了一个函数指针pf,改函数指针指向类似于void   *pf(void)的函数   
char *a="This is ";// 这个在常量区分配一个空间,然后a指向此空间
char a[] = "This is";
             // 这个在常量区分配一个空间,然后又在栈上分配一个空间,将常量区的内容复制过来,所以可以修改
 
//比如你有三个函数:
void hello(void) { printf("你好!"); }
void bye(void) { printf("再见!"); }
void ok(void) { printf("好的!"); }
 
/* //定义一个函数指针类型/// */ 
typdef void (*funcptr)(void);
                   // 这样就构造了一个通用的函数;你用的时候可以这样:
void speak(int id)
{
   funcptr words[3] = {&hello, &bye, &ok};
   funcptr fun = words[id];
   (*fun)();
}
 这时候在工作直接调用这个speak()函数接口就可以实现相似的功能。
speak(0)就会显示“你好!”
speak(1)就会显示“再见!”
speak(2)就会显示“好的!”
 
用于处理参数和返回值的形式都一样,但是功能不确定的一组函数,可以使用函数指针
比如算术运算符,加、减、乘、除,都可以用typedef int (*calc)(int,int)代表,等等

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值