DAY20 结构和其他数据形式(上)【一万六千字超详细】

文章目录

  • 前言
  • 14.1 示例问题:创建图书目录
  • 14.2建立结构声明
  • 14.3定义结构变量
      • 14.3.1 初始化结构
      • 14.3.2 访问结构成员
      • 14.3.2 结构的初始化器
  • 14.4 结构数组
    • 14.4.1 声明结构数组
    • 14.4.2 标识结构数组的成员
  • 14.3 嵌套结构
  • 14.6 指向结构的指针
    • 14.6.1 声明和初始化结构指针
    • 14.6.2 用指针访问成员
  • 14.7 向函数传递结构的信息
    • 14.7.1传递结构成员
    • 14.7.2 传递结构的地址
    • 14.7.3 传递结构
    • 14.7.4 其他结构特征
  • 如果您发现文章有错误请与我留言,感谢


前言


14.1 示例问题:创建图书目录

创建一个图书目录,包含一本书的书名,作者和价格:
如果能把图书信息装在一个数组内,其中每个元素包含一本书的相关信息。

所以我们需要一种即能包含字符串又能包含数字的数据形式,而且还要保持各信息的独立。

利用struct变量创建一个图书目录,包含一本书的书名,作者和价格:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>	
#include <string.h>
#define MAXTITLE 41
#define MAXAUTH 31
char* s_gets(char* st, int n);
struct book {
	//结构模板 标记是book
	char title[MAXTITLE];
	char author[MAXAUTH];
	float value;
};//结构模板结束

int main()
{
	struct book library;//把library声明为一个book类型的变量
	printf("Please enter the book title.\n");
	s_gets(library.title, MAXTITLE);
	printf("Please enter author.\n");
	s_gets(library.author, MAXAUTH);
	printf("Now enter the value.\n");
	scanf("%f", &library.value);
	printf("%s by %s : $%.2f\n", library.title, library.author, library.value);
	printf("%s: \"%s\" ($%.2f)\n", library.author, library.title, library.value);
	printf("Done.\n");
}
char* s_gets(char* st, int n)
{
	char* ret_val;
	char* find;
	ret_val = fgets(st, n, stdin);
	if (ret_val)//ret_val != EOF
	{
		find = strchr(st, '\n');
		if (find)
			*find = '\0';//如果地址不是NULL,就放置一个空字符
		else//如果未找到换行符
			while (getchar() != '\n')//循环读取输入缓冲区中的剩余字符,直到遇到换行符为止。这样可以清空输入缓冲区,防止下一次输入被之前的输入影响。
				continue}
	return ret_val;
}

在这里插入图片描述

详解

这段代码定义了一个函数s_gets,用于从标准输入中获取用户输入的字符串,并将其存储在指定的字符数组中。下面是代码的解释:

char* s_gets(char* st, int n):这是函数的定义,它接受两个参数,一个是指向字符数组的指针st,另一个是整数n,表示最多可以读取的字符数。

char* ret_val; :定义一个指向字符的指针ret_val,用于存储fgets函数的返回值。

char* find; :定义一个指向字符的指针find,用于在输入的字符串中查找换行符。

ret_val = fgets(st, n, stdin); :调用fgets函数从标准输入中读取用户输入的字符串,并将其存储在字符数组st中,最多读取n个字符。函数返回值被赋给ret_val。

if (ret_val):检查fgets函数的返回值,如果返回值不为NULL,表示成功读取了用户输入的字符串。

find = strchr(st, '\n'); :在读取的字符串中查找换行符\n的位置,并将其地址赋给find指针。

if (find):如果找到了换行符。

* find = '\0'; :将换行符替换为字符串结束符\0,这样可以截断字符串,只保存换行符之前的内容。
else:如果未找到换行符。

while (getchar() != '\n'):循环读取输入缓冲区中的剩余字符,直到遇到换行符为止。这样可以清空输入缓冲区,防止下一次输入被之前的输入影响。

continue; :继续下一次循环。

return ret_val; :返回fgets函数的返回值,即读取的字符串或者NULL。

总的来说,这段代码的作用是安全地从标准输入中获取用户输入的字符串,并处理换行符以及输入缓冲区中的剩余字符。

14.2建立结构声明

在这里插入图片描述

在这里插入图片描述

这样该函数就创建一个结构变量dickens,该变量的结构布局book(标记)

14.3定义结构变量

结构有两层含义。一层含义是“结构布局”,刚才已经讨论过了。结构布局告诉编译器如何表示数据,但是它并未让编译器为数据分配空间。下一步是创建一个结构变量,即是结构的另一层含义。程序中创建结构变量的一行是:
struct book library;
编译器执行这行代码便创建了一个结构变量library。编译器使用book模板为该变量分配空间:一个内含MAXTITL个元素的char数组、一个内含MAXAUTL个元素的char数组和一个float类型的变量。这些存储空间都与一个名称library结合在一起

如图:

在这里插入图片描述

在结构变量的声明中,struct book所起的作用相当于一般声明中的int或float。例如,可以定义两个 struct book类型的变量,或者甚至是指向struct book类型结构的指针

struct book doyle, panshin, * ptbook;

结构变量doyle和penshin中都包含title,author,value部分,

在这里插入图片描述

14.3.1 初始化结构

初始化变量和数组如下: int count = 0;
int fibo[7] = {0,1,1,2,3,5,8};
结构变量是否也可以这样初始化?是的,可以。初始化一个结构变量与初始化数组的语法类似:

struct book library = (
"The Pious Pirate and the Devious Damsel",
"Renee Vivotte",
1.95);

简而言之,我们使用在一对花括号中括起来的初始化列表进行初始化,各初始化项用逗号分隔因此, title成员可以被初始化为一个字符串,value成员可以被初始化为一个数字。为了让初始化项与结构中
各成员的关联更加明显,我们让每个成员的初始化项独占一行。这样做只是为了提高代码的可读性

14.3.2 访问结构成员

结构就像一个“超级数组”,它可以储存各种各样的类型。数组可以用下标访问各元素,访问结构中的成员要通过点运算符(.)

例如
library.value即访问library的value部分。可以像使用任何float类型变量那样使用library.value。与此类似,可以像使用字符数组那样使用library.title。

因此,程序中有s_gets(library.title,MAXTITL) ;和 scanf(“%f”, &library.value);这样的代码。

本质上,.title、.author和,value 的作用相当于book结构的下标。

注意,虽然library是一个结构,但是library.value是一个float类型的变量,可以像使用其他float类型变量那样使用它

例如,scanf(“%f”,…)需要一个float类型变量的地址,而&library.float正好符合要求。.比&的优先级高因此这个表达式和&(library.float)一样。

如果还有一个相同类型的结构变量,可以用相同的方法:

struct book bill, newt;
s_gets(bil1.title,MAXTITL); 
s_gets(newt.title,MAXTITL);

.title 引用book 结构的第1个成员。注意,程序清单14.1中的程序以两种不同的格式打印了 library结构变量中的内容。这说明可以自行决定如何使用结构成员。

14.3.2 结构的初始化器

其语法与数组初始化类似,但是结构初始化要使用点运算符和成员名标识的元素。例如,只初始化book结构的value成员.

struct book surprise = {.value = 10.99};

可以按照任意位置初始化:

struct book surprise = {.value = 10.99.author = "Xia",
                        .title = "C Primer Plus"};

另外,对特定成员的最后一次赋值才是她实际获得的值

struct book surprise = {.value = 10.99.author = "Xia",
                        20.99};

赋给value的值为20.99,因为在声明结构中他在author之后,20.99将10.99覆盖了

14.4 结构数组

接下来,我们要把14.1中的程序拓展成处理多本书。显然,每本书的基本信息都可以用一个book类型的结构
变量来表示。所以我们可以使用这一类型的结构数组来处理多本书。

struct book library [MAXBKS];//book类型的结构数组

代码如下

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
char* s_gets(char* st, int n);
#define MAXTITL 40
#define MAXAUTL 40
#define MAXBKS 100   /*书籍的最大数量*/
struct book {   /*简历book模板*/
    char title[MAXTITL];
    char author[MAXAUTL];
    float value;
};
int main()
{
    struct book library[MAXBKS];  /*book类型结构的数组*/
    int count = 0;
    int index;
    printf("Please enter the book title.\n");
    printf("Preas [enter] at the start of a line to stop.\n ");
    while (count < MAXBKS && s_gets(library[count].title, MAXTITL) != NULL && library[count].title[0] != '\0')
    {
        printf("Now enter the author.\n");
        s_gets(library[count].author, MAXAUTL);
        printf("Now enter thr value.\n");
        scanf("%f", &library[count].value);
        count++;
        while (getchar() != '\n')
            continue;
        if (count < MAXBKS)
            printf("Enter the next title.\n");
    }
    if (count > 0)
    {
        printf("Here is the list of your books:\n");
        for (index = 0; index < count; index++)
            printf("%s by %s: $%.2f\n", library[index].title, library[index].author, library[index].value);
    }
    else
        printf("No books? Too bad.\n");
    return 0;
        
}
char* s_gets(char* st, int n)
{
    char* ret_val;
    char* find;
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');
        if (find)
            *find = '\0';
        else
            while (getchar() != '\n')//循环读取缓冲区的剩余字符,直到遇到换行符位置。这样可以清空缓冲区防止下一次输入被影响
                continue;
    }
    return ret_val;
}

在这里插入图片描述

在程序中定义了一个结构体book,包含书籍的标题、作者和价格。同时定义了一个结构体数组library用于存储书籍信息。用户可以输入书籍的标题、作者和价格,程序会逐个存储在library数组中。用户可以输入多达100本书籍的信息,超过100本书籍或者输入空行时,停止输入。

char * s_gets(char * st, int n)函数用于安全地获取用户输入的字符串,避免缓冲区溢出。在main函数中,通过调用s_gets函数获取用户输入的书籍标题和作者。用户输入价格时使用scanf函数,然后清除输入行中剩余的字符。

最后,程序会打印出用户输入的书籍列表,包括书籍的标题、作者和价格。如果没有输入书籍信息,则会输出"No books? Too bad."。

这段代码中的这行语句是一个while循环的条件判断语句,含义如下:

  • count < MAXBKS:表示当前已经输入的书籍数量count小于书籍的最大数量限制MAXBKS,即还有空间可以继续输入书籍信息。
  • s_gets(library[count].title, MAXTITL) != NULL:调用s_gets函数获取用户输入的书籍标题,并判断返回值是否不为NULL。如果用户输入了有效的书籍标题,则继续执行循环;如果用户输入了空行或者发生了错误,则循环结束。
  • library[count].title[0] != '\0':检查当前输入的书籍标题的第一个字符是否不是空字符。如果用户输入了空行(即标题的第一个字符是空字符),则循环结束。

综合起来,这行语句的含义是:只要当前输入的书籍数量未达到最大限制且用户输入了有效的书籍标题(不是空行),就继续循环接收用户输入的书籍信息。

14.4.1 声明结构数组

声明结构数组和声明其他类型的数组类似。

下面是一个声明结构数组的例子: struct book library [MAXBKS];
以上代码把library声明为一个内含MAXBKS个元素的数组数组的每个元素都是一个book类型的数组

因此,library[0]是第1个book类型的结构变量,library[1]是第2个book类型的结构变量,以此类推。参看图14.2可以帮助读者理解。数组名library本身不是结构名,它是一个数组名,该数组中的每个元素都是struct book类型的结构变量。

在这里插入图片描述

14.4.2 标识结构数组的成员

为了标识结构数组中的成员,可以采用访问单独结构的规则:在结构名后面加一个点运算符,再在点运算符后面写上成员名。如下所示:

library[0].value /第1个数组元素与value相关联/
library[4].title /*第5个数组元素与title相关联./

注意,数组下标紧跟在library后面,不是成员名后面:
library.value[2] //错误
library[2],value // 正确
使用1ibrary[2].value的原因是:1ibrary[2]是结构变量名,正如1ibrary(1]是另一个变量名。

顺带一提,下面的表达式代表什么?
library[2].title[4]
这是 library 数组第3个结构变量(library[2]部分)中书名的第5个字符(title[4]部分)。
该例指出,点运算符右侧的下标作用于各个成员,点运算符左侧的下标作用与结构数组。

最后,总结一下:

library	// 一个book结构的数组	
library[2]	// 一个数组元素,该元素是book结构	
library(2].title	// 一个char数组(library[2]的title成员)	
library[2].title[4] // 数组中library[2]元素的title 成员的一个字符

14.3 嵌套结构

有时,在一个结构中包含另一个结构(即嵌套结构)很方便

例如,Shalala Pirosky创建了一个有关她朋友信息的结构。显然,结构中需要一个成员表示朋友的姓名。然而,名字可以用一个数组来表示,其中包含名和姓这两个成员。

程序清单14.3是一个简单的示例:

#include <stdio.h>
#define LEN 20
const char* msgs[5] =
{
    "  Thank you for the wonderful evening, ",
    "You certainly prove that a ",
    "is a special kind of guy.We must get together",
    "over a delicious ",
    " and have a few laughs"
};
struct names {
    /*第1个结构*/
    char first[LEN];
    char last[LEN];
};
struct guy {
    //第2个结构
    struct names handle;  //嵌套结构
    char favfood[LEN];
    char job[LEN];
    float income;
};
int main(void)
{
    struct guy fellow = {
        { "Ewen", "Villard"},
        "grilled salmon",
        "personality coach",
        68112.00
    };
    printf("Dear %s, \n\n", fellow.handle.first);
    printf("%s%s.\n", msgs[0], fellow.handle.first);
    printf("%s%s\n", msgs[1], fellow.job);
    printf("%s\n", msgs[2]);
    printf("%s%s%s", msgs[3], fellow.favfood, msgs[4]);
    if (fellow.income > 150000.0)
        puts("!!");
    else if (fellow.income > 75000.0)
        puts("!");
    else
        puts(".");
    printf("\n%40s%s\n", " ", "See you soon,");
    printf("%40s%s\n", " ", "Shalala");
    return 0;
}

输出:

在这里插入图片描述

首先,注意如何在结构声明中创建嵌套结构。
和声明int类型变量一样,进行简单的声明: struct names handle;
该声明表明handle是一个struct name类型的变量。当然,文件中也应包含结构names的声明。其次,注意如何访问嵌套结构的成员,这需要使用两次点运算符: printf(“Hello, ts!\n”, fellow.handle,first);
从左往右解释fellow.handle.first:
(fellow.handle).first
也就是说,找到fellow,然后找到fellow的handle的成员,再找到handle的first 成员。

14.6 指向结构的指针

至少有4个理由可以解释为何要使用指向结构的指针。

  • 第一,就像指向数组的指针比数组本身更容易操控(如,排序问题)一样,指向结构的指针通常比结构本身更容易操控。

  • 第二,在一些早期的C实现中,结构不能作为参数传递给函数,但是可以传递指向结构的指针。

  • 第三,即使能传递一个结构,传递指针通常更有效率。

  • 第四,一些用于表示数据的结构中包含指向其他结构的指针。

下面的程序(程序清单14.4)演示了如何定义指向结构的指针和如何用这样的指针访问结构的成员。

#include <stdio.h>
#define LEN 20
struct names {
    char first[LEN];
    char last[LEN];
};
struct guy {
    struct names handle;
    char favfood[LEN];
    char job[LEN];
    float income;
};
int main(void)
{
    struct guy fellow[2] = {
        {
            { "Ewen", "Villard" },
            "grilled salmon",
            "personality coach",
            68112.00
        },
        {
            { "Rodney", "Swillbelly" },
            "tripe",
            "tabloid editor",
            432400.00
        }
    };
    struct guy * him; /*这是一个指向结构的指针*/
    printf("address #1: %p #2: %p\n", &fellow[0], &fellow[1]);
    him = &fellow[0];  /*告诉编译器该指针指向何处*/
    printf("pointer #1: %p #2: %p\n", him, him + 1);
    printf("him->income is $%.2f: (*him).income is $%.2f\n", him->income, (*him).income);
    him++;   /*指向下一个结构*/
    printf("him->favfood is %s: him->handle.last is %s\n", him->favfood, him->handle.last);
    return 0;
}

在这里插入图片描述
我们先来看如何创建指向guy类型结构的指针,然后再分析如何通过该指针指定结构的成员。

14.6.1 声明和初始化结构指针

声明结构指针很简单:

struct guy * him;

首先是关键字 struct ,其次是结构标记 guy ,然后是一个 * 号,最后跟着指针名

该声明并未创建一个新的结构,但是指针him现在可以指向任意现有的guy类型的结构

例如,如果 barney是一个guy类型的结构,可以这样写:

him = &barney;

和数组不同的是,结构名并不是结构的地址,因此要在结构名前面加上&运算符。
在本例中,fellow是一个结构数组,这意味着fellow[0]是一个结构。
所以,要让him指向 fellow[0],可以这样写:

 him = &fellow[0];

输出的前两行说明赋值成功。比较这两行发现,him指向fellow[0],him + 1指向 fellow[1]

注意,him加1相当于him指向的地址加84。在十六进制中,874-820=54(十六进制)=84(十进制),因为每个guy结构都占用84字节的内存:names.first占用20字节,names,last占用20字节,favfood占用20字节,job占用20字节,income占用4字节(假设系统中float占用4字节)。

14.6.2 用指针访问成员

指针him 指向结构变量fellow[0],如何通过him 获得fellow[0]的成员的值?程序清单14.4中的第3行输出演示了两种方法。
第1种方法也是最常用的方法:使用->运算符。该运算符由一个连接号(-)后跟一个大于号(>)组成。我们有下面的关系:
如果him == &barney,那么 him->income 即是 barney.income
如果him == &fellow[0],那么him->income即是fellow[0].income
换句话说,->运算符后面的结构指针和.运算符后面的结构名工作方式相同(不能写成him.incone,因为him不是结构名)。
这里要着重理解him是一个指针是该指针所指向结构的一个成员。所以在该例中,him->income是一个float类型的变量
但是hime->income。
第2种方法是,以这样的顺序指定结构成员的值:如果him == &fellow[0],
那么*him == fellow[0],因为&和 *是一对互逆运算符。因此,可以做以下替代:

fellow(0].income ==(*him).income

必须要使用圆括号,因为.运算符比*运算符的优先级高。
总之,如果him是指向guy类型结构barney的指针,下面的关系恒成立:

barney.income ==(*him).income ==him->income //假设 him -- &barney

接下来,我们来学习结构和函数的交互。

14.7 向函数传递结构的信息

ANSIC允许把结构作为参数使用。所以程序员可以选择是传递结构本身,还是传递指向结构的指针。如果你只关心结构中的某一部分,也可以把结构的成员作为参数。我们接下来将分析这3种传递方式,首先介绍以结构成员作为参数的情况。

14.7.1传递结构成员

只要结构成员是一个具有单个值的数据类型(即,int及其相关类型、char、float、double 或指针),便可把它作为参数传递给接受该特定类型的函数。程序清单14.5中的财务分析程序(初级版本)演示了这一点,该程序把客户的银行账户添加到他/她的储蓄和贷款账户中。

#include <stdio.h>
#define FUNDLEN 50
struct funds {
    char bank[FUNDLEN];//声明结构
    double bankfund;
    char save[FUNDLEN];
    double savefund;
};
double sum(double x, double y);
int main(void)
{
    struct funds stan = {  //初始化结构
        "Garlic-Melon Bank",
        4032.27,
        "Lucky's Savings and Loan",
        8543.94
    };
    printf("Stan has a total of $%.2f.\n", sum(stan.bankfund, stan.savefund));//通过点运算符访问结构
    return 0;
}
/*两个double类型的数相加*/
double sum(double x, double y)
{
    return (x + y);
}

在这里插入图片描述

在这里插入图片描述

14.7.2 传递结构的地址

这次我们把结构的地址作为参数

#include <stdio.h>
#define FUNDLEN 50
struct funds {
    char bank[FUNDLEN];
    double bankfund;
    char save[FUNDLEN];
    double savefund;
};
double sum(const struct funds *monney);   /*参数是一个指针*/
int main(void)
{
    struct funds stan = {
        "Garlic-Melon Bank",
        4032.27,
        "Lucky's Savings and Loan",
        8543.94
    };
    printf("Stan has a total of $%.2f.\n", sum(&stan));
    return 0;
}
double sum(const struct funds * money)
{
    return (money->bankfund + money->savefund);//通过->来访问结构中的变量
}

结果同上

在这里插入图片描述
sum()函数使用指向funds结构的指针(money)作为它的参数把地址&stan传递给该函数,使得指针money 指向结构 stan然后通过->运算符获取stan.bankfund 和stan.savefund的值。由于该函数不能改变指针所指向值的内容,所以把money声明为一个指向const的指针。
虽然该函数并未使用其他成员,但是也可以访问它们。注意,必须使用&运算符来获取结构的地址和数组名不同,结构名不是其地址的别名

14.7.3 传递结构

把结构本身作为参数传递给函数:

#include <stdio.h>
#define FUNDLEN 50
struct funds {
    char bank[FUNDLEN];
    double bankfund;
    char save[FUNDLEN];
    double savefund;
};
double sum(struct funds moolah);  /*参数是一个结构*/
int main(void)
{
    struct funds stan = {
        "Garlic-Melon Bank",
        4032.27,
        "Lucky's Savings and Loan",
        8543.94
    };
    printf("Stan has a total os $%.2f.\n", sum(stan));
    return 0;
}
double sum( struct funds moolah)
{
    return (moolah.bankfund + moolah.savefund);
}

结果同上

14.7.4 其他结构特征

现在的C允许把一个结构赋值给另一个结构,但是数组不能这样做
也就是说,
如果n_data和o_data都是相同类型的结构,可以这样做:

o_data = n_data; //把一个结构赋值给另一个结构

这条语句把ndata的每个成员的值都赋给。data的相应成员。即使成员是数组,也能完成赋值
另外,还可以把一个结构初始化为相同类型的另一个结构:

struct names right_field = {"Ruthie", "George"};
struct names captain = right_field; //把一个结构初始化为另一个结构

现在的C(包括ANSIC),函数不仅能把结构本身作为参数传递,还能把结构作为返回值返回

把结构作为函数参数可以把结构的信息传送给函数:

把结构作为返回值的函数能把结构的信息从被调函数传回主调函数。

结构指针也允许这种双向通信,因此可以选择任一种方法来解决编程问题。我们通过另一组程序示例来演示这两种方法。

为了对比这两种方法,我们先编写一个程序以传递指针的方式处理结构,然后以传递结构和返回结构的方式重写该程序。

1.以传递指针的方式处理结构:

#include <stdio.h>
#include <string.h>
#define NLEN 30
struct namect {
    char fname[NLEN];
    char lname[NLEN];
    int letters;
};
void getinfo(struct namect *);
void makeinfo(struct namect *);
void showinfo(const struct namect *);
char * s_gets(char * st, int n);
int main(void)
{
    struct namect person;
    getinfo(&person);
    makeinfo(&person);
    showinfo(&person);
    return 0;
}
void getinfo(struct namect * pst)
{
    printf("Please enter your first name.\n");
    s_gets(pst->fname, NLEN);
    printf("Please entre your last name.\n");
    s_gets(pst->lname, NLEN);
}
void makeinfo(struct namect * pst)
{
    pst->letters = strlen(pst->fname) + strlen(pst->lname);
}
void showinfo(const struct namect * pst)
{
    printf("%s %s, your name contains %d letters.\n", pst->fname, pst->lname, pst->letters);
}
char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');  //查找换行符
        if (find)
        *find = '\0';   //如果地址不是NULL,在此处放置一个空字符
        else
        while (getchar() != '\n')   //处理输入行的剩余字符
        continue;
    }
    return ret_val;
}

例如

    getinfo(&person);
    makeinfo(&person);
    showinfo(&person);
 s_gets(pst->fname, NLEN);
 s_gets(pst->lname, NLEN);

第一个函数输入,第二个函数计算长度,第三个函数输出。

在这里插入图片描述
在这里插入图片描述

2.传递并用函数返回结构:

在这里插入图片描述

#include <stdio.h>
#include <string.h>
#define NLEN 30
struct namect {
    char fname[NLEN];
    char lname[NLEN];
    int letters;
};
struct namect getinfo(void);
struct namect makeinfo(struct namect);
void showinfo(struct namect);
char * s_gets(char * st, int n);
int main(void)
{
    struct namect person;
    person = getinfo();
    person = makeinfo(person);
    showinfo(person);
    return 0;
}
struct namect getinfo(void)
{
    struct namect temp;
    printf("Please enter your first name.\n");
    s_gets(temp.fname, NLEN);
    printf("Please enter your last name.\n");
    s_gets(temp.lname, NLEN);
    return temp;
}
struct namect makeinfo(struct namect info)
{
    info.letters = strlen(info.fname) + strlen(info.lname);
    return info;
}
void showinfo(struct namect info)
{
    printf("%s %s, your name contains %d letters.\n", info.fname, info.lname, info.letters);
}
char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');  //查找换行符
        if (find)
        *find = '\0';   //如果地址不是NULL,在此处放置一个空字符
        else
        while (getchar() != '\n')   //处理输入行的剩余字符
        continue;
    }
    return ret_val;
}

运行结果同上

在这里插入图片描述

总的来说,用指针传递时优点是占空间小,效率高。但是可能不安全,需要const.用结构参数和返回值时每个函数都会创建自己的副本(备份),当值很大时会很麻烦


如果您发现文章有错误请与我留言,感谢

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

技术选型指南:Oracle、SQL Server还是DB2?

Oracle vs SQL Server vs DB2 - 选哪个好&#xff1f; 在企业级数据管理领域&#xff0c;常用的几个选择有Oracle、SQL Server和DB2。 首先&#xff0c;我们从以下几个方面做一下对比&#xff1a; 1. 性能和稳定性&#xff1a; Oracle: Oracle就像是那种精密的瑞士手表&…

【字符串】【 LCP】【C++算法】2573找出对应 LCP 矩阵的字符串

作者推荐 【深度优先搜索】【树】【有向图】【推荐】685. 冗余连接 II 本文涉及知识点 字符串 LCP LeetCode:2573找出对应 LCP 矩阵的字符串 对任一由 n 个小写英文字母组成的字符串 word &#xff0c;我们可以定义一个 n x n 的矩阵&#xff0c;并满足&#xff1a; lcp[i…

Linux-目录I/O-004

学习重点&#xff1a; 1.目录I/O的函数接口 2.目录的遍历&#xff0c;目录的递归遍历1.【mkdir】 1.1函数原型 【int mkdir(const char *pathname, mode_t mode);】1.2函数功能 创建目录文件1.3函数参数 1.3.1【pathname】 文件路径1.3.2【mode】 文件的权限1.4返回值 …

【软件使用】postman使用教程

​ &#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;软件安装及使用 ⛳️ 功不唐捐&#xff0c;玉汝于成 ​ 目录 前言 正文 步骤1&#xff1a;安装Postman 步骤2&#xff1a;发送请求 步骤3&#xff1a;管理环境变量 步骤4&#xff1…

全链路压测演进之迭代式压测

1.背景原因 !! 做系统服务压测都是比较耗时耗人力的&#xff0c;特别是在生产环境上做压测&#xff0c;压测的时间都是在晚上23点后&#xff0c;甚至在凌晨1-4点&#xff0c;每次投入的人力成本较高&#xff08;经常是晚上通宵加班压测&#xff0c;疲惫感十足&#xff09;&…

Mybatis常见问题

引言 MyBatis工作原理如下图所示&#xff1a; 1、读取MyBatis配置文件&#xff1a;mybatis-config.xml为MyBatis的全局配置文件&#xff0c;配置了MyBatis的运行环境等信息&#xff0c;例如数据库连接信息。 2、加载映射文件&#xff1a;映射文件即SQL映射文件&#xff0c;该…

Redis部署方式(一)四种部署方式介绍

redis的四种部署方式&#xff1a; Redis单机模式部署、Redis主从模式部署、Redis哨兵模式部署、Cluster集群模式部署&#xff0c;后面三种&#xff08;主从模式&#xff0c;Sentinel哨兵模式&#xff0c;Cluster模式&#xff09;也可以统称为集群模式。 一、单机 1、缺点&…

SG-8018CG晶体振荡器可编程

SG-8018CG 晶体振荡器是一款集宽频率范围、高稳定性、低功耗及超小型封装于一身的高性能时钟源解决方案。是需要在高温环境中运作的复杂电子系统的理想选择。通过SG-Writer II工具的支持&#xff0c;SG-8018CG系列提供了快速、灵活的编程选项&#xff0c;使得它能够迅速适应市场…

MySQL多实例部署:从概念到实操的全面指南

目录 MySQL多实例管理 单实例 什么是多实例 多实例的好处 多实例的弊端 MySQL多实例用在哪些场景 资金紧张的公司 用户并发访问量不大的业务 大型网站也有用多实例 部署MySQL多实例 rpm和源码的优缺点 二进制方式安装mysql 准备二进制mysql运行所需的环境 准备多…

【大模型 向量库】从向量搜索到向量数据库

大模型向量库 向量&#xff1a;AI核心向量库&#xff1a;语义近似搜索大模型 向量库YOLO 向量数据库嵌入&#xff08;Embedding&#xff09;设计最近邻搜索近似近邻搜索 主流向量数据库Milvus 实践 向量&#xff1a;AI核心 向量伴随着 AI 模型的发展而发展。 向量&#xff…

【vue3】手动实现md在线编辑

1.背景 由于知识库的一些.md格式的文件的文件内容可能会有变动&#xff0c;如果频繁下载修改后&#xff0c;再进行上传&#xff0c;会让用户操作不方便&#xff0c;为此接入md在线编辑功能 2 md在线编辑具体实现 2.1 搭建项目 搭建项目下载和引入bytemd和fflate相关依赖&…

Microsoft Office Visio 2007中绘制大括号

文章目录 一、Microsoft Office Visio 2007中绘制大括号 一、Microsoft Office Visio 2007中绘制大括号 在Microsoft Office Visio 2007中绘制大括号的方法如下&#xff1a; 打开Visio 2007——文件——形状——其他Visio方案——标注 此时左侧栏中出现“标注”栏&#xff0c…

通过VSCode开发Python项目

一、插件准备 Python 插件&#xff0c;必须 autoDocstring 生成注释&#xff0c;和Pycharm一样输入三个引号"""会生产注释结构 Todo Tree 高亮显示 TODO/FIXME 二、python相关设置 一&#xff09;设置python环境 按"F1"打开命令面板&#xff08;…

YOLOv8改进 | 进阶实战篇 | 利用辅助超推理算法SAHI推理让小目标无所谓遁形(支持视频和图片)

欢迎大家订阅我的专栏一起学习YOLO! 一、本文介绍 本文给大家带来的是进阶实战篇,利用辅助超推理算法SAHI进行推理,同时官方提供的版本中支持视频,我将其进行改造后不仅支持视频同时支持图片的推理方式,SAHI主要的推理场景是针对于小目标检测(检测物体较大的不适用,…

【Docker】Docker存储卷

文章目录 一、什么是存储卷二、为什么需要存储卷三、存储卷分类四、管理卷Volume创建卷方式一&#xff1a;Volume 命令操作方式二&#xff1a;-v 或者--mount 指定方式三&#xff1a;Dockerfile 匿名卷 操作案例Docker 命令创建管理卷Docker -v 创建管理卷Docker mount 创建管理…

js-Vue Router 中的方法,父A-子B-子C依次返回,无法返回到A,BC中形成循环跳转解决

1.常用的方法 在 Vue Router 中&#xff0c;有一些常用的方法用于实现路由导航和管理。以下是一些常见的 Vue Router 方法及其作用&#xff1a; push: router.push(location, onComplete, onAbort) 作用&#xff1a;向路由历史记录中添加一个新条目&#xff0c;并导航到指定的路…

电脑文件误删除如何恢复?2024最新三种恢复方法

我们在使用电脑的过程中&#xff0c;随着时间的不断推移&#xff0c;渐渐的我们会发现C盘内存空间不足了。这是因为我们很多文件都默认存储在C盘&#xff0c;所以导致C盘空间不足&#xff0c;电脑运行越来越慢。那么电脑哪些文件可以删除&#xff0c;电脑删除的东西怎么恢复&am…

Istio复习总结:xDS协议、Istio Pilot源码、Istio落地问题总结

1、xDS协议 1&#xff09;、xDS是什么 xDS是一类发现服务的总称&#xff0c;包含LDS、RDS、CDS、EDS以及SDS。Envoy通过xDS API可以动态获取Listener&#xff08;监听器&#xff09;、Route&#xff08;路由&#xff09;、Cluster&#xff08;集群&#xff09;、Endpoint&…

openai公司的chatgpt-3.5参数库内还未增加sora的语料信息

openai公司的chatgpt-3.5参数库内还未增加sora的语料信息&#xff01;我想通过openai公司的chatgpt3.5来了解一下关于sora的技术信息&#xff0c;结果呢&#xff0c;它竟然回答不知道sora是什么。看来&#xff0c;sora的语料库信息还未来得及加入chatgpt3.5的训练模型中。 如图…

Linux——网络通信TCP通信常用的接口和tcp服务demo

文章目录 TCP通信所需要的套接字socket()bind()listen()acceptconnect() 封装TCP socket TCP通信所需要的套接字 socket() socket()函数主要作用是返回一个描述符&#xff0c;他的作用就是打开一个网络通讯端口&#xff0c;返回的这个描述符其实就可以理解为一个文件描述符&a…