C复合结构——结构体struct

C语言结构体详解
本文详细介绍了C语言中结构体的定义与使用方法,包括三种定义方式、成员访问、结构体数组及指针的应用,同时还讲解了链表的创建与操作,以及结构体中常用的内存操作函数。

文章目录:

一: 结构体定义的3种方式

1.第一种方式 先声明结构体,再定义变量名

2.第二种方式 声明类型的同时定义变量

3.第三种方式 直接定义结构体类型变量

二: 访问结构体中的成员

三: 结构体数组

1.语法形式 

2.初始化

四:指向结构体类型数据的指针 

1.指向结构体类型数据的指针 

1.1 概念性质  

1.2 语法一般形式

1.3 访问的一般形式 

2.结构体指针变量作为函数参数

2.1 案例一:(先用结构体变量作函数参数)

2.2 案例二:(改用指向结构体变量(或数组)的指针作实参)

2.3 用结构体变量的成员作参数

3.指向结构体数组的指针 

五:用指针处理链表

1.链表概述

2.简单链表例子

3.建立动态链表

3.1 打印链表

3.2 删除节点

3.3 更新节点

3.4 增加节点

五:几个结构体中常用的函数memcpy() memset() bzero()

1.结构体的复制函数memcpy

2.内存空间赋值函数memset

3.内存空间清零函数bzero


C语言——结构体&位域 

main(){
    struct bs{
        unsigned a:1;
        unsigned b:3;
        unsigned c:4;
    } bit,*pbit;
    bit.a=1;    /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
    bit.b=7;    /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
    bit.c=15;    /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
    printf("%d,%d,%d\n",bit.a,bit.b,bit.c);    /* 以整型量格式输出三个域的内容 */
    pbit=&bit;    /* 把位域变量 bit 的地址送给指针变量 pbit */
    pbit->a=0;    /* 用指针方式给位域 a 重新赋值,赋为 0 */
    pbit->b&=3;    /* 使用了复合的位运算符 "&=",相当于:pbit->b=pbit->b&3,位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3) */
    pbit->c|=1;    /* 使用了复合位运算符"|=",相当于:pbit->c=pbit->c|1,其结果为 15 */
    printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c);    /* 用指针方式输出了这三个域的值 */
}

一: 结构体定义的3种方式

对于结构体变量的成员可以像普通变量一样进行各种运算
    student2.score = student1.score;
    sum = student1.score + student2.score;
    student1.age ++;
    ++ student1.age;

可以引用结构体变量【成员】的地址,也可以引用结构体【变量】的地址
    scanf("%d", &student1.num);    // 输入 student1.num 的值
    printf("%o", &student1);       // 输出 student1 的首地址

但不能用以下语句整体读入结构体变量如:
    scanf("%d,%s,%c,%d,%f,%s", &student1);

1.第一种方式 先声明结构体,再定义变量名

// 第一种方式 先声明结构体,再定义变量名
struct Student
{
    int age;
    float score;
    char sex;
};
struct Student st2;

2.第二种方式 声明类型的同时定义变量

// 第二种方式 声明类型的同时定义变量
struct Student2
{
    int age;
    float score;
    char sex;
} st2;

3.第三种方式 直接定义结构体类型变量

// 第三种方式 直接定义结构体类型变量
struct 
{
    int age;
    float score;
    char sex;
} st3;

二: 访问结构体中的成员

如何取出结构体变量中的每一个成员【重点】:

结构体变量名.成员名

指针变量名->成员名 (第二种方式更常用)
其中指针变量名->成员名 在计算机内部会被转化成 (*指针变量名).成员名 的方式来执行。所以说这两种方式是等价的


struct Student st = {80, 66.6, 'F'};  // 初始化  定义的同时赋初值
    struct Student * pst = &st;       // &st不能改成st

    pst->age = 88;     // 第二种方式
    st.score = 66.6f;  // 第一种方式 

三: 结构体数组

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
struct person
{
    char name[20];
    int count;
 
}leader[3] = {{"Li", 0},
             {"Zhang", 0},
             {"Fun", 0}};
 
void main()
{
    int i, j;
    char leader_name[20];
    for(i = 1; i<= 10;i++)
    {
        scanf("%s", leader_name);
        for(j=0;j<3;j++)
            if(strcmp(leader_name, leader[j].name) == 0)
                leader[j].count ++;
    }
    printf("\n");
    for(i=0;i<3;i++)
        printf("%5s: %d\n", leader[i].name, leader[i].count);
    system("pause");
}

1.语法形式 

    struct student
    {
		int num;
		char name[20];
		char sex;
		int age;
		float score;
		char addr[30];
    };
    struct student student[3];

    也可以这样:

    struct student
    {
	    int num;
	    char name[20];
	    char sex;
	    int age;
	    float score;
	    char addr[30];
     }student[3];

2.初始化

    struct student
    {
	    int num;
        	…
    };
    struct student str[]{{…},{…},{…}};

    也可以这样:

    struct student
    {
	    int num;
	    char name[20]; 
	    char sex;     
  	    int age; 
	    float score; 
	    char addr[30];
     }stu[2]= {
		         {101,″LiLin″,′M′,18,87.5,″Beijing″},		 
                 {102,″Zhang″,′F′,19,99,″Shanghai″}
	             };

四:指向结构体类型数据的指针 

1.指向结构体类型数据指针 

1.1 概念性质  

    一个结构体变量的指针就是该结构体变量所占据的内存段的起始地址
    设置一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址

1.2 语法一般形式

    struct 结构名 *结构指针变量名
    struct stu *pstu;    //stu表示结构 pstu表示指针名称 *pstu表示指针

    结构指针变量必须先赋值后才能使用
    赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量
    例如
        struct student *pstu;
        struct student
        {
	        int num;
	        char name[20];
	        char sex;
	        int age;
	        float score;
	        char addr[30]
        } boy;

        pstu =&boy;//正确的     pstu=&student错误表示

1.3 访问的一般形式 

  (*结构指针变量).成员名        结构指针变量->成员名
        例如:
             (*pstu).num  //学生的学号
 	         pstu->num    //学生的学号
#include <stdio.h>
struct stu{
	int num;
	char *name;
	char sex;
	float score;
}boy1 = {101,"yujunwen",'M',60}


void main(){
	struct stu *pstu;
	pstu=&boy1;
	printf("学号为%d 姓名为%s",boy1.num,boy1.name);

	printf("学号为%d 姓名为%s",(*pstu).num,(*pstu).name);

	printf("学号为%d 姓名为%s",pstu->num,pstu->name);
}

2.结构体指针变量作为函数参数

将一个结构体变量的值传递给另一个函数,主要形式:

    用结构体变量的成员作参数
        【变量】用结构体变量作实参
        【变量指针】用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址穿给形参

2.1 案例一:(先用结构体变量作函数参数)

#include <stdio.h>
#include <string.h>
struct student{
	int num;
	char name[20];
	float score[3];
};
void print(struct student)
void main(){
	struct student stu;
	
	stu.num = 8;
	//下面的yujunwen出现在常量区,对应上面的char name[20]出现在栈中
	strpy(stu.name,"yujunwen");
	//stu.name ="yujunwen";
	//如果使用上面注释中的方式,需要改为char *name.将常量区的yujunwen的地址交给name 指针
	stu.score[0] = 98.5;
	stu.score[1] = 99.0;
	stu.score[2] = 99.5;
	print(stu); 
}
void printf(struct student stu){
printf("学号为%d",stu.num);
printf("姓名为%d",stu.name);
printf("语文为%d",stu.score[0]);
printf("数学为%d",stu.score[1]);
}

2.2 案例二:(改用指向结构体变量(或数组)的指针作实参)

#include <stdio.h>
#include <string.h>
struct student{
	int num;
	char name[20];
	float score[3];
};

void print(struct student *);
void main(){
	struct student stu;
		
	stu.num = 8;
	strpy(stu.name,"yujunwen");
	stu.score[0] = 98.5;
	stu.score[1] = 99.0;
	stu.score[2] = 99.5;
	print(&stu); 
}
void printf(struct student *p){
printf("学号为%d",p->num);
printf("姓名为%d",p->name);
printf("语文为%d",p->score[0]);
printf("数学为%d",p->score[1]);
}

2.3 用结构体变量的成员作参数

例如:用 stu[1].num 或 stu[2].name 作函数实参,将实参值传给形参
用法和用普通变量作实参是一样的,属于 值传递 方式
应当注意实参与形参的类型保持一致

3.指向结构体数组指针 

程序已定义了指针 p 为指向 struct student 类型数据的变量
它只能指向一个 struct student 型的数据(p 的值是 stu 数组的一个元素的起始地址)
而不能指向 stu 数组元素中的某一成员,(即 p 的地址不能是成员地址)
    例如,下面是不对的:
        p = &stu[1].name

    此时,在 p 中存放 stu[1] 元素的 name 成员的起始地址
        p = (struct student *)&stu[1].name;
#include <stdio.h>
#inlcude <stdlib.h>
 
struct student
{
    int num;
    char name[20];
    char sex;
    int age;
};
 
struct student stu[3] = {{10101, "Li Lin", 'M', 18},
                         {10102, "Zhang Fun", 'M', 19},
                         {10103, "Wang Min", 'F', 20}};
 
int main()
{
    struct student *p;
    printf("No.    name        sex        age\n");
    for(p=stu; p<stu+3;p++)
        printf("%5d %-20s %2c %4d\n", p->num, p->name, p->sex, p->age);
    system("pause");
}





运行结果如下:
No.    name        sex      age
10101 Li Lin        M       18
10102 Zhang Fun     M       19
10103 Wang Min      F       20

五:用指针处理链表

1.链表概述

struct student
{
    int num;
    float score;
    struct student *next;
};

其中成员 num 和 score 用来存放结点中的有用数据(用户需要用到的数据)

next 是指针类型成员,它指向 struct student 类型数据(这是 next 所在结构体类型)

2.简单链表例子

#include <stdio.h>
#include <stdlib.h>
 
#define NULL 0
 
struct student
{
    long num;
    float score;
    struct student *next;
};
 
void main()
{
    struct student a, b, c, *head, *p;
    a.num = 99101; a.score = 89.5;
    b.num = 99103; b.score = 90;
    c.num = 99107; c.score = 85;//对结点的 num 和 score 成员赋值
    head = &a;//将结点 a 的起始地址赋给头指针 head
    a.next = &b;//将结点 b 的起始地址赋给 a 结点的 next 成员
    b.next = &c;
    c.next = NULL;// c 结点的 next 成员不存放其他结点地址
    p = head;//使 p 指针指向 a 结点
    do
    {
        printf("%ld %5.1f\n", p->num, p->score);// 输出 p 指向的结点的数据
        p = p->next;//使 p 指向下一结点
    }while(p != NULL);//输出完 c 结点后 p 的值为 NULL
    system("pause");
}

运行结果:

99101  89.5
99103  90.0
99107  85.0

3.建立动态链表

是指在程序执行过程中从无到有地建立起一个键表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系

#include <stdio.h>
#include <stdlib.h>
 
#define NULL 0
#define LEN sizeof(struct student)
 
struct student
{
    long num;
    float score;
    struct student *next;
};
 
struct student *create()
{
    struct student *p1, *p2, *head;
    int num;
    float score;
    int n = 0;
 
    head = NULL;
 
    p1 = p2 = (struct student *)malloc(LEN);
 
    printf("please input num and score.\n");
    scanf("%d,%f", &p1->num, &p1->score);
 
    while(p1->num != 0)
    {
        n ++;
        if(n == 1)
            head = p1;
        else
            p2->next = p1;
        p2 = p1;
        p1 = (struct student *)malloc(sizeof(struct student));
 
        printf("please input num and score.\n");
 
        scanf("%d,%f", &p1->num, &p1->score);
    }
    p2->next = NULL;
    return head;
}
 
void printlist(struct student *head)
{
    struct student *p;
    p = head;
    if(head != NULL)
    {
        do
        {
            printf("num=%d score=%f\n", p->num, p->score);
            p = p->next;
        }while(p != NULL);
    }
}
 
void main()
{
    struct student *head;
    head = create();
    printlist(head);
    system("pause");
}

3.1 打印链表

void printlist(struct student *head)
{
    struct student *p;
    p = head;

    if(head != NULL)
    {
        do 
        {
            printf("num=%d score=%5.2f\n", p->num, p->score);
            p = p->next;
        } while (p != NULL);
    }
    /* while(p -> next != NULL)
    {
        printf("num=%d score=%f\n", p->num, p->score);
        p = p->next;
    }*/
}

3.2 删除节点

struct student *delNode(struct student *head, int num)
{
    printf("delNode.\n");
    struct student *p1, *p2;
    if(head == NULL)
    {
        printf("The List is NULL.\n");
    }
    else
    {
        p1 = head;
        while(p1->next != NULL && p1->num != num)
        {
            p2 = p1;
            p1 = p1->next;
        }
        if(p1->num == num)
        {
            if(p1 == head)
                head = p1->next;
            else
                p2->next = p1->next;
        }
        else
            printf("Can not find list num.\n");
    }
    return head;
}

3.3 更新节点

struct student *update(struct student *head, int index, int num, float score)
{
    printf("update.\n");
    struct student *p;
    if(head == NULL)
    {
        printf("The List is NULL.\n");
    }
    else
    {
        p = head;
        while(p->next != NULL && p->num != index)
        {
            p = p->next;
        }
        if(p->num == index)
        {
            p->num = num;
            p->score = score;
        }
        else
            printf("Can not find list index.\n");
    }
    return head;
}

3.4 增加节点

struct student *add(struct student *head, int index, int num, float score)
{
    printf("add.\n");
    struct student *p1, *p2, *p3;
    if(head == NULL)
    {
        printf("The List is NULL.\n");
    }
    else
    {
        p1 = p2 = head;
        while(p1->next != NULL && p1->num != index)
        {
            p1 = p1->next;
            p2 = p1;
        }
        if(p1->num == index)
        {
            p3 = (struct student *)malloc(LEN);
            p3->num = num;
            p3->score = score;

            if(p2->next == NULL)
            {
                p2->next = p3;
                p3->next = NULL;
            }
            else
            {
                p3->next = p2->next;
                p2->next = p3;   
            }
        }
        else
            printf("Can not find list index.\n");
    }
    return head;
}

五:几个结构体中常用的函数memcpy() memset() bzero()

1.结构体的复制函数memcpy

在C语言中,结构体的成员如果是基本数据类型(int、char、double)可以用=号赋值

如果是字符串,字符串不是基本数据类型,可以用strcpy函数赋值

如果要把结构体变量的值赋给另一个结构体变量,有两种方法:
    1)一种是把结构体变量成员的值逐个赋值给另一个结构体变量的成员,这种方法太笨,没人使用;
    2)另一种方法是内存拷贝,C语言提供了memcpy(memory copy的简写)实现内存拷贝功能。

函数声明:

void *memcpy(void *dest, const void *src, size_t n);

memcpy(&girl2,&girl1,sizeof(struct st_girl));

    参数说明:

        src 源内存变量的起始地址。

        dest 目的内存变量的起始地址。

        n 需要复制内容的字节数。

        函数返回指向dest的地址,函数的返回值意义不大,程序员一般不关心这个返回值

2.内存空间赋值函数memset

memset 函数是内存空间赋值函数,用来给某一块内存空间进行赋值的

包含在<string.h>头文件中

函数的声明如下:

void *memset(void *s, int v, size_t n);


    s为内存空间的地址,一般是数组名或结构体的地址。

    v为要填充的值,填0就是初始化。

    n为要填充的字节数。

3.内存空间清零函数bzero

bzero函数是内存空间清零。

包含在<string.h>头文件中。

函数的声明如下:

void bzero(void *s, size_t n);

    s为内存空间的地址,一般是数组名或结构体的地址。

    n为要清零的字节数。

 如果要对数组或结构体清零,用memset和bzero都可以,没什么差别

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

刘鑫磊up

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

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

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

打赏作者

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

抵扣说明:

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

余额充值