前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >剖析c语言结构体的高级用法(一)

剖析c语言结构体的高级用法(一)

作者头像
用户6280468
发布2022-03-21 08:42:15
4770
发布2022-03-21 08:42:15
举报
文章被收录于专栏:txp玩Linux

前言

在写这篇文章之前,说实话,自身对结构体的用法,只会两点——就是点访问式和指针式访问结构体内部成员。这对一个搞底层的工程师来讲,显然实在太low了。不妨读者看到这里,可以停下来思索一下,看看自己对c语言结构体掌握了多少。下面是我这几天结合自己的学习而总结的一篇算比较全的关于结构体的用法,欢迎大家来吐槽。

正文

其实在之前的文章里面,我已经有说为啥在c语言里面要引入结构体这一概念——超详细的链表学习,这里的话,我就不再废话了,直接来点实际的。

一、结构体的各种使用方法(很全):

  • 这里的话,我以实际例子直接开干,就不过多的介绍一些非常基础的东西(有没看明白的读者可以上网查)。

1、结构体传参:

a、先来看第一个实际例子(结构体指针做形参):

代码语言:javascript
复制
# include <stdio.h>
# include <stdlib.h>

struct Student {
    char name[20];
    float fScore[3];
}student = {"dire",98.5,89.0,93.5}; //初始化结构体变量 


void Display(struct Student *pStruct)
{
     printf("------Information-------\n");
     printf("Name:%s\n",pStruct->name);
     printf("Chinese:%.2f\n",(*pStruct).fScore[0]);
     printf("Math:%.2f\n",(*pStruct).fScore[1]);
     printf("English:%.2f\n",pStruct->fScore[2]);
}


int main ()
{
        Display(&student);        //将结构体变量的首地址作为实参传入pStruct指针变量中 

        return 0;
}

演示结果:

说明:

这里我们定义了一个结构体struct student ,并且定义了结构体变量student,同时为结构体各个成员赋值,然后定义了一个函数,这个函数传参里面定义了一个结构体变量;然后我们在主函数里面把结构体变量的地址作为函数形参传入到函数里面去;然后在函数里面进行一些操作,比如正如你所见一样,都是在访问结构体成员,而且是指针式的访问(书上把"->"叫做箭头操作符),这个正如我上面开头所说,很常见,但你细心的话,你发现了一个(*pStruct).fScore[0]

居然也能够访问成功(看到这里想必你平时可能就没怎么看到这种写法了吧,哈哈哈。),这种写法也是可以的,下次见到就不要见怪 了。

b、再来看一个示例(结构体变量做形参):

代码语言:javascript
复制
   #include <stdio.h>
   typedef struct A{
            int a;
   }s;
  void fun(s b)
  {
        b.a=99;
        printf("the b.a is %d\n",b.a);
 }
 int main(void)
 {
         s c; //这里的s等价于struct A
         c.a=8;
         printf("the c.a is %d\n",c.a);

         fun(c);

         return 0;
 }

演示结果:

说明:

这里定义结构体的时候,使用关键字typedef,这个时候结构体右大括号的后面s就不是表示结构体变量了,而是给结构体struct A起了一个别名叫s仅此而已,方便你少敲几个键盘。

2、结构体数组的几种使用形式:

a、结构体数组示例:

代码语言:javascript
复制
   #include <stdio.h>
   struct{
     char *name;  //姓名
     int num;  //学号
     int age;  //年龄
     char group;  //所在小组
     float score;  //成绩
  }class[5] = {
    {"Li ping", 5, 18, 'C', 145.0},
    {"Zhang ping", 4, 19, 'A', 130.5},
    {"He fang", 1, 18, 'A', 148.5},
    {"Cheng ling", 2, 17, 'F', 139.0},
    {"Wang ming", 3, 17, 'B', 144.5}
};
int main()
{
     int i, num_140 = 0;
     float sum = 0;
     for(i=0; i<5; i++)
     {
         sum += class[i].score;
         if(class[i].score < 140) num_140++;
     }
    printf("sum=%.2f\naverage=%.2f\nnum_140=%d\n", sum, sum/5, num_140);
    return 0;
   }

演示结果:

说明:

上面的结构体数组class[5]相当于数组里面的每个一个元素是一个结构体。

b、结构体指针数组:

代码语言:javascript
复制
    #include  <stdio.h>
    typedef struct A{
         int a;
     }s,*st;
      int main(void)
      {
         s stu1,stu2;
         st array[2];
         stu1.a=2;
         stu2.a=3;
         array[0]=&stu1;
         array[1]=&stu2;
         printf("thearray[0]=%d,array[1]=%d\n",array[0]- >a,array[1]->a);

         return 0;
    } 

演示结果:

说明:

这里的用法跟指针数组用法差不多——c专题之指针---数组指针与指针数组的区别。这里你可能会说了,既然有结构体指针数组的使用,那是不是也也应该有结构体数组指针的用法,我要告诉你的是,这里我做了测试,没有成功,我这里给你看示例(因为我在网上和书上没有查到这种用法,我觉得应该是没有;如果读者看到这里有什么建议可以和我说;我这里测试了只有下标是0的时候,可以访问成功,其他下标不能成功):

代码语言:javascript
复制
  #include <stdio.h>
  struct R{
int a;
int b;
char *name;
}s[3]={{2,4,"like"},{3,5,"like"},{2,4,"like"}};
int main(void)
{
    struct R (*array)[3];
    array=&s;
    printf("the array->b is %d\n",array[0]->a);

return 0;
 }

c、结构体里面嵌套结构体数组(这里就顺便也讲了结构体嵌套的知识点了):

结构体嵌套的问题有哪些?

----结构体的自引用,就是在结构体内部,包含指向自身类型结构体的指针。

----结构体的相互引用,就是说在多个结构体中,都包含指向其他结构体的指针

结构体应该注意的问题?

----结构体定义中可以嵌套其他结构体类型的变量,不可以嵌套自己这个类型的变量。

代码语言:javascript
复制
 #include<stdio.h>
 struct E {
     int a;
 };
  struct D{
    struct D a; //A是一个结构体,A的内部还会有一个结构体,
    //以此下>去,无线循环(类似于递归函数)。在内存分配的时候,由于无限的嵌套,无法确定结构体的长度,所>以时非法的。
    struct E b;
    int value;
};
int main()
{
    return 0;
}

演示结果:

可以嵌套自己类型的指针。

代码语言:javascript
复制
 #include<stdio.h>
 struct E {
     int a;
 };
  struct D{

     struct D *c;
    struct E b;
    int value;
};
int main()
{
    return 0;
}

说明:

由于指针的长度时确定的(在32位机器上指针长度是4),所以编译器能够确定该

结构体的长度。

这个指针看似指向自身,其实不是,而是执行同一类型的不同结构。

综合应用:

代码语言:javascript
复制
#include <stdio.h>
 struct A{
int year;
int month;
int day;
 };

   struct B{
     long int num;
     struct A a;
     //    struct A *b;
     struct A array[3];
     char *name;
}s1={200,{1998,2,3},{{1999,3,4},{1999,5,6}, 
15 {1999,7,8}},"wangwu"};

  int main(void)
   {
    struct B s2;
    s2=s1;  
   printf("%ld %s %d\n",s2.num,s2.name,sizeof(int));
printf("year:%d,month:%d,day:%d\n",s2.a.year,s2.a.month,s2.a.day);
printf("year:%d,month:%d,day:%d\n",s2.array[0].year,s2.array[0].month,s2.array[0].day);
printf("year:%d,month:%d,day:%d\n",s2.array[1].year,s2.array[1].month,s2.array[1].day);
printf("year:%d,month:%d,day:%d\n",s2.array[2].year,s2.array[2].month,s2.array[2].day);
  return 0;
 }

演示结果:

说明:

在这里我们可以看到,结构体之间是可以进行赋值的,就比如s2=s1,这样就可以用结构体变量s2来继续访问结构体里面的各个成员(可以达到同样的效果的)。这里在结构体里面嵌套了结构体变量和结构体数组,用法和不嵌套的时候是一样的。下面我们关键来讲一下这个结构体里面嵌套结构体指针,该怎样来操作呢,这是要讲的重点了(读者看到这里也可以先想想该如何进行操作):

代码语言:javascript
复制
 #include <stdio.h>
  struct B{
    long int num;
    struct B*b;
 }s1={200,0};

  int main(void)
  {
struct B s2={200,&s1
};

     printf("the s2.num=%ld\n",s2.num);
     printf("(*(s2.b)).num=%ld\n",(*(s2.b)).num);

return 0;
  }

演示结果:

说明:

这里操作的话,转了一个弯来,总之还是那句话,你在使用指针的时候,记得事先一定要给它赋有效的地址值,千万不要是NULL(关于这个原因可以看这里——c专题之指针---野指针和空指针解析)。下面是我在网上看到的多嵌套的写法(大家只要了解一下就行,实际写代码不会这样搞,这样搞确实是比较恶心):

代码语言:javascript
复制
   #include<stdio.h>
   struct s1
   {
         float a;
         struct
        {
           int ba;
           int bb;

           struct
           {
               int bca;
               int (*bcb)[3];//数组指针
            } *bc;
        } b;
    };
int main( void )
{
int a[3]={3};
struct
{
    int bca;
    int (*bcb)[3];
}bc=
{
    3,
    &a
};

struct s1 s=
{
    3.14,
    {
        1,
        2,
        ( void* )&bc
    }
};
    struct s1 *p=&s;
    printf("%d\n",*(p->b.bc->bcb)[0]);
    return 0;
 }

演示结果:

3、结构体函数指针:

代码语言:javascript
复制
 #include <stdio.h>
 #include <stdlib.h>
 struct A{
int(*add)(int a,int b);//函数指针
int(*sub)(int a,int b);
int(*mult)(int a,int b); 
   };
   int test_add(int a,int b)
   {
        return (a+b);
   }
   int test_sub(int a,int b)
   {
        return (a-b);
   }
  int test_mult(int a,int b)
  {
        return (a*b);
   }
  void print_usage()
 {
        printf("打印用法\n");
 }
 int main(int argc,char **argv)
   {
        struct A s={
       .add=test_add,
       .sub=test_sub,
       .mult=test_mult,
          };
      int a=9,b=3;
         printf("a+b=%d\n",s.add(a,b));
          printf("a-b=%d\n",s.sub(a,b));
          printf("a*b=%d\n",s.mult(a,b));

           return 0;
 } 

演示结果:

说明:

用法还是和函数指针的用法一样,区别不大。

二、总结:

上面汇总了一些结构体的高级用法,有些不怎么常见,但是开阔一些眼界还是有的,哈哈。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-01-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 txp玩Linux 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 正文
    • 一、结构体的各种使用方法(很全):
      • 1、结构体传参:
      • 3、结构体函数指针:
    • 二、总结:
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档