【嵌入式C语言】指针数组结构体

news2024/12/27 20:43:26

指针与数组

  • 指针与数组
    • 指针数组
    • 数组指针
  • 多维数组
    • 数组名的保存
  • 结构体
    • 定义结构体
    • 定义结构体变量
    • 使用typedef简化结构体声明
    • 访问结构体成员
    • 结构体内存分配
    • 字节对齐
    • 位域
      • 定义位域
      • 位域的限制
      • 示例

指针与数组


指针数组和数组指针是两个不同的概念,它们涉及到指针和数组的组合使用。

指针数组

  • 定义: 指针数组是一个数组,其中的每个元素都是一个指针。
  • 示例: int *ptrArray[5]; 声明了一个包含5个元素的指针数组,每个元素都是一个指向整数的指针。
  • 用途: 指针数组常用于存储一组指针,每个指针可以指向不同类型的数据或不同位置的数组。
#include <stdio.h>
int main()
{
    char a[5] = {'a', 'b', 'c', 'd', 'e'};
    char *p[5];
    printf("sizeof(a) = %d\n", sizeof(a));
    printf("sizeof(p) = %d\n", sizeof(p));
    return 0;
}

输出

cd 'e:\CProject\output'
PS E:\CProject\output> & .\'指针数组.exe'
sizeof(a) = 5
sizeof(p) = 40

可以看到指针数组的大小是个数*对应类型的指针的大小

举例2

#include <stdio.h>
int main() {
    int a = 1, b = 2, c = 3;
    int *ptrArray[3] = {&a, &b, &c};

    // 打印指针数组中每个元素指向的值
    for (int i = 0; i < 3; i++) {
        printf("%d ", *ptrArray[i]);
    }
    return 0;
}

输出

PS E:\CProject\output> cd 'e:\CProject\output'
PS E:\CProject\output> & .\'指针数组.exe'
1 2 3 

在这个例子中,ptrArray 是一个包含3个指针的数组,每个指针分别指向整数变量 abc

数组指针

  • 定义: 数组指针是一个指针,它指向一个数组。
  • 示例: int (*ptr)[5]; 声明了一个指针,指向包含5个元素的整数数组。
  • 用途: 数组指针常用于处理二维数组或作为指向动态分配数组的指针。
#include <stdio.h>
int main() {
    int arr[3][5] = {{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15}};
    int (*ptr)[5] = arr;

    // 打印数组指针指向的数组中的元素
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 5; j++) {
            printf("%d ", ptr[i][j]);
        }
        printf("\n");
    }

    return 0;
}

在这个例子中,ptr 是一个指针,指向包含5个元素的整数数组,它被初始化为指向二维数组 arr 的第一行。

总体而言,指针数组和数组指针提供了在数组和指针之间灵活切换的方式,依赖于实际需求选择使用哪一种形式。


多维数组

数组名的保存

  • 定义一个指针,指向int a[10];的首地址
  • 定义一个指针,指向int b[5][6];的首地址
#include <stdio.h>
int main()
{
    int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int b[3][4];

    int *p1 = a;
    int **p2 = b;	// 这样是错误的,二维数组和二维指针没有关系

    return 0;
}

这样是错误的,二维数组和二维指针没有关系

二维数组是一行一行读取的,一次就读取4列的数据

二维指针是存储线性的地址

正确的读取方式

#include <stdio.h>
int main()
{
    int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int b[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};

    int *p1 = a;
    int(*p2)[4] = b; // 每次读4个

    printf("%d\n\n", *p1);
    printf("%d\n", *p2[0]);
    printf("%d\n", *p2[1]);
    printf("%d\n", *p2[2]);

    return 0;
}

举例2

int c[2][3][4];
int (*p)[3][4];	//T

结构体

C语言中的结构体(struct)是一种用户自定义的数据类型,它允许你将不同类型的数据组合在一起。这使得你可以创建更复杂的数据结构来表示现实世界中的实体。一个结构体可以包含多个成员(member),每个成员都有自己的数据类型和名称。

下面是一些关于C语言结构体的基本概念和用法:

定义结构体

要定义一个结构体,你需要使用struct关键字,后跟结构体的标签(可选),接着是花括号包围的一系列成员声明,最后以分号结束。

struct Point {
    int x;
    int y;
};

定义结构体变量

一旦定义了结构体,就可以声明该结构体类型的变量。有两种方式来做这件事:

  1. 在定义结构体的同时声明变量:
struct Point pt1, pt2;
  1. 先定义结构体,然后在其他地方声明变量:
struct Point;

// ... 之后某个地方

struct Point pt1, pt2;

如果你给结构体指定了标签(如上面例子中的Point),那么可以在后续代码中仅使用struct Point来声明新的变量。

使用typedef简化结构体声明

为了简化结构体变量的声明,通常会与typedef一起使用:

typedef struct {
    int x;
    int y;
} Point;

这样,以后可以直接使用Point作为类型名来声明结构体变量,而不需要再写struct Point

访问结构体成员

结构体成员通过点运算符.来访问:

pt1.x = 10;
pt1.y = 20;

如果结构体是指针,则使用箭头运算符->来访问成员:

Point *p = &pt1;
p->x = 30; // 等价于 (*p).x = 30;

结构体内存分配

结构体变量的内存是在声明时分配的。如果你需要动态分配结构体的内存,可以使用malloc()calloc()函数。

Point *p = (Point *)malloc(sizeof(Point));
if (p != NULL) {
    p->x = 40;
    p->y = 50;
}
// 使用完后记得释放内存
free(p);

字节对齐

#include <stdio.h>

struct student
{
    char x;      // 4 字节对齐
    int roll;    // 4
    float marks; // 4
};
int main()
{
    struct student s1;
    printf("struct size: %d\n", sizeof(s1));
    return 0;
}

字节对齐:为了效率,希望牺牲一点空间换取时间的效率

分别看一下定义的顺序不同,占用的空间大小!

#include <stdio.h>

struct student
{
    char x;   // 2
    short y;  // 2
    int roll; // 4
};

struct stu
{
    char x;   // 4
    int roll; // 4
    short y;  // 4
};
int main()
{
    struct student s1;
    printf("struct size: %d\n", sizeof(s1));

    struct stu s2;
    printf("struct size: %d\n", sizeof(s2));
    return 0;
}

在这里插入图片描述

由于定义的顺序不同,占用的空间大小也不同!

第一种定义方式里,char剩余3bytes,可以给short类型的变量使用,如下图所示:
在这里插入图片描述

第二种定义的顺序,在内存中的分布如下
在这里插入图片描述

位域

位域(bit-fields)是C语言中的一种特殊结构体成员,它允许你定义一个特定宽度的字段,以位为单位。位域主要用于需要紧凑存储或与硬件通信的场合,比如嵌入式系统编程。

在位域中,你可以指定每个成员占用的位数,这可以减少内存的使用量。然而,使用位域也可能会导致代码的可移植性问题,因为不同的编译器可能对位域的实现有所不同,包括它们如何打包和对齐数据。

定义位域

位域通过在结构体成员声明后添加冒号和位宽来定义。以下是位域的一个简单例子:

struct packed_struct {
    unsigned int f1 : 1; // 1 bit
    unsigned int f2 : 3; // 3 bits
    unsigned int f3 : 4; // 4 bits
};

在这个例子中,packed_struct包含三个位域成员:f1f2f3,分别占据1位、3位和4位。请注意,位域成员必须是整数类型(如intunsigned int等),并且不能是数组或指针。

位域的限制

  • 位域的大小:位域的最大宽度取决于底层整型的大小。例如,在大多数系统上,unsigned int通常是32位,所以你不能创建超过32位的位域。
  • 匿名位域:有时会看到不带名称但带有宽度的位域,这样的位域用于填充或者对齐。例如,unsigned int : 2;将占用2位,但没有关联的变量名。
  • 不可寻址性:由于位域是由多个位组成的,因此不能取位域成员的地址。
  • 端序依赖:不同体系结构上的字节顺序(大端或小端)会影响位域的实际布局。

示例

这里有一个更完整的示例,展示了如何使用位域:

#include <stdio.h>

struct Flags {
    unsigned int flag1 : 1;
    unsigned int flag2 : 1;
    unsigned int : 6; // 匿名位域,用于填充或对齐
    unsigned int flag3 : 1;
};

int main() {
    struct Flags flags;
    flags.flag1 = 1;
    flags.flag2 = 0;
    flags.flag3 = 1;

    printf("flag1: %d\n", flags.flag1);
    printf("flag2: %d\n", flags.flag2);
    printf("flag3: %d\n", flags.flag3);

    return 0;
}

这个程序定义了一个Flags结构体,其中包含了三个标志位,每个都只占用了1位。注意,我们还插入了一个6位的匿名位域,用于确保后续的位域从一个新的字节开始(假设编译器按照8位边界对齐)。

请记住,尽管位域提供了一种节省空间的方法,但在编写代码时应该考虑到它的可移植性和潜在的复杂性。如果你不需要精确控制位级别的数据,通常最好使用普通的结构体成员。


【嵌入式C语言】 https://www.bilibili.com/video/BV1RW411G7cr/?p=44&share_source=copy_web&vd_source=8af85e60c2df9af1f0fd23935753a933

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2266582.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HarmonyOS NEXT 实战之元服务:静态案例效果(二)

背景&#xff1a; 前几篇学习了元服务&#xff0c;后面几期就让我们开发简单的元服务吧&#xff0c;里面丰富的内容大家自己加&#xff0c;本期案例 仅供参考 先上本期效果图 &#xff0c;里面图片自行替换 效果图代码案例如下&#xff1a; Index里面实现 import { authent…

Android Studio超级详细讲解下载、安装配置教程(建议收藏)

博主介绍&#xff1a;✌专注于前后端、机器学习、人工智能应用领域开发的优质创作者、秉着互联网精神开源贡献精神&#xff0c;答疑解惑、坚持优质作品共享。本人是掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战&#xff0c;深受全网粉丝喜爱与支持✌有…

从安全角度看 SEH 和 VEH

从安全角度看 SEH 和 VEH 异常处理程序是处理程序中不可预见的错误的基本方法之一 https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/exceptions/ SEH——结构化异常处理程序 就其工作方式而言&#xff0c;异常处理程序与其他处理程序相比相当基础&#xff0…

运行Zr.Admin项目(前端)

1.确认环境信息 我这里装的是node16.17版本的 官网16版本的最新为v16.20.2&#xff0c;下载链接https://nodejs.org/dist/v16.20.2/node-v16.20.2-x64.msi 2.去掉ssl 进入到Zr.Admin项目根目录&#xff0c;进入到ZR.vue 打开package.json 文件&#xff0c;删除启动命令配置中…

uniapp登录

第一步整登录 先整个appid APPID和APPSecret https://developers.weixin.qq.com/community/develop/article/doc/000ca4601b8f70e379febac985b413 一个账号只能整一个小程序 正确流程 调用uni.login https://juejin.cn/post/7126553599445827621 https://www.jb51.net/a…

esp32学习:用虫洞ESP32S3-EYE开发板快速实现USB摄像头(UVC免驱)

直接上干货&#xff1a;实现一个USB摄像头&#xff0c;免驱UVC设备。 硬件准备&#xff1a; 乐官方推荐的Cam开发板就是乐鑫带摄像头OV2604的esp32-s3-eye&#xff0c;我们虫洞esp32-s3-eye完全兼容这个板子哦&#xff0c;虫洞ESP32-S3-EYE 人脸识别 esp-cam升级 OpenCV LVGL …

CMake 构建项目并整理头文件和库文件

本文将介绍如何使用 CMake 构建项目、编译生成库文件&#xff0c;并将头文件和库文件整理到统一的目录中以便在其他项目中使用。 1. 项目结构 假设我们正在构建一个名为 rttr 的开源库&#xff0c;初始的项目结构如下&#xff1a; D:\WorkCode\Demo\rttr-master\|- src\ …

磁盘结构、访问时间、调度算法

目录 一、什么是磁盘&#xff1f; 二、磁盘分类 1、从磁头分 2、通过盘面分 三、一次磁盘读/写的时间 四、磁盘调度算法 1、先来先到服务算法FCFS 2、最短寻找时间优先SSTF 3、扫描算法&#xff08;SCAN&#xff09; 4、LOOk算法 5、循环扫描算法&#xff08;C-SCAN…

重生之我在异世界学编程之C语言:深入预处理篇(上)

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 本文目录 引言正文一、预处理的作用与流程&#xf…

Github——网页版上传文件夹

第一步&#xff1a;创建一个新的仓库或进入已存在的仓库页面 第二步&#xff1a;点进对应的文件夹下&#xff0c;然后 点击 “Upload files” 第三步&#xff1a;将文件夹拖拽到上传区域 打开资源管理器&#xff0c;将要上传的文件夹从计算机中拖拽到上传区域。 注意&#xf…

LeetCode - Google 校招100题 第6天 回溯法(Backtracking) (8题)

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/144743505 LeetCode 合计最常见的 112 题: 校招100题 第1天 链表(List) (19题)校招100题 第2天 树(Tree) (21题)校招100题 第3天 动态规划(DP) (20题)

flask后端开发(2):URL与视图

目录 URL定义request获取请求参数 gitcode地址&#xff1a; https://gitcode.com/qq_43920838/flask_project.git URL定义 from flask import FlaskappFlask(__name__)app.route(/) def hello_world():return Hello World!app.route(/profile) def profile():return 我是个人…

<数据集>风力发电机损伤识别数据集<目标检测>

数据集下载链接 &#xff1c;数据集&#xff1e;风力发电机损伤识别数据集&#xff1c;目标检测&#xff1e;https://download.csdn.net/download/qq_53332949/90187097数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;2527张 标注数量(xml文件个数)&#xff1a;252…

【工具推荐】MobaXterm远程终端管理工具最全攻略,涉及下载、安装、字体配置、中文汉化版、中文显示乱码和中文输入乱码、adb tab无效无法补全、Telnet/ssh使用说明、使用技巧等保姆级教程

MobaXterm远程终端管理工具史上最全攻略&#xff0c;涉及下载、安装、字体等配置、解决中文乱码、Telnet/ssh/Serial使用教程、高级功能使用技巧等。MobaXterm 是一个增强型的 Windows 终端。其为 Windows 桌面提供所有重要的远程网络终端工具&#xff08;如 SSH、X11、RDP、VN…

19、鸿蒙学习——配置HDC命令 环境变量

一、下载Command Line Tools 可参考上篇《鸿蒙学习——配置OHPM、hvigor环境变量》 二、配置hdc环境变量 hdc命令行工具用于HarmonyOS应用/元服务调试所需的工具&#xff0c;该工具存放在命令行工具自带的sdk下的toolchains目录中。为方便使用hdc命令行工具&#xff0c;请将…

Go语言及MongoDB数据库安装配置详解!

Go语言安装 首先讲一下go语言的安装&#xff0c;这部分可直接从官网下载&#xff0c;基本上一键配置的&#xff1a; 官网地址&#xff1a;All releases - The Go Programming Language 选择自己对应系统的安装包&#xff0c;这里官网提供了5种不同的包可自行下载 之后便是默认…

Linux配置ODBC连接Mysql

1、安装mysql 2、安装unixodbc odbcinst -j 查询unixodbc版本以及配置文件路径 3、安装mysql-connector-odbc ####下载 wget https://cdn.mysql.com/archives/mysql-connector-odbc-9.0/mysql-connector-odbc-9.0.0-1.el7.x86_64.rpm ####安装 rpm -ivh mysql-connector-od…

芯片Tapeout power signoff 之IR Drop Redhawk Ploc文件格式及其意义

数字IC后端工程师在芯片流程最后阶段都会使用redhawk或voltus进行设计的IR Drop功耗signoff分析。必须确保静态&#xff0c;动态ir drop都符合signoff标准。 在做redhawk ir drop分析前&#xff0c;我们需要提供一个redhawk ploc供电点坐标。 数字IC设计后端实现前期预防IR D…

数据仓库工具箱—读书笔记02(Kimball维度建模技术概述03、维度表技术基础)

Kimball维度建模技术概述 记录一下读《数据仓库工具箱》时的思考&#xff0c;摘录一些书中关于维度建模比较重要的思想与大家分享&#x1f923;&#x1f923;&#x1f923; 第二章前言部分作者提到&#xff1a;技术的介绍应该通过涵盖各种行业的熟悉的用例展开&#xff08;赞同…

设置首选网络类型以及调用Android框架层的隐藏API

在Android SDK中提供的framework.jar是阉割版本的&#xff0c;比如有些类标记为hide&#xff0c;这些类不会被打包到这个jar中&#xff0c;而有些只是类中的某个方法或或属性被标记为hide&#xff0c;则这些类或属性会被打包到framework.jar&#xff0c;但是我们无法调用&#…