C语言入门-结构体6

news2024/11/20 23:34:26

结构体入门

编写程序,用struct分别表示平面上的点和平面上的矩形。
#include <stdio.h>

int main() {
struct point {int x; int y;};
struct point p1 = {1, 2};
printf(“(%d, %d)\n”, p1.x, p1.y);

struct rectangle {
    struct point p1;
    struct point p2;
};

struct rectangle square = {{1, 2}, {3, 4}};
printf("p1 = (%d, %d), p2 = (%d, %d)\n", square.p1.x, square.p1.y, square.p2.x, square.p2.y);

}

结构体和函数

在C语言中,结构体和函数可以结合使用,以便更好地组织和管理数据和操作。

结构体与函数的结合使用

传递结构体给函数

你可以将结构体作为参数传递给函数,有两种方式:传值和传引用(通过指针)。

传值
#include <stdio.h>

struct Point {
    int x;
    int y;
};

void printPoint(struct Point p) {
    printf("Point: (%d, %d)\n", p.x, p.y);
}

int main() {
    struct Point p1 = {10, 20};
    printPoint(p1);
    return 0;
}

在这种方式中,结构体的值被复制到函数参数中,函数内的修改不会影响原始结构体。

传引用(指针)
#include <stdio.h>

struct Point {
    int x;
    int y;
};

void movePoint(struct Point *p, int dx, int dy) {
    p->x += dx;
    p->y += dy;
}

int main() {
    struct Point p1 = {10, 20};
    movePoint(&p1, 5, -5);
    printf("Moved Point: (%d, %d)\n", p1.x, p1.y);
    return 0;
}

在这种方式中,传递的是结构体的指针,函数内的修改会影响原始结构体。

使用结构体和函数的完整示例

#include <stdio.h>

// 定义结构体
struct Rectangle {
    int width;
    int height;
};

// 计算面积的函数
int area(struct Rectangle r) {
    return r.width * r.height;
}

// 计算周长的函数
int perimeter(struct Rectangle *r) {
    return 2 * (r->width + r->height);
}

int main() {
    struct Rectangle rect = {10, 20};

    printf("Area: %d\n", area(rect));
    printf("Perimeter: %d\n", perimeter(&rect));

    return 0;
}

结构体数组

考虑编写一个程序来统计每个 C 关键字的出现次数。我们需要一个字符字符串数组来存储关键字的名称,并且需要一个整数数组来存储计数。一个可能的方案是使用两个并行数组,keywordkeycount,如下所示:

char *keyword[NKEYS];
int keycount[NKEYS];

但是,数组是并行的这一事实暗示了一种不同的组织方式,即结构体数组。每个关键字是一对:

char *word;
int count;

并且这是一个包含若干对的数组。结构体声明如下:

struct key {
    char *word;
    int count;
} keytab[NKEYS];

这段代码声明了一个结构体类型 key,定义了一个 keytab 结构体数组,并为这些结构体分配了存储空间。数组的每个元素都是一个结构体。这也可以写成:

struct key {
    char *word;
    int count;
};
struct key keytab[NKEYS];

由于结构体 keytab 包含了一组固定的名称,所以将其设为外部变量并在定义时初始化一次是最简单的做法。结构体的初始化类似于之前的初始化 - 定义之后紧跟着用大括号括起来的初始化列表:

struct key {
    char *word;
    int count;
} keytab[] = {
    "auto", 0,
    "break", 0,
    "case", 0,
    "char", 0,
    "const", 0,
    "continue", 0,
    "default", 0,
    /* ... */
    "unsigned", 0,
    "void", 0,
    "volatile", 0,
    "while", 0
};

实现

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>

#define MAXWORD 100
#define NKEYS (sizeof(keytab) / sizeof(keytab[0])) // Calculate the number of keywords

struct key {
    char *word;
    int count;
} keytab[] = {
    {"auto", 0}, {"break", 0}, {"case", 0}, {"char", 0}, {"const", 0},
    {"continue", 0}, {"default", 0}, {"unsigned", 0}, {"void", 0}, 
    {"volatile", 0}, {"while", 0}
};

int getword(char *, int);
int binsearch(char *, struct key *, int);

/* count C keywords */
int main() {
    int n;
    char word[MAXWORD];
    
    // Read words until end of file
    while (getword(word, MAXWORD) > 0) {
        if (isalpha(word[0])) {
            // If the word is a keyword, increment its count
            if ((n = binsearch(word, keytab, NKEYS)) >= 0) {
                keytab[n].count++;
            }
        }
    }
    
    // Print all keywords that were found
    for (n = 0; n < NKEYS; n++) {
        if (keytab[n].count > 0) {
            printf("%4d %s\n", keytab[n].count, keytab[n].word);
        }
    }
    return 0;
}

/* binsearch: find word in tab[0]...tab[n-1] */
int binsearch(char *word, struct key tab[], int n) {
    int cond;
    int low, high, mid;

    low = 0;
    high = n - 1;
    
    // Perform binary search
    while (low <= high) {
        mid = (low + high) / 2;
        if ((cond = strcmp(word, tab[mid].word)) < 0) {
            high = mid - 1;
        } else if (cond > 0) {
            low = mid + 1;
        } else {
            return mid;
        }
    }
    return -1;
}

int getword(char *s, int lim) {
    int i;

    for (i = 0; i < lim && (*s = getchar()) != EOF && !isblank(*s); s++, i++)
        ;
    *s = '\0';
    return i;
}

结构体指针

用指针的方式重写上面的程序。

#include <stdio.h>   // 标准输入输出头文件
#include <ctype.h>   // 字符处理函数头文件
#include <string.h>  // 字符串处理函数头文件

#define MAXWORD 100  // 定义最大单词长度为100

// 函数声明
int getword(char *, int);
struct key *binsearch(char *, struct key *, int);

// 主函数,统计C语言关键字,指针版本
int main() {
    char word[MAXWORD];  // 用于存储单词的字符数组
    struct key *p;       // 指向关键字表的指针

    // 获取单词并检查是否为字母开头
    while (getword(word, MAXWORD) != EOF) {
        if (isalpha(word[0])) {
            // 在关键字表中查找单词
            if ((p = binsearch(word, keytab, NKEYS)) != NULL) {
                p->count++;  // 如果找到则计数加1
            }
        }
    }

    // 输出关键字出现的次数
    for (p = keytab; p < keytab + NKEYS; p++) {
        if (p->count > 0) {
            printf("%4d %s\n", p->count, p->word);
        }
    }

    return 0;  // 返回0表示程序正常结束
}

/* 
 * binsearch: 在tab[0]...tab[n-1]中查找单词 
 */
struct key *binsearch(char *word, struct key *tab, int n) {
    int cond;  // 比较结果
    struct key *low = &tab[0];  // 指向表首的指针
    struct key *high = &tab[n];  // 指向表尾后一个元素的指针
    struct key *mid;  // 中间元素的指针

    // 二分查找算法
    while (low < high) {
        mid = low + (high - low) / 2;  // 计算中间位置
        if ((cond = strcmp(word, mid->word)) < 0) {
            high = mid;  // 在低半区查找
        } else if (cond > 0) {
            low = mid + 1;  // 在高半区查找
        } else {
            return mid;  // 找到单词,返回指针
        }
    }

    return NULL;  // 未找到单词,返回NULL
}

自引用结构体

假设我们想处理一个更一般的问题,即统计某些输入中所有单词的出现次数。由于单词列表事先未知,我们不能方便地对其进行排序并使用二分查找。然而,我们也不能对每个到来的单词进行线性搜索,以查看它是否已经出现过;这样程序的运行时间会太长。(更准确地说,其运行时间可能会随着输入单词数量的增加而成二次方增长。)我们如何组织数据以有效地处理任意单词列表呢?

一种解决方案是始终保持已见单词的集合按顺序排列,将每个单词按其到达的顺序放入其适当位置。但这不应通过在线性数组中移动单词来完成,这同样会耗费太多时间。相反,我们将使用一种称为二叉树的数据结构。树包含每个不同单词的一个“节点”;每个节点包含:

  • 指向单词文本的指针,
  • 出现次数的计数,
  • 指向左子节点的指针,
  • 指向右子节点的指针。

任何节点都不能有超过两个子节点;它可能只有零个或一个子节点。节点的维护方式是,使得在任何节点处,左子树仅包含按字典顺序小于该节点单词的单词,而右子树仅包含按字典顺序大于该节点单词的单词。以下是通过逐个插入遇到的单词构建的句子“now is the time for all good men to come to the aid of their party”的树结构。

具体解释

在这个例子中,二叉树的数据结构被用来保持单词的顺序并高效地统计它们的出现次数。以下是一些关键点:

  1. 插入操作

    • 每次插入一个新单词时,比较新单词与当前节点的单词。
    • 如果新单词小于当前节点的单词,则移至左子树继续比较。
    • 如果新单词大于当前节点的单词,则移至右子树继续比较。
    • 如果新单词等于当前节点的单词,则增加该节点的计数。
  2. 二叉树的优势

    • 保持动态排序:新单词可以快速插入到适当位置,而不需要移动其他元素。
    • 查找效率:二叉树的结构使得查找操作在平均情况下可以在对数时间内完成(O(log n)),大大优于线性查找的O(n)时间复杂度。

举例

对于句子“now is the time for all good men to come to the aid of their party”,我们依次插入每个单词,构建的二叉树如下:

  • 第一个单词“now”成为根节点。
  • 第二个单词“is”小于“now”,插入左子树。
  • 第三个单词“the”大于“now”,插入右子树。
  • 依此类推,构建整个树结构。

二叉树示意图

在这里插入图片描述

这个示意图展示了单词按插入顺序构建的二叉树结构,每个节点按上述规则插入。

实现

#include <stdio.h>
#include <ctype.h>
#include <string.h>

#define MAXWORD 100
struct tnode *addtree(struct tnode *, char *);
void treeprint(struct tnode *);
int getword(char *, int);
// struct tnode *talloc(void);

struct tnode {
    char *word;
    int count;
    struct tnode *left;
    struct tnode *right;
};

// 函数声明
struct tnode *addtree(struct tnode *, char *);
void treeprint(struct tnode *);
int getword(char *, int);

/* 
 * 主函数,统计单词频率 
 */
int main() {
    struct tnode *root;  // 根节点指针
    char word[MAXWORD];  // 存储单词的字符数组

    root = NULL;  // 初始化根节点为空

    // 获取单词并检查是否为字母开头
    while (getword(word, MAXWORD) > 0) {
        if (isalpha(word[0])) {
            root = addtree(root, word);  // 添加单词到二叉树
        }
    }

    printf("%s\n", root->word);
    treeprint(root);  // 打印二叉树中的单词及其频率
    return 0;  // 返回0表示程序正常结束
}

int getword(char *s, int lim) {
    int i;

    for (i = 0; i < lim && (*s = getchar()) != EOF && !isblank(*s); s++, i++)
        ;
    *s = '\0';
    return i;
}

#include <stdlib.h> // 标准库,包含 malloc 和相关函数

// 函数声明
struct tnode *talloc(void); 
char *strdup(char *);    

/* 
 * addtree: 在节点 p 及其以下位置添加一个包含单词 w 的节点 
 */
struct tnode *addtree(struct tnode *p, char *w) {
    int cond;

    if (p == NULL) {  // 如果节点为空,说明新单词到达
        p = talloc();  // 创建一个新节点
        p->word = strdup(w);  // 复制单词到新节点
        p->count = 1;  // 初始化计数为 1
        p->left = p->right = NULL;  // 左右子节点设为空
    } else if ((cond = strcmp(w, p->word)) == 0) {
        p->count++;  // 如果单词已存在,计数递增
    } else if (cond < 0) {
        p->left = addtree(p->left, w);  // 如果小于当前节点单词,递归添加到左子树
    } else {
        p->right = addtree(p->right, w);  // 如果大于当前节点单词,递归添加到右子树
    }
    return p;  // 返回节点指针
}

/* 
 * talloc: 分配一个 tnode 节点的空间 
 */
struct tnode *talloc(void) {
    return (struct tnode *) malloc(sizeof(struct tnode));  // 分配内存并转换为 tnode 指针
}

/* 
 * strdup: 复制字符串 s 
 */
char *strdup(char *s) {
    char *p;
    p = (char *) malloc(strlen(s) + 1);  // 为字符串分配空间,+1 用于 '\0' 终止符
    if (p != NULL)
        strcpy(p, s);  // 复制字符串到分配的空间
    return p;  // 返回指向新字符串的指针
}

/* 
 * treeprint: 以中序遍历方式打印树 p 
 */
void treeprint(struct tnode *p) {
    if (p != NULL) {
        treeprint(p->left);  // 打印左子树
        printf("%4d %s\n", p->count, p->word);  // 打印当前节点
        treeprint(p->right); // 打印右子树
    }
}

运行

now is the time for all good men to come to the aid of their party
   1 aid
   1 all
   1 come
   1 for
   1 good
   1 is
   1 men
   1 now
   1 of
   1 party

   2 the
   1 their
   1 time
   2 to

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

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

相关文章

vue的学习--day3

1、尝试使用json文件模拟增删改查 json server:准备一份自己的数据&#xff08;这里我用的是老师给的&#xff09;。 转到d盘&#xff0c;然后打开json文件&#xff1a; 下面模拟增删改查&#xff1a; 借助工具postman或apifox或apipost&#xff1a; 这里我下载了apifox&…

【Abaqus Case】2D弹塑性接触分析

原文链接&#xff1a;https://www.cnblogs.com/aksoam/p/18283296 更多精彩&#xff0c;关注博客园主页&#xff0c;不断学习&#xff01;不断进步&#xff01; 我的主页 csdn很少看私信&#xff0c;有事请b站私信 博客园主页-发文字笔记-常用 有限元鹰的主页 内容&#xf…

VCS+Vivado联合仿真BUG

场景&#xff1a; 在vcsvivado联合仿真过程中&#xff0c;对vivado导出的shell脚本修改&#xff0c;修改某些source文件路径&#xff0c;vcs编译时会报Permission Denied。 问题描述 对shell脚本修改如下&#xff1a; 修改仅为注释掉某一行&#xff0c;下面变为source文件新…

Golang | Leetcode Golang题解之第214题最短回文串

题目&#xff1a; 题解&#xff1a; func shortestPalindrome(s string) string {n : len(s)fail : make([]int, n)for i : 0; i < n; i {fail[i] -1}for i : 1; i < n; i {j : fail[i - 1]for j ! -1 && s[j 1] ! s[i] {j fail[j]}if s[j 1] s[i] {fail[i…

sql查询 只取某字段重复数据中的一条

一. 前提条件 某表的主键由两个字段A、B构成&#xff08;或者更多&#xff09;&#xff0c;任何其中一个字段都可能具有重复的数据。 需要只取字段A所有重复数据中的一条构成查询结果&#xff0c;也就是字段A取到所有的可能取值且无重复。 二. 方法一&#xff08;where ... …

填报高考志愿,怎样正确地选择大学专业?

大学专业的选择&#xff0c;会关系到未来几年甚至一辈子的发展方向。这也是为什么很多人结束高考之后就开始愁眉苦脸&#xff0c;因为他们不知道应该如何选择大学专业&#xff0c;生怕一个错误的决定会影响自己一生。 毋庸置疑&#xff0c;在面对这种选择的时候&#xff0c;我…

3-2 梯度与反向传播

3-2 梯度与反向传播 主目录点这里 梯度的含义 可以看到红色区域的变化率较大&#xff0c;梯度较大&#xff1b;绿色区域的变化率较小&#xff0c;梯度较小。 在二维情况下&#xff0c;梯度向量的方向指向函数增长最快的方向&#xff0c;而其大小表示增长的速率。 梯度的计算 …

Java--封装详解

1.该漏的漏&#xff0c;该藏的藏 我们程序设计要追求“高内聚&#xff0c;低耦合”。高内聚就是类的内部数据操作细节自己完成&#xff0c;不允许外部干涉&#xff1b;低耦合&#xff1a;仅暴露少量的方法给外部使用 2.封装&#xff08;数据的隐藏&#xff09;私有&#xff1a…

运维锅总详解计算机缓存

本文从OSI模型中的每一层缓存介绍、常见开源中间件缓存举例、TCP/IP协议栈中的缓存机制、操作系统中的缓存、访问缓存数据的时间范围统计等方面对计算机中的缓存进行详细介绍。希望对您有所帮助&#xff01; 一、OSI模型中的每一层缓存 1. 物理层&#xff08;Physical Layer&…

微信小程序消息通知(一次订阅)

在微信公众平台配置通知模版 通过wx.login获取code发送给后端 let that this // 登陆codewx.login({success: function (res) {if (res.code) {// 发送code到后端换取openid和session_keythat.setData({openCode: res.code})console.log(that.data.openCode, openCode);// 调…

Simulink中示波器连续运行的方法

1.在Simulink中,经常要使用到示波器,默认示波器是定时运行的,只能观察到一小部分运行的波形;实际调试过程中,经常要连续运行,因此,需要设置示波器为连续运行模式,下面将介绍示波器连续运行的方法。 打开Simulink仿真软件,找到仿真设置按钮,点击设置: 2.将其停止时间…

前端面试题(CSS篇二)

一、请解释一下 CSS3 的 Flex box&#xff08;弹性盒布局模型&#xff09;&#xff0c;以及适用场景 相关知识点: Flex是FlexibleBox的缩写&#xff0c;意为"弹性布局"&#xff0c;用来为盒状模型提供最大的灵活性。 任何一个容器都可以指定为Flex布局。行内元素也可…

AcWing 1511:笨鸟

【题目来源】https://www.acwing.com/problem/content/1513/【题目描述】 小王特别喜欢玩 flappy birds&#xff0c;但是他比较菜&#xff0c;所以向大家寻求帮助&#xff0c;游戏规则大家都懂&#xff0c;每一秒如果点击屏幕&#xff0c;小鸟会从 (x,y) 飞到 (x1,y1)&#xff…

3C电子制造行业MES系统,提高企业生产效率

随着科技的不断进步&#xff0c;3C电子制造行业正迎来传统工厂向数字化工厂转型的阶段。在这场变革中&#xff0c;MES系统发挥着重要的作用&#xff0c;成为了企业变革的“智慧大脑”&#xff0c;引领着生产流程的优化和升级。 那么&#xff0c;MES系统究竟有哪些功能&#xf…

新纪录将圆周率计算到了小数点后202万亿位 用了28块61.44TB SSD

独立存储研究机构StorageReview与闪存领导厂商Solidigm联合宣布&#xff0c;将圆周率计算到了小数点后202万亿位(确切地说是202,112,290,000,000位数)&#xff0c;相比今年3月初的原有记录105万亿位几乎翻了一番。 本次计算采用了戴尔PowerEdge R760服务器&#xff0c;配置Int…

Jenkins 任务中的 java.lang.InterruptedException 异常解析与解决

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

[激光原理与应用-98]:南京科耐激光-激光焊接-焊中检测-智能制程监测系统IPM介绍 - 2 - 什么是激光器焊接

目录 一、什么是激光焊接 1.1 概述 1.2 激光焊接的优点 二、激光焊接的应用 2.1 哪些场合必须使用激光焊接 1. 汽车制造业 2. 航空航天领域 3. 电子行业&#xff1a;消费类电子3C 4. 医疗器械制造 5. 新能源锂电池行业 6. 其他领域 三、激光焊接的分类 3.1 按焊接…

scratch猫咪追星星 2024年6月中国电子学会 图形化编程 scratch编程等级考试二级真题和答案解析

目录 scratch猫咪追星星 一、题目要求 1、准备工作 2、功能实现 二、案例分析 1、角色分析 2、背景分析 3、前期准备 三、解题思路 1、思路分析 2、详细过程 四、程序编写 五、考点分析 六、推荐资料 1、入门基础 2、蓝桥杯比赛 3、考级资料 4、视频课程 5、…

入门篇:构建第一个鸿蒙ArkTS应用(Stage模型)

为确保运行效果&#xff0c;本文以使用DevEco Studio NEXT Developer Beta1版本为例&#xff0c;点击此处获取下载链接。 创建ArkTS工程 若首次打开DevEco Studio&#xff0c;请点击Create Project创建工程。如果已经打开了一个工程&#xff0c;请在菜单栏选择File > New &…

月之暗面kimi引擎的底层推理系统,如何引领智能助手技术的未来

在人工智能领域,一个令人瞩目的超级明星悄然升起——那就是“月之暗面kimi”引擎。这个引擎不仅拥有强大的底层推理系统方案,还搭载了独特的调度技术,让Kimi智能助手成为了众人关注的焦点。今天,我们将深入探索这一技术的奥秘,看看它是如何改变智能助手的未来。 首先,…