BSP Day 27

结构体和函数

结构体变量作为函数形参时也是值传递, 在函数内修改形参, 不会影响外界实参。

#include <stdio.h>

struct Person{
    char *name;
    int age;
};

void test(struct Person per);

int main()
{
    struct Person p1 = {"lnj", 35};
    printf("p1.name = %s\n", p1.name); // lnj
    test(p1);
    printf("p1.name = %s\n", p1.name); // lnj
    return 0;
}
void test(struct Person per){
    per.name = "zs";
}

接下来附上俩道关于结构体函数和指针的练习题 

1.设有一组学生的成绩数据已经放在结构数组boy中, 1)计算不及格人数。要求:使用结构指针变量作为函数参数编程。struct stu 2.在学生wang ming之前添加一条记录“105, ma li, f, 20”,并输出所有学生信息。3.学生cheng 1ing已转学,请将其记录从数组中删除,并输出所有学生信息。   

 

我觉得其实就是结构体的增删改查,代码如下:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct
{
	int id;
	char name[20];
	char sex;
	float score;
}Stu;
 
int failed(Stu *p,int n)
{
	int i,j=0;
	for(i=0;i<n-1;i++)
	{
		if((*(p+i)).score<60)
			j++;
	}
	return j;
}
 
void add(Stu *p)
{
	*(p+5)=*(p+4);
	(*(p+4)).id=105;
	(*(p+4)).sex='f';
	strcpy((*(p+4)).name,"ma li");
	(*(p+4)).score=20;
}
 
void print(Stu *p,int n)
{
	int i;
	for(i=0;i<n;i++)
	{
		if((*(p+i)).id==0)
			;
		else
			printf("id=%d name=%s sex=%c score=%.2f\n",(*(p+i)).id,(*(p+i)).name,\
			(*(p+i)).sex,(*(p+i)).score);
	}
	printf("\n\n");
}
 
int main()
{
	Stu stu[6]={{101,"li ping",'m',45},	 
			{102,"zhang ping",'m',62.5},
			{103,"he fang",'m',92.5},
			{104,"cheng ling",'f',87},
			{106,"wang ming",'m',58}};
	int i=0;
	Stu *p=NULL;
	p = &stu; 
	i = failed(p,6);
	printf("不及格的人数为%d\n",i);
	add(p);
	print(p,6);
	stu[3].id=0;
	print(p,6);
	
	return 0;
}

 还有通过结构体指针嵌套来输入和输出并排序,将它分装成函数后如下:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct home
{
	int id;
	char name[20];
	char addr[20];
}HOME;
typedef struct per
{
	char name[20];
	int age;
	HOME *home;
}PER;
void scanf_struct(PER person[],int length);
void print_struct(PER person[],int length);
void pai_struct(PER person[],int length);
int main()
{
	HOME home = {9527,"周星驰","中国香港"}; 
	PER person1 = {
	.name = "周星驰",
	.age = 23,
	.home = &home},person2;
	
	printf("%s %d %d %s %s\n",person1.name,person1.age,person1.home->id,person1.home->name,person1.home->addr);
	PER person[6];
	scanf_struct(person,6);
	print_struct(person,6);
	pai_struct(person,6);
	print_struct(person,6);
	return 0;
 } 
 void scanf_struct(PER person[],int length)
 {
 	int i;
 	HOME home = {0,NULL,NULL};
 	for(i=0;i<length;i++)
 	{
 		person[i].home = &home;
 		scanf("%s%d%d%s%s",person[i].name,&person[i].age,&person[i].home->id,person[i].home->name,person[i].home->addr);
	 }
 }
  void print_struct(PER person[],int length)
 {
 	int i;
 	for(i=0;i<length;i++)
 	{
 		printf("%s %d %d %s %s\n",person[i].name,person[i].age,person[i].home->id,person[i].home->name,person[i].home->addr);
	 }
 }
 void pai_struct(PER person[],int length)
 {
 	int i,j;
 	PER temp;
 	for(i=0;i<length-1;i++)
 	{
 		for(j=0;j<length-1-i;j++)
 		{
 			if(person[j].age>person[j+1].age)
 			{
 				temp = person[j];
 				person[j] = person[j+1];
 				person[j+1] = temp;
			 }
		 }
	 }
 }

注意:结构体虽然是构造类型, 但是结构体之间赋值是值拷贝, 而不是地址传递 。

还有今天看了结构体变量占用内存的大小,感觉很有意思,分享一下

 struct Person{
        int age; // 4
        char ch; // 1
        double score; // 8
    };
    struct Person p;
    printf("sizeof = %i\n", sizeof(p)); // 16

占用内存最大属性是score, 占8个字节, 所以第一次会分配8个字节。

将第一次分配的8个字节分配给age4个,分配给ch1个, 还剩下3个字节。

当需要分配给score时, 发现只剩下3个字节, 所以会再次开辟8个字节存储空间。

一共开辟了两次8个字节空间, 所以最终p占用16个字节。

 struct Person{
        int age; // 4
        double score; // 8
        char ch; // 1
    };
    struct Person p;
    printf("sizeof = %i\n", sizeof(p)); // 24

占用内存最大属性是score, 占8个字节, 所以第一次会分配8个字节。

将第一次分配的8个字节分配给age4个,还剩下4个字节。

当需要分配给score时, 发现只剩下4个字节, 所以会再次开辟8个字节存储空间。

将新分配的8个字节分配给score, 还剩下0个字节。

当需要分配给ch时, 发现上一次分配的已经没有了, 所以会再次开辟8个字节存储空间。

一共开辟了3次8个字节空间, 所以最终p占用24个字节。

最后分享一下大端小端的区别

大、小端序的由来(只是说一下大、小端名字的由来 摘自<深入理解计算机系统第三版>)

术语“little endian(小端)”和“big endian(大端)”出自Jonathan Swift的《格列佛游 记》(Gulliver's Trabels)一书,其中交战的两个派别无法就应该从哪一端(小端还是大端)打开一个半熟的鸡蛋达成一致。

一下是Jonathan Swift在1726年关于大小端之争历史的描述:

“......下面要告诉你的是,Lilliput和Blefuscu这两大强国在过去36个月里一直在苦战。战争开始是由于以下的原因:我们大家都认为,吃鸡蛋前,原始的方法是打破鸡蛋较大的一端,可是当今皇帝的祖父小时候吃鸡蛋,一次按古法打鸡蛋是碰巧将一个手指弄破了,因此他的父亲,当时的皇帝,就下了一道敕令,命令全体臣民吃鸡蛋时打破鸡蛋较小的一端,违令者重罚。老百姓们对这项命令极为反感。历史告诉我们,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位。这些叛乱大多都是由Blefuscu的国王大臣们煽动起来的。叛乱平息后,流亡的人总是逃到那个帝国去寻救避难。据估计,先后几次有11000人情愿受死也不肯去打破鸡蛋较小的一端。关于这一争端,曾出版过几百本大部著作,不过大端派的书一直是受禁的,法律也规定该派的任何人不得做官。”(此段译文摘自网上蒋剑锋译的 《格列佛游记》第一卷第4章。)

在他那个时代,Swift是在讽刺英国(Lilliput)和法国(Blefuscu)之间的持续的冲突。Danny Cohen,一位网络协议的早期开创者,第一次使用这两个术语来指代字节顺序,后来这个术语被广泛接纳了

什么是大端 / 小端?
大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中

为什么会有大小端之分呢?
因为在计算机系统中,我们以字节为存储单元,每个地址单元都对应着一个字节,一个字节为8bit。而在C语言中,不仅仅是一个字节来存储一个数据,除了一个字节的char,还有两个字节的short,四个字节的int等等(看具体编译器)。另外,对于位数大于8位的处理器,例如32位的处理器,由于寄存器的宽度大于一个字节,那么就有如何将多个字节进行排布的问题,于是就出现了大小端的问题。下面举个例子:

 大、小端存储各自的优势:

小端模式 :强制转换数据不需要调整字节内容,1、2、4字节的存储方式一样。

大端模式 :符号位的判定固定为第一个字节,容易判断正负

怎么测试电脑是小端模式还是大端模式呢?

1.将int 48存起来,然后取得其地址,再将这个地址转为char* 这时候,如果是小端存储,那么char*指针就指向48;48对应的ASCII码为字符‘0’;

void judge_bigend_littleend1()
{
    int i = 48;
    int* p = &i;
    char c = 0;
    c = *((char*)p);

    if (c == '0')
        printf("小端\n");
    else
        printf("大端\n");
}

2.定义变量int i=1;将 i 的地址拿到,强转成char*型,这时候就取到了 i 的低地址,这时候如果是1就是小端存储,如果是0就是大端存储。

void judge_bigend_littleend2()
{
    int i = 1;
    char c = (*(char*)&i);

    if (c)
        printf("小端\n");
    else
        printf("大端\n");
}

 3.定义联合体,一个成员是多字节,一个是单字节,给多字节的成员赋一个最低一个字节不为0,其他字节为0 的值,再用第二个成员来判断,如果第二个字节不为0,就是小端,若为0,就是大端。

void judge_bigend_littleend3()
{
    union
    {
        int i;
        char c;
    }un;
    un.i = 1;

    if (un.c == 1)
        printf("小端\n");
    else
        printf("大端\n");
}

 好了,今天就分享这么多,明天继续。

 


 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixiaxiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值