结构体和函数
结构体变量作为函数形参时也是值传递, 在函数内修改形参, 不会影响外界实参。
#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");
}
好了,今天就分享这么多,明天继续。