精华内容
下载资源
问答
  • 本篇博文主要介绍结构体函数和指针的应用 结构体(struct),主要分三个部分 建立结构声明:描述结构如何组合的主要方法,创建一个模板 例如:struc...

    本篇博文主要介绍结构体、函数和指针的应用

    • 结构体(struct),主要分三个部分

      • 建立结构声明:描述结构如何组合的主要方法,创建一个模板
        • 例如:struct book {
          char title[MAX];
          char author[MAX];
          float value;
          };
        • 这就是一个结构声明,它并没有创建一个实际的数据对象,而是描述了组成这类对象的元素。因为只是声明所以就没有占用内存。
      • 定义结构变量: struct book library;
        • 编译器使用book为模板为该变量分配空间

        • struct book library;是以下声明的简化

        • struct book {
          char title[MAX];
          char author[MAX];
          float value;
          } library;

        • 可以将声明结构和定义结构合并一步:

        • struct {
          char title[MAX];
          char author[MAX];
          float value;
          }library;

      • 初始化结构:
        • struct book library = {
          “C language”,
          “Ronny”,
          2.0
          };
        • 访问结构成员:用结构运算符(.)
        • library.value = 2.0
        • 也可以单独给某个成员赋值:
        • struct book library = {
          .value=2.0
          };
    • 指向结构的指针,声明和初始化结构指针

      • struct book {
        char title[MAX];
        char author[MAX];
        float value;
        };

      • struct book * red;这个声明不是建立一个新的结构,而是意味着指针red可以指向任何有book类型的结构

      • struct book my_book;
        red = &my_book;
        和数组不同,结构体的名字不是该结构的地址,必须使用&运算符

      • 用指针访问成员,运算符(->)

      • my_book.value==(*red).value==red->value

    • 函数和指针

      • 函数是有地址的,因为函数的机器语言实现是由载入到内存的代码组成。指向函数的指针中保存着函数代码起始处的地址
      • 当声明一个数据指针时,必须声明它指向的数据的类型。当声明一个函数指针时,必须声明它指向的函数类型。要指定函数类型,就要指出函数的返回类型以及函数的参量类型。
      • void ToUpper (char *)
      • void ( * pt) (char * )
        从这个声明中看出,第一个圆括号将运算符*和pt结合在一起,这意味着pt是一个指向函数的指针。这样( * pt)是一个函数,并使(char * )作为该函数的参量列表,void作为返回类型
      • 举例几个关于函数指针和函数名之间赋值的例子:
        • void ToUpper (char *);
          void ToLower (char *);
          int round (double);
          void ( * pt) (char *);
          pt = ToUpper ; //合法,ToUpper 是函数ToUpper ()的地址
          pt = ToLower;//合法,ToLower 是函数ToLower ()的地址
          pt = round ;//无效,round是错误类型的函数
          pt = ToUpper ( );//无效ToUpper ( )不是地址
    • 看下实际程序中是如何运用的

    #include<stdio.h>
    #include<stdlib.h>
    
    //声明结构体class1 class是关键字 需回避 
    struct class1{
    	unsigned char name;
    	unsigned char number;
    };
    
    //声明结构体school,注意里面还嵌套其他结构体
    struct school
        struct class1 east;
    	struct class1 west;
    };
    
    //定义函数,入参是结构体school指针
    int struct_fun(struct school *sh)
    {
    	sh->east.name=1;
    	sh->west.number=2;
    	printf("In struct_fun;\n");
    	return 0;
    }
    //定义一个函数,也是常说的回调函数
    int callback_fun(int z,int h)
    {
    	int k=0;
    	printf("In callback_func\n");
    	k=z+h;
    	return k;
    }
    
    //定义函数,参数是函数指针
    int add_func(int (*pt_fun)(int x,int y),int g,int h)
    {
    	int j=0;
    	printf("In add func\n");
    	//传进来的函数指针的使用方法,有两种都可以
    	//j=pt_fun(g,h);
    	j=(*pt_fun)(g,h);
    	return j;
    }
    
    int main()
    {
    	//定义结构school变量
    	struct school cqut;
    	unsigned int a=20,b=30,c=0;
    	//传入结构体地址作为参数
    	struct_fun(&cqut);
    	printf("cqut.east.name %d\tcqut.west.number %d\n",cqut.east.name,cqut.west.number);
    	//函数指针是入参,回调函数
    	c=add_func(callback_fun,a,b);
    	printf("Callback %d\n",c);
    	
    }
    
    展开全文
  • 一、结构体与函数参数结构体作函数参数可分为传值与传指针。1.传值时结构体参数会被拷贝一份,在函数体内修改结构体参数成员的值实际上是修改调用参数的一个临时拷贝的成员的值,这不会影响到调用参数。在这种情况...

    一、结构体与函数参数

    结构体作函数参数可分为传值与传指针。

    1.传值时结构体参数会被拷贝一份,在函数体内修改结构体参数成员的值实际上是修改调用参数的一个临时拷贝的成员的值,这不会影响到调用参数。在这种情况下,由于涉及到结构体参数的拷贝,程序空间及时间效率都会受到影响,所以这种方法基本不用。

    例如:

    typedef struct tagSTUDENT{

    char name[20];

    int age;

    }STUDENT;

    void fun(STUDENT stu)

    {

    printf(“stu.name=%s,stu.age=%d/n”,stu.name,stu.age);

    }

    2.传指针时直接将结构体的首地址传递给函数体,在函数体中通过指针引用结构体成员,可以对结构体参数成员的值造成实际影响。这种用法效率高,经常采用。

    例如:

    typedef struct tagSTUDENT{

    char name[20];

    int age;

    }STUDENT;

    void fun(STUDENT* pStu)

    {

    printf(“pStu->name=%s,pStu->age=%d/n”,pStu->name,pStu->age);

    }

    二、结构体与函数返回值

    对于某些版本的C语言编译器,返回值仅能为基本数据类型如int、char以及指针,因此结构体作为一种组合数据类型,不能以值的方式返回,而在有些版本的C编译器中又可以直接返回结构体变量

    ,在C++中也是可以直接返回结构体变量的。

    直接返回结构体变量示例如下;

    typedef struct tagSTUDENT{

    char name[20];

    int age;

    }STUDENT;

    STUDENT fun();

    int _tmain(int argc, _TCHAR* argv[])

    {

    STUDENT p=fun();

    printf("p.name=%s",p.name);

    return 0;

    }

    STUDENT fun()

    {

    STUDENT stu;

    stu.age=18;

    strcpy(stu.name,"xiaoming");

    return stu;

    }

    以指针方式返回结构体示例如下:

    typedef struct tagSTUDENT{

    char name[20];

    int age;

    }STUDENT;

    STUDENT* fun()

    {

    STUDENT* p=malloc(sizeof(STUDENT));

    p->age=18;

    strcpy(p->name,"xiaoming");

    return p;

    }

    关于结构体,看内核又遇到了,关于赋值中存在·的奇怪用法,在网上没有找到答案,却把以前一直弄的比较模糊的对齐问题给翻出来了。如下为转发内容:

    有人给对齐原则做过总结,具体在哪里看到现在已记不起来,这里引用一下前人的经验(在没有#pragma pack宏的情况下):

    原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。

    原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct

    a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)

    原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。

    这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。

    例1:struct{

    short a1;

    short a2;

    short a3;

    }A;

    struct{

    long a1;

    short a2;

    }B;

    sizeof(A) = 6; 这个很好理解,三个short都为2。

    sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。

    例2:struct A{

    int a;

    char b;

    short c;

    };

    struct B{

    char b;

    int a;

    short c;

    };

    sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。

    sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。

    深究一下,为什么是这样,我们可以看看内存里的布局情况。

    a b c

    A的内存布局:1111, 1*, 11

    b a c

    B的内存布局:1***, 1111, 11**

    其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。

    B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。

    再看一个结构中含有结构成员的例子:

    例3:struct A{

    int a;

    double b;

    float c;

    };

    struct B{

    char e[2];

    int f;

    double g; short h;

    struct A i;

    };

    sizeof(A) = 24;

    这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。

    sizeof(B) = 48; 看看B的内存布局。

    e f g h i B的内存布局:11* *, 1111, 11111111, 11 * * * * *

    *, 1111* * * *, 11111111, 1111 * * * *

    i其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。

    以上讲的都是没有#pragma pack宏的情况,如果有#pragma

    pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) =

    16; sizeof(B) = 32;

    有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。

    a b c

    A的内存布局:1111, 11111111, 1111

    e f g h i B的内存布局:11, 1111, 11111111, 11

    , 1111, 11111111, 1111

    那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。

    还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned

    int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。

    使用位域的主要目的是压缩存储,其大致规则为: 1)

    如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止; 2)

    如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍; 3)

    如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式; 4) 如果位域字段之间穿插着非位域字段,则不进行压缩; 5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。

    还是让我们来看看例子。

    例4:struct A{ char f1 : 3; char f2 : 4; char f3 : 5; };

    a b c

    A的内存布局:111, 1111 *, 11111 * * *

    位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。

    例5:struct B{ char f1 : 3; short f2 : 4; char f3 : 5; };

    由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。

    例6:struct C{ char f1 : 3; char f2; char f3 : 5; };

    非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。

    考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。

    最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间

    展开全文
  • 函数内实现malloc的方式 1-使用指针作为函数返回值的方式 dataStc *func1() { dataStc *p; p = (dataStc *)malloc(sizeof(dataStc)); p->size = 6; return p; } 2-使用二级指针的方式 void func2(dataStc ...

    在函数内实现malloc的方式

    1-使用指针作为函数返回值的方式

    dataStc *func1()
    {
        dataStc *p;
        p = (dataStc *)malloc(sizeof(dataStc));
        p->size = 6;
        return p;
    }
    

    2-使用二级指针的方式

    void func2(dataStc **p)
    {
        *p = (dataStc *)malloc(sizeof(dataStc));
        (*p) -> size = 8;
    }
    

    运行效果

    int main(int argc, char const *argv[])
    {
        dataStc *p1 = NULL;
        dataStc *p2 = NULL;
        p1 = func1();
        func2(&p2);
        printf("p1->size:%d\n", p1->size);
        printf("p2->size:%d\n", p2->size);
        if (p1)
        {
            printf("p1->size:%d\n", p1->size);
        }
        else
        {
            printf("p1=NULL\n");
        }
    
        if (p2)
        {
            printf("p2->size:%d\n", p2->size);
        }
        else
        {
            printf("p2=NULL\n");
        }
        
        
        return 0;
    }
    
    /*
    p1->size:6
    p2->size:8
    */
    

    在函数内释放指针的地址空间

    1-使用一级指针释放结构体

    void func_free1(dataStc *p)
    {
    	free(p);
    	// p = NULL; 这里的p是形参无法修改到指针的值,可以修改指针
    }
    

    所以使用一级指针可以释放掉内存地址但是不可以改变指针变量的值,应为在函数内p相当于一个形参

    2-使用二级指针释放结构体

    应为二级指针是指向指针的指针所以形参使用二级指针可以改变指针的值

    void func_free2(dataStc **p)
    {
    	free(*p);
    	*p = NULL;
    }
    

    运行结果

    int main(int argc, char const *argv[])
    {
        dataStc *p1 = NULL;
        dataStc *p2 = NULL;
        
        printf("p1 =:%p\n", &p1);
        printf("p2 =:%p\n", &p2);
        
        p1 = func1();
        func2(&p2);
        
        printf("p1->size:%d\n", p1->size);
        printf("p2->size:%d\n", p2->size);
    
        func_free1(p1);
        func_free2(&p2);
    
        if (p1)
        {
            printf("p1->size:%d\n", p1->size);
        }
        else
        {
            printf("p1=NULL\n");
        }
    
        if (p2)
        {
            printf("p2->size:%d\n", p2->size);
        }
        else
        {
            printf("p2=NULL\n");
        }
        
        printf("p1 =:%p\n", &p1);
        printf("p2 =:%p\n", &p2);    
        return 0;
    }
    
    /*
    p1 =:0x7fffeeabf798
    p2 =:0x7fffeeabf7a0
    p1->size:6
    p2->size:8
    p1->size:0
    p2=NULL
    p1 =:0x7fffeeabf798			p1被释放但是任然指向这个地址
    p2 =:0x7fffeeabf7a0
    
    
    */
    

    总结

    指针在被释放之后仍然指向之前的地址如果不小心操作会导致段错误。所以在释放之后最好对指针变量进行赋值为NULL。所以使用二级指针的方式可以很好的申请与释放内存并修改指针的值。

    展开全文
  • 结构体类似C++里的类,但结构体与类有一个区别,就是类默认的成员是private,结构体默认的成员是public。结构体可以定义public、private、protected,可以定义static成员。可以定义构造和析构函数。也可以继承,也...

    396d5e45dd522a345aadd59557345137.png

    九州编程

    首先,图片太小。结构体类似C++里的类,但结构体与类有一个区别,就是类默认的成员是private,结构体默认的成员是public。结构体可以定义public、private、protected,可以定义static成员。可以定义构造和析构函数。也可以继承,也可以定义虚函数123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112#include "stdafx.h"typedef struct st_father{    int m_iValueOne;    st_father(){        printf("st_father 构造\r\n");        m_iValueOne = 10;    }    virtual ~st_father(){        printf("st_father 析构\r\n");    }    void st_father_public()    {        printf("st_father_public \r\n");    }    virtual void st_virtual()    {        printf("st_father st_virtual\r\n");    }    void TEST()    {        printf("st_father TEST\r\n");    }private:    int m_iValuePrivate;      static int m_iValueStatic;      void st_father_private()    {        printf("st_father_private \r\n");    }protected:    void st_father_protected()    {        printf("st_father_protected\r\n");    }  }ST_FATHER;  typedef struct st_son: public st_father{    st_son()    {        printf("st_son 构造\r\n");    }      ~st_son()    {        printf("st_son析构\r\n");    }      virtual void st_virtual()    {        printf("st_son st_virtual\r\n");    }      void TEST()    {        printf("st_son TEST\r\n");    }}ST_SON;    int main(int argc, char* argv[]){    ST_FATHER st_father;    ST_SON st_son;      ST_FATHER* p;      printf("st_father.m_iValueOne = %d\r\n", st_father.m_iValueOne);      //private成员不能访问,结构体默认是public,类默认是private    //printf("st.m_iValuePrivate = %d\r\n", st.m_iValuePrivate);      //结构体中也可以定义虚函数,也可以定义static变量    printf("sizeof(ST_FATHER) = %d\r\n", sizeof(ST_FATHER));      //外部也是只能调用public方法    st_father.st_father_public();    //st_father.st_father_protected();    //st_father.st_father_private();    st_father.st_virtual();    st_son.st_virtual();    p = &st_father;    p->st_virtual();    p = &st_son;    p->st_virtual();      return 0;}--------------------- 作者:guoyiyan1987 来源:CSDN 原文:https://blog.csdn.net/guoyiyan1987/article/details/80433432 版权声明:本文为博主原创文章,转载请附上博文链接!

    展开全文
  • 上一节课, 我们讲解了指向结构体的指针, 这次我们来讲讲, 结构体与函数~~ 下面我们来看一个小例子: #include struct Student { int age; char *name; }; void test(struct Student s) { s.age = 30; s....
  • C语言结构体与指针和函数与指针

    千次阅读 2019-01-29 21:55:31
    本篇博文主要介绍结构体函数和指针的应用 结构体(struct),主要分三个部分 建立结构声明:描述结构如何组合的主要方法,创建一个模板 例如:struct book { char title[MAX]; char author[MAX]; ...
  • C语言结构体中如何包含函数

    万次阅读 多人点赞 2018-08-09 10:42:28
    C语言结构体里面也可以包含函数,如同类中有方法一样,但是不能通过直接放过一个函数进去,需要通过函数指针的方式,同时,关于类的构造函数与析构函数C语言表示是没有的,需要你自己手动解决这些问题。 下面讲讲...
  • 结构体是由一系列具有相同... 注意指针函数与函数指针表示方法的不同,千万不要混淆。最简单的辨别方式就是看函数名前面的指针*号有没有被括号()包含,如果被包含就是函数指针,反之则是指针函数。 要声明一个函数指
  • C语言结构体与函数

    2020-02-11 21:26:24
    C语言中,结构体内部不可定义函数; 但是定义函数时,可以是结构体类型, 如: struct student { int num; char name[10]; char sex; }; struct student search(void) { struct student xiaoming; … return ...
  • C语言结构体结构体引例结构体变量的定义结构体变量的使用结构体变量作为函数参数结构体变量的初始化 结构体 引例 输出平均分最高的学生信息 #include <stdio.h> struct student { int num; char name[10]; ...
  • 1. 函数指针  一般的函数指针可以这么定义:  int(*func)(int,int);  表示一个指向含有两个int参数并且返回值是int形式的任何一个函数指针. 假如存在这样的一个函数:  int add2(int x,int y)  {  return ...
  • C语言结构体中的函数指针与函数

    千次阅读 2011-08-29 15:42:22
    1、函数指针 一般的函数指针可以这么定义: int(*func)(int,int); 表示一个指向含有两个int参数并且返回值是int形式的任何一个函数指针. 假如存在这样的一个函数: int add2(int x,int y) { retu
  • C语言全套资料 C语言程序设计 C语言算法 C语言课件 C语言顺序程序设计,C语言数组,C语言循环控制,C语言预处理命令,C语言文件操作指针,C语言选择结构程序设计,C语言结构体与共用体,C语言文件操作,C语言函数
  • 1.结构体内部成员函数 在 C 语言中,我们不能在结构体内部定义成员变量。但在 C++ 中,结构体不仅可以拥有成员变量,还可以拥有成员方法。 2.直接初始化 在 C 语言中,我们不能直接初始化结构体成员变量。但在 ...
  • 小甲鱼结构体与共用体03 实战例题:有一个结构体变量stu,内含学生学号、姓名和3门课程的成绩。通过调用函数print将它们输出。 ①先用结构体变量作函数参数: //先用结构体变量作函数参数 #include <stdio.h>...
  • 在C++中除了类中可以有构造函数和析构函数外,结构体中也可以包含构造函数和析构函数,这是因为结构体和类基本雷同,唯一区别是,类中成员变量默认为私有,而结构体中则为公有。注意,C++中的结构体是可以有析构函数...
  • C语言中有结构体的概念...在C语言结构体中只能自定义数据类型,不允许有函数,而C++中的结构体可以加入成员函数。 那么C++的结构体和C++的类有什么区别呢? 一般C++结构体中定义的成员默认是public,而类中的定义...
  • 前面我们用了C语言结构体与函数指针模拟C++类的思想。 今天我们用C语言实现Javabean功能 #include<stdio.h> typedef struct student Student; struct student{ int id; char * name; void (*setId)...
  • C语言结构体类型声明定义分离

    千次阅读 2019-09-06 09:41:14
    C语言结构体类型声明定义分离: 问题描述: 假设代码A处用到一个结构体S,但S的定义依赖于A处后续的其它类型,因此S不能简单地放到A处之前定义(比较麻烦,也不是不可,用我现在说的方法就可以) 解决方法: ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,499
精华内容 599
关键字:

c语言结构体与函数

c语言 订阅