当前位置: 首页 > news >正文

C和指针——struct结构

struct

C 语言没有其他语言的对象(object)和类(class)的概念,struct 结构很大程度上提供了对象和类的功能。

下面是struct自定义数据类型的一个例子。

struct tag { 
    member-list
    member-list 
    member-list  
    ...
} variable-list;

声明了数据类型car和该类型的变量car。

struct car
{
    char *name;
    float price;
    int speed;
} mycar;
struct car myca = {"大众", 178.9, 100};
mycar.name = "本田";

如果将 struct 变量传入函数,函数内部得到的是一个原始值的副本。

#include <stdio.h>

struct turtle {
  char* name;
  char* species;
  int age;
};

void happy(struct turtle t) {
  t.age = t.age + 1;
}

int main() {
  struct turtle myTurtle = {"MyTurtle", "sea turtle", 99};
  happy(myTurtle);
  printf("Age is %i\n", myTurtle.age); // 输出 99
  return 0;
}

上面示例中,函数happy()传入的是一个 struct 变量myTurtle,函数内部有一个自增操作。但是,执行完happy()以后,函数外部的age属性值根本没变。原因就是函数内部得到的是 struct 变量的副本,改变副本影响不到函数外部的原始数据。

指针变量也可以指向struct结构。

struct book {
  char title[500];
  char author[100];
  float value;
}* b1;

上面示例中,变量b1是一个指针,指向的数据是struct book类型的实例。

为了使用指向该结构的指针访问结构的成员,必须使用 -> 运算符,如下所示:

b1->title;//9-2.c
struct Books
{
    char title[50];
    char author[50];
    char subject[100];
    int book_id;
};

// 函数声明
void printBook(struct Books *books);

int main()
{
    struct Books Book1;
    struct Books Book2;

    /**
     * Book1 描述 
     */
    strcpy(Book1.title, "C Programming");
    strcpy(Book1.author, "Nuha Ali");
    strcpy(Book1.subject, "C Programming Tutorial");
    Book1.book_id = 6495407;

    /* Book2 详述 */
    strcpy(Book2.title, "Telecom Billing");
    strcpy(Book2.author, "Zara Ali");
    strcpy(Book2.subject, "Telecom Billing Tutorial");
    Book2.book_id = 6495700;

    /* 通过传 Book1 的地址来输出 Book1 信息 */
    printBook(&Book1);

    /* 通过传 Book2 的地址来输出 Book2 信息 */
    printBook(&Book2);

    return 0;
}


void printBook(struct Books *book)
{
    printf("Book title : %s\n", book->title);
    printf("Book author : %s\n", book->author);
    printf("Book subject : %s\n", book->subject);
    printf("Book before book_id : %d\n", book->book_id);

    (*book).book_id = (*book).book_id + 1;
    printf("Book agter book_id : %d\n", book->book_id);
}

struct 结构也可以作为数组成员。下面示例声明了一个有1000个成员的数组books,每个成员都是自定义类型book的实例。

struct Books
{
    char title[50];
    char author[50];
    char subject[100];
    int book_id;
};

int main(int argc, char const *argv[])
{
    struct Books books[1000];

    books[0].book_id = 22;
    books[0].book_id = 7;
    return 0;
}

struct的嵌套

struct 结构的成员可以是另一个 struct 结构。

struct species {
  char* name;
  int kinds;
};

struct fish {
  char* name;
  int age;
  struct species breed;
};

上面示例中,fish的属性breed是另一个 struct 结构species。

// 写法三
struct fish shark = {
  .name="shark",
  .age=9,
  .breed={"Selachimorpha", 500}
};

引用breed属性的内部属性,要使用两次点运算符(shark.breed.name)。

对字符数组属性赋值,要使用strcpy()函数,不能直接赋值,因为直接改掉字符数组名的地址会报错。

strcpy(shark.breed.name), "Harry");

struct 结构内部不仅可以引用其他结构,还可以自我引用,即结构内部引用当前结构。比如,链表结构的节点就可以写成下面这样。

struct node {
  int data;
  struct node* next;
};

上面示例中,node结构的next属性,就是指向另一个node实例的指针。下面,使用这个结构自定义一个数据链表。

// p9-2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    struct node
    {
        int data;
        struct node *next;
    };

    struct node *head;
    // 生成一个三个节点的列表 (11)->(22)->(33)
    head = malloc(sizeof(struct node));

    head->data = 11;
    head->next = malloc(sizeof(struct node));

    head->next->data = 22;
    head->next->next = malloc(sizeof(struct node));

    head->next->next->data = 33;
    head->next->next->next = NULL;

    // 遍历这个列表
    for (struct node *cur = head; cur != NULL; cur = cur->next)
    {
        printf("%d\n", cur->data);
    }
    return 0;
}

实验 考虑下面发这些声明和数据,并debug

#include <stdio.h>

int main(int argc, char const *argv[])
{
    struct NODE
    {
        int a;
        struct NODE *b;
        struct NODE *c;
    };
    // 5个成员的数组nodes
    struct NODE nodes[5] =
        {
            {5, nodes + 3, NULL},
            {15, nodes + 4, nodes + 3},
            {22, NULL, NULL},
            {12, nodes + 1, nodes},
            {18, nodes + 2, nodes + 1},
        };
    struct NODE *np = nodes + 2;
    struct NODE **npp = &nodes[1].b;
    /* nodes 是数组,数组名就是该数组的指针,也是该数组第一个元素的指针 */
    // 输出该数组的地址
    printf("nodes的地址是 %p\n", nodes); // 0x7ffeefbff460
    printf("nodes的地址是 %p \n", &nodes);
    printf("nodes+2的地址是 %p \n", nodes + 2);
    // printf("%d\n", nodes.a)//错误;指针访问属性 需要使用 ->
    printf("nodes[0] a的值 %d\n", nodes->a);    // 5 通过指针访问
    printf("(*nodes).a)的值 %d\n", (*nodes).a); // (*nodes)获取的是nodes[1]

    printf("nodes[3] a的值 %d\n", nodes[3].a); // 12
    printf("nodes[3].c的值 %p\n", nodes[3].c); //0x7ffeefbff460
    //  访问的是nodes[0]
    printf("nodes[3].c->a的值 %d\n", nodes[3].c->a); // 5
    // printf("%d\n", *nodes); 使用* 操作符对指针执行间接访问,*nodes的右值是nodes的整个结构
    // printf("%d\n", *nodes.a); //错误
    printf("nodes[4]的值地址 %p \n", &nodes[4]);
    printf("nodes[3].b->b的值 %p \n", nodes[3].b->b); //  nodes[3].b 获取的是 nodes + 1 即 nodes[1]的指针,然后nodes[1]->b ,就是nodes[4]的指针

    // [] () . ->  是一级运算 从左往右边 结合,  * &是二级运算
    printf("*nodes[3].b->b的值 %p \n", *nodes[3].b->b);  // {18, nodes + 2, nodes + 1},//最后运算*,由前边可以知道 nodes[3].b->b就是nodes[4]的指针,然后*,得到nodes[4]。看下一提题的验证
    printf("nodes[3] a 的值 %d \n", (*nodes[3].b->b).a); // 18

    printf("&nodes[3].a 的值%p \n", &nodes[3].a); // 数子12 第物理地址
    // printf("&nåodes[3].c %p \n", &nodes[3].c);
    printf("&nodes[3].c的值%p \n", &nodes[3].c->a); //数字15 的物理地址
    printf("&nodes->a 的值%p \n", &nodes->a);       //数字5 的物理地址

    printf("nodes+2的地址是 %p \n", nodes + 2);
    printf("np的值%p \n", np);       // np为nodes[2]的地址
    printf("np->a的值%d \n", np->a); // 12
    // printf("np->c->c->a的值%d \n", np->c->c->a); // 12

    printf("npp的值%p \n", npp); // np为nodes[2]的地址
    // printf("npp->a的值%p \n", npp->a); // 非法
    printf("*npp的值%p \n", *npp);
    printf("**npp的值%p \n", **npp);

    return 0;
}

相关文章:

  • 怎么转换音频格式?建议收藏这几个方法
  • 小红书如何养号?小红书如何精准引流?
  • 音频裁剪软件有哪些?来看看这几个实用软件
  • 极智开发 | linux 下 ssh 或 scp 免密连接配置方法
  • 离线数仓搭建_02_服务器配置与数据生产
  • 猿创征文|我的四个月Java学习成长之路——从基础到框架再到项目
  • 【毕业设计】试卷自动批改系统 - opencv python 机器视觉
  • 神经网络算法有哪些模型,神经网络模型应用实例
  • 微信公众号题库接口系统 内部含有接口授权使用
  • (附源码)springboot美食分享系统 毕业设计 612231
  • 视频产生的本质及色彩空间:RGB 和 YUV
  • 002-JVM 常用命令
  • 数据分析报告常见步骤
  • “零”成本即可搭建OA系统,终于知道低代码平台为什么那么火
  • 【芯片前端】延迟一拍出数的握手型ram结构的一次探索
  • 【笔记】你不知道的JS读书笔记——Promise
  • 【附node操作实例】redis简明入门系列—字符串类型
  • 08.Android之View事件问题
  • AzureCon上微软宣布了哪些容器相关的重磅消息
  • Dubbo 整合 Pinpoint 做分布式服务请求跟踪
  • Laravel Telescope:优雅的应用调试工具
  • Object.assign方法不能实现深复制
  • PHP 使用 Swoole - TaskWorker 实现异步操作 Mysql
  • swift基础之_对象 实例方法 对象方法。
  • 从零搭建Koa2 Server
  • 分布式事物理论与实践
  • 区块链将重新定义世界
  • 移动端 h5开发相关内容总结(三)
  • 用jquery写贪吃蛇
  • 仓管云——企业云erp功能有哪些?
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • ​LeetCode解法汇总2670. 找出不同元素数目差数组
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • #Linux(帮助手册)
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • (2)nginx 安装、启停
  • (MATLAB)第五章-矩阵运算
  • (笔记)Kotlin——Android封装ViewBinding之二 优化
  • (二)springcloud实战之config配置中心
  • (附源码)spring boot车辆管理系统 毕业设计 031034
  • (附源码)spring boot校园拼车微信小程序 毕业设计 091617
  • (转)linux自定义开机启动服务和chkconfig使用方法
  • (转)程序员技术练级攻略
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • .netcore 获取appsettings
  • .NET开源项目介绍及资源推荐:数据持久层
  • .net利用SQLBulkCopy进行数据库之间的大批量数据传递
  • .NET中的Event与Delegates,从Publisher到Subscriber的衔接!
  • .Net组件程序设计之线程、并发管理(一)
  • .project文件
  • @FeignClient注解,fallback和fallbackFactory
  • @hook扩展分析
  • @WebService和@WebMethod注解的用法
  • [2016.7 day.5] T2