C语言基础8——结构体

// 本节有两部分,第一部分为结构体的定义、初始化、结构体数组。第二部分为结构体对齐,在考研初试中选择题出现较多。(☆☆☆☆)

1、结构体的定义、初始化、结构体数组。

        Q1:首先知道,为什么要学结构体?

                之前学过的构造数据类型有,数组,但是数组只能放相同的数据类型,例如int类型数组只能放int类型的数据,而很多时候需要将不同类型的数据组合成一个整体,以方便引用。例如:一个学生的信息有,学号,姓名,性别,年龄,地址等等属性,如果针对学生的各个属性都分别单独定义一个变量,那么在有多个学生时,变量就难以分清,因此,C语言提供了结构体来管理不同类型的数据组合。      

        声明一个结构体类型的一般形式为,先声明结构体类型,再定义变量名。例如:
struct student//结构体名 
{//成员列表
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
};      //注:定义结构体一定要加分号。

        一般将结构体定义在main函数外。

        【例 1.1】结构体的 scanf 读取和输出。

                注:结构体输出时必须单独去访问内部每个成员

#include <stdio.h>

struct student//结构体名
{//成员列表
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
};
int main() {
    struct student yhw={201245,"hh",'M',21,98.0,"nuc"};//变量和结构体类型名字不能相同
    printf("%d,%s,%c,%d,%f,%s",yhw.num,yhw.name,yhw.sex,yhw.age,yhw.score,yhw.addr);
    scanf("%d%s %c%d%f%s",&yhw.num,&yhw.name,&yhw.sex,&yhw.age,&yhw.score,&yhw.addr);//混合读取时,%d,%s,%f,都可以挨着,因为它们会忽略空格,%c和别的读取之间需要间隔,因为%c不能忽略空格。
    printf("%d,%s,%c,%d,%f,%s",yhw.num,yhw.name,yhw.sex,yhw.age,yhw.score,yhw.addr);
    return 0;
}

运行结果如图:

        【例 1.2】结构体数组的 scanf 读取和输出。

#include <stdio.h>

struct student//结构体名
{//成员列表
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
};
int main() {
    struct student y[3];
    int i;
    for(i=0;i<3;i++){
        scanf("%d %s %c %d %f %s",&y[i].num,&y[i].name,&y[i].sex,&y[i].age,&y[i].score,&y[i].addr);
    }
    for(i=0;i<3;i++){
        printf("%d,%s,%c,%d,%f,%s\n",y[i].num,y[i].name,y[i].sex,y[i].age,y[i].score,y[i].addr);
    }
    return 0;
}

运行结果如图:

        结构体类型声明要放在 main 函数之前,这样 main 函数中才可以使用这个结构体,工作中
往往把结构体声明放在头文件中。注意,结构体类型声明最后一定要加分号,否则会编译不通。
另外,定义结构体变量时,使用 struct student 来定义,不能只有 struct student ,否则也会
编译不通,sarr 是结构体数组变量。结构体的初始化只能在一开始定义,如果struct student yhw={201245,"hh",'M',21,98.0,"nuc"};已经执行,即 struct student yhw  已经定义,就不能再执
yhw={201245,"hh",'M',21,98.0,"nuc"}。如果结构体变量已经定义,那么只能对它的每个成
员单独赋值,如 yhw.num=1003
        采用“结构体变量名.成员名”的形式来访问结构体成员,例如用 yhw.num 访问学号。在进行
打印输出时,必须访问到成员,而且 printf 中的 % 类型要与各成员匹配。使用 scanf 读取标准输
入时,也必须是各成员取地址,然后进行存储,不可以写成 &s ,即不可以直接对结构体变量取
地址。整型数据( %d )、浮点型数据( %f )、字符串型数据( %s )都会忽略空格,但是字符
型数据( %c )不会忽略空格,所以如果要读取字符型数据,那么就要在待读取的字符数据与其
他数据之间加入空格。

2、结构体对齐        

        结构体本身的对齐规则有好几条,比较难记,而且对于考研初试完全没有必要,考研初
试只需要记住一条, 结构体的大小必须是其最大成员的整数倍
       例如上述结构体中,int num (4)+ char name[20] (20) + char sex (1) + int age (4) + float score (4) + char addr[30] (30) = 63 个字节
        但是在实际情况中,上述数据类型中,“int,char,float”最大成员是int(最大成员计算不包含数组),所占4个字节,那么其余成员字节都得是4的整数倍,而其中char sex (1个字节)与char addr[30] (30个字节)都不是4的整数倍,给 sex +3 ,addr+2,之后,结构体一共大小为63+3+2=68个字节。
        在计算中,若其中两个类型所占字节之和小于最大成员所占字节,那么两个共占字节=最大字节。
        Q2:为什么要对齐?
              为了CPU高效的去取内存上的数据。(408课程会讲到原理)
        注:这里要熟记各个类型数据所占的字节,double(8)、float(4)、short(2)、int(4)、char(1)。

3、结构体指针与typedef

3.1 结构体指针

        结构体指针就是该变量所占据的内存段的起始地址。可以设置一个指针变量,用它指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。指针变量也可以用来 指向结构体数组中的元素,从而能够通过结构体指针快速访问结构体内的每个成员。

        例一:结构体指针的使用

#include <stdio.h>

struct student{
    int num;
    char name[20];
    char sex;
};
//结构体指针的练习
int main() {
    struct student s={1001,"yhw",'M'};
    struct student sarr[3]={1001,"yhw1",'M',1005,"yhw2",'M',1007,"yhw3",'F'};
    struct student *p;  //定义了一个结构体指针变量

    p = &s;
    printf("%d,%s,%c\n",s.num,s.name,s.sex);
    p = sarr;//数组名里面所存的是数组起始地址,因此p = sarr 和 p = &sarr[0] 等价。
    printf("%d,%s,%c\n",(*p).num,(*p).name,(*p).sex);
    p++;
    printf("%d,%s,%c\n",(*p).num,(*p).name,(*p).sex);//方式1通过结构体指针访问成员
    printf("%d,%s,%c\n",p->num,p->name,p->sex);//方式2通过结构体指针访问成员
    return 0;
}

3.2 typedef的使用

        前面定义结构体变量时使用的语句是 struct student s ,以这种方式来定义结构体变量有些
麻烦,即每次都需要写 struct student 。那么有没有简单一些的定义方式呢?答案是肯定的,可
以选择使用 typedef 声明新的类型名来代替已有的类型名。
        
#include <stdio.h>

//typedef的使用,typedef起别名
typedef struct student{
    int num;
    char name[20];
    char sex;
}stu,*pstu;//stu 等价于struct student
           //pstu 等价于struct student *


typedef int INGETER;

int main() {
    stu s={1001,"yhw",'M'};
    stu *p1 = &s;//定义了一个结构体指针变量
    pstu p2 = &s;
    INGETER num = 10 ;
    printf("i=%d,p->num=%d\n",num,p1->num);
    printf("i=%d,p->num=%d\n",num,p2->num);
    return 0;
}

        使用stu定义结构体变量和使用structstudent定义结构体变量是等价的;使用INTEGER 定义变量i和使用int定义变量i是等价的;pstu等价于structstudent*,所以p是结构体指针变量。

4、C++引用讲解

4.1 C++应用讲解 

        对于 C++ ,首先新建源文件时,名字需要叫 main.cpp, cpp 后缀结尾, 不能像我们之前那样叫 main.c ,王道数据结构都是用的 C 语言语法,但是额外使用了 C++ 的引用呢,原因是很多数据结构都采用了这个做法,比如严老师的数据结构也是这样编写的,下面我们来看一下引用的便捷性。
        例4.1.1,在子函数中修改主函数普通变量的值。
#include <stdio.h>
//C++的引用的讲解,在子函数中修改主函数普通变量的值。

void modify_num(int &b){//形参中写&这个符号,称之为引用而不是取地址。
    b = b + 1;
}
int main() {
    int a = 10;
    modify_num(a);
    printf("after modify_num = %d\n",a);
    return 0;
}

        在上述代码中,如果改为纯C,则代码如下:

#include <stdio.h>
void modify_num(int *b)
{ 
    *b=*b+1;
}
int main() {
    int a=10;
    modify_num(&a);
    printf("after modify_num a=%d\n",a);
    return 0;
}

        可以发现,在C++代码中当在子函数中要修改函数中变量的值,就用&,不需要修改,就不用,代码效率明显变高。

        例4.1.2,在子函数中修改主函数的一级指针变量。

#include <iostream>

void modify_pointer(int* &p,int* &q)//引用必须和变量名紧邻。
{
    p = q;
}
//子函数中修改主函数的一级指针变量
int main() {
    int *p = NULL;
    int i = 10;
    int *q = &i;
    modify_pointer(p,q);
    printf("after modify_point = %d\n",*p);//操作系统中设置了,空地址不能访问。
    return 0;//当代码执行完毕后,退出代码为 -1073741819,不为0,那么代表进程异常结束。
}

        上述代码如果改为纯C,就需要用到二级指针:

#include <stdio.h>
void modify_pointer(int **p,int *q)//相对于 C++这里是 int **p;
{ 
    *p=q;//这里的写法和例 1.2 中的是非常类似的
}
int main() {
    int *p=NULL;
    int i=10;
    int *q=&i;
    modify_pointer(&p,q);//相对于 C++这里是&p
    printf("after modify_pointer *p=%d\n",*p);
    return 0;
}

4.2 C++的布尔类型

        布尔类型在 C 语言没有,是 C++的,有 true (1)和 false =(0) 。例如:
        
#include <stdio.h>
//设置布尔值的好处是提升了代码的可阅读性
int main() {
    bool a= true;
    bool b= false;
    printf("a=%d,b=%d\n", a,b);
    return 0;
}

5.课后OJ作业

5.1 作业1代码

#include <stdio.h>

typedef struct student{
    int num;
    char name[20];
    char sex;
}stu;

int main() {
    stu s;
    scanf("%d %s %c",&s.num,s.name,&s.sex);
    printf("%d %s %c\n",s.num,s.name,s.sex);
    return 0;
}

5.2 作业2代码

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

 void modify_pointer(char * &p)
{
    p = (char*)malloc(100);
    fgets(p,100,stdin);//stdin代表标准输入。
};
int main() {
    char *p=NULL;
    modify_pointer(p);
    puts(p);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值