C/C++(结构体)

结构体(struct)

从某种意义上说,会不会使用struct,如何使用struct是区别一个开发人员是否具备丰富开发经验的试金石。
处理由不同类型成员构成的构造类型,要采用结构体的方式。
定义:关键字struct.

无名结构体,一般用于定义类型相同时,定义变量;并且不会带来多余的命名。

struct{
    char name[40];
    int age;
    int num;
    float score;
}s1,s2,s3,s4;
int main() {
    struct{
        char name[40];
        int age;
        int num;
        float score;
    }s1;
    ....
    return 0;
}

有名结构体

一处定义各处使用

struct stu{
    char name[40];
    int age;
    int num;
    float score;
}
int main() {
    struct stu s1;
    struct stu s2;
    struct stu s3;
    return 0;
}

typedef

对现有类型取别名,不能创造新的类型(typerename)。
使用方法:

1.先用原类型定义变量
2.在定义前加上typedef
3.将原来的名字改成自己需要的类型名

typedef char        int8;
typedef short       int16;
typedef int         int32;
typedef long long   int64;
int main() {
    int8  i8;
    int16 i16;
    int32 i32;
    int64 i64;
    return 0;
}

别名结构体

typedef是一个常用于对结构体取别名的关键字,更好的用于结构体中

typedef struct{
    char name[40];
    int age;
    int num;
    float score;
}Stu;
int main() {
    Stu s1;
    Stu s2;
    Stu s3;
}

typedef与#define区别

typedef:构成c语言语句参与编译。
#define构成的语句在预处理阶段处理完毕

#fefine DINT int
int main() {
    DINT a,b,c;//所有声明时要替换。
}
int main() {
    char *p,q;
    printf("sizeof(p) = %d sizeof(q) = %d\n",sizeof(p),sizeof(q));//4,1
    typedef char * pChar;
    pChar a,b;
    printf("sizeof(a) = %d sizeof(b) = %d\n",sizeof(a),sizeof(b));//4,4

#define DpChar char *
    DpChar m,n;//<=>char * m,n;
    printf("sizeof(m) = %d sizeof(n) = %d\n",sizeof(m),sizeof(n));//4,1

    return 0;
}

struct的初始化

凡是基本类型,既可以在定义的时候初始化,也可以先定义再赋值。
凡是构造类型,要么在定义的时候初始化,不可以先定义再以实例化的方式赋值。
初始化是一种特殊的语法,跟赋值不等价。
成员赋值,不能已初始化的方式赋值(如数组,字符串等)

struct stu{
    char name[100];
    int num;
    char sex;
    float score;
}

int main() {
    struct stu s = {"assassin",10001,'f',99};
    //也可以这样赋值
    /*
    struct stu s;
    s.name = "assassin";//错误的,赋值不能这样写,和初始化有区别
    strcpy(s.name,"assassin");
    s.num = 1001;
    ...
    */
    return 0;
}
struct stu{
    char name[100];
    int num;
    char sex;
    float score;
}
int main() {
    struct stu s;
    scanf("%s%d %c%f\n",s.name,&s.num,&s.sex,&s.score);//其中s.name,name是个数组,数组时个特殊的指针,所以前面不用取址符。&的优先级低于"."的优先级。
    return 0;
}

struct的访问

一类是访问栈上的,一类是访问堆上的。(点乘员运算符,指向成员运算符)

#include<stdio.h>
#include<stdlib.h>
struct stu{
    char name[100];
    int num;
    char sex;
    float score;
};
int main() {
    struct stu s = {"assassin",10001,'f',99};
    struct stu *pa = &s;
    //点乘运算访问
    printf("name   = %s\n",(*pa).name);
    printf("num    = %d\n",(*pa).num);
    printf("sex    = %c\n",(*pa).sex);
    printf("score  = %f\n",(*pa).score);

    printf("======================\n");
    
    printf("name   = %s\n",s.name);
    printf("num    = %d\n",s.num);
    printf("sex    = %c\n",s.sex);
    printf("score  = %f\n",s.score);
    //足以说明*pa <=> s


//"."点乘优先级高于"*"解引用运算符
    printf("======================\n");
    //指向成员访问
    printf("name   = %s\n",pa->name);
    printf("num    = %d\n",pa->num);
    printf("sex    = %c\n",pa->sex);
    printf("score  = %f\n",pa->score);
    return 0;
}
/*
name   = assassin
num    = 10001
sex    = f
score  = 99.000000
======================
name   = assassin
num    = 10001
sex    = f
score  = 99.000000
======================
name   = assassin
num    = 10001
sex    = f
score  = 99.000000
*/

堆上的访问

typedef struct stu{
    char name[100];
    int num;
    char sex;
    float score;
}Stu;
int main() {
    Stu *pa = (Stu*)malloc(sizeof(Stu));
    strcpy(pa->name,"assassin");//赋值
    pa->num = 10001;
    pa->sex = 'f';
    pa->score = 99;

    printf("name   = %s\n",pa->name);
    printf("num    = %s\n",pa->num);
    printf("sex    = %s\n",pa->sex);
    printf("score  = %s\n",pa->score);

    free(pa);
    return 0;
}
/*
name   = assassin
num    = 10001
sex    = f
score  = 99.000000
*/

struct赋值

相同结构体类型的变量间,可以赋值。不同类型不可以。词语发基础奠定了可以用与传参和返值。

typedef struct stu{
    char name[100];
    int num;
    char sex;
    float score;
}Stu;
int main() {
    Stu s;
    Stu s2 = {"assassin",1003,'f',99};
    s = s2;
    printf("%s %d %c %f\n",s.num,s.num,s.sex,s.score);//是可以赋值的

    return 0;
}

结构体支持赋值,访问和数组一样

typedef struct _stu{
    char name[10];
}Stu;
typedef struct _array{
    int arr[10];
}Array;
int main() {
    int arr[10] = {1,2,23,4,5,6,78,};
    int arr2[10];
    arr2 = arr;//error
    Stu stu1 = {"assassin"};
    Stu stu2;
    stu2 = stu1;//正确

    Array arr3 = {1,2,23,4,5,6,7,8};
    Array arr4;
    arr4 = arr3;//和上面有区别
    for(int i = 0;i<10;i++) {
        printf("%d\n",arr4.arr[i]);
    }
}

传参和返回值的本质就是依次赋值

typedef struct _complex{
    float real;
    float image;
}MyComplex;
//实现一个函数,实现两个负数相加
MyComplex addComplex(MyComplex x,MyComplex y) {
    MyComplex t;
    t.real = x.real + y.real;
    t.image = x.image + y.image;
    return t;
}
int main() {
    MyComplex c1 = {1,2};
    MyComplex c2 = {3,4};
    MyComplex retc = addComplex(c1,c2);

    printf("(%.2f,%.2f)\n",retc.real,retc.image);

    return 0;
}
//(4.00,6.00)

C语言对于数组的处理就是指针,对于结构体也推荐传指针。(即使有很大的数据,只传4个字节,而且效率提高了很大)

typedef struct _complex{
    float real;
    float image;
}MyComplex;
//实现一个函数,实现两个负数相加
MyComplex addComplex(MyComplex *px,MyComplex *py) {
    MyComplex t;
    t.real = px->real + py->real;
    t.image = px->image + py->image;
    return t;
}
int main() {
    MyComplex c1 = {1,2};
    MyComplex c2 = {3,4};
    MyComplex retc = addComplex(&c1,&c2);

    printf("(%.2f,%.2f)\n",retc.real,retc.image);

    return 0;
}
//效率大大提高

结构体数组

结构体数组的本质:是一维数组,只不过是一维数组中的每一个个成员又是结构体。

typedef struct _stu{
    int num;
    char name[100];
    char sex;
    float score;
}Stu;
int main() {
    Stu s[] = {{1001,"assassin",'f',99},{1002,"Wunworld",'f',89},{1003,"seafwg"},{1004,"intelwisd"}};
    for(int i = 0;i < sizeof(s)/sizeof(*s);i++) {
        printf("num    = %d\n",s[i].num);
        printf("name   = %s\n",s[i].nam);
        printf("sex    = %c\n",s[i].sex);
        printf("score  = %f\n",s[i].score);
    }
}

实例投票:现有三位侯选人员,侯选人包含名字和选票数两项,现在 10 人对其投票,每个人限投票一次,投票完毕,打印投票结果,如有名字打错,算作弃权处理。

typedef struct _candidate{
    char name[30];
    int voteCount;
}Candidate;
void disCandidate(Candidate *c,int n,int m);
int main() {
    //初始化候选人
    Candidate can[3] = {
        {"assassin",0},
        {"wunworld",0},
        {"seafwg",0}
    };    
    int count = 0;
    char buf[1024];
    //是个人选举
    for(int i = 0;i < 10;i++) {
        printf("请你选举自己心中的候选人");
        scanf("%s",buf);
        int flag = 1;
        for(int j = 0;j < 3;j++) {            
            if(!strcmp(buf,can[j].name)) {
                can[j].voteCount++;
                flag = 0;
            }
        }
        if(flag != 0) {
            count++;
        }
    }
    disCandidate(can,3,count);
}
//显示选举后的选票人
void disCandidate(Candidate *c,int n,int count) {
    for(int i = 0;i < n;i++) {
        printf("Name:10%s VoteCount:%2d\n",c[i].name,c[i].voteCount);
    }
    printf("the give up people: %d\n",count);
    //进行排序
    int idx = 0;
    for(int i = 0;i < n;i++) {
        if(c[i].voteCount > c[idx].voteCount) {
            idx = i;
        }
    }
    printf("恭喜%s获得了选举!\n",c[idx].name);
}

结构体的嵌套

struct _birth {
    int year;
    int month;
    int day;
};

typedef struct _stu {
    char name[20];
    int num;
    char sex;
    float score;
    struct _birth birth;
    //<=>
    /*struct _birth {
        int year;
        int month;
        int day;
    }birth;*/
}Stu;
int main() {
    Stu s = {"assassin",10001,'f',99,{1992,04,15}};
    printf("name  = %s\n",s.name);
    printf("year  = %d\n",s.birth.year);
    printf("month  = %d\n",s.birth.month);
    printf("day    = %d\n",s.birth.day);
}
/*
name  = assassin
year  = 1992
month  = 4
day    = 15
*/

结构体类型的大小

类型本身不占空间,类型产生的变量才占空间
结构体中的每个成员的地址均是可以获得的。

typedef struct _staff{
    char sex;
    int age;
}Staff;

int main() {
    Satff s;
    printf("sizeof(Staff) = %d\n",sizeof(Staff));
    printf("sizeof(s)     = %d\n",sizeof(s));
    
    printf("&s        = %p\n",&s);
    printf("&s.sex    = %p\n",&s.sex);
    printf("&s.age    = %p\n",&s.age);
    
    return 0;
}
/*
sizeof(Staff) = 8
sizeof(s)     = 8
&s        = 0061FEA8
&s.sex    = 0061FEA8
&s.age    = 0061FEAC
*/
typedef struct _staff{
    char sex;
    int age;
    short like;
}Staff;

int main() {
    Staff s;
    printf("sizeof(Staff) = %d\n",sizeof(Staff));
    printf("sizeof(s)     = %d\n",sizeof(s));
    
    printf("&s        = %p\n",&s);
    printf("&s.sex    = %p\n",&s.sex);
    printf("&s.age    = %p\n",&s.age);
    printf("&s.like   = %p\n",&s.like);
    
    return 0;
}
/*
sizeof(Staff) = 12
sizeof(s)     = 12
&s        = 0061FEA4
&s.sex    = 0061FEA4
&s.age    = 0061FEA8
&s.like   = 0061FEAC
*/

为什么结构体里面的char,short是4个字节
sex后面空了三个字节,然后去填age,like后面空了两个字节,sex与age直接空了三个字节。like应该填到那个位置。

内存对齐

一个成员变量需要多少个机器周期去读的现象,称为内存不对齐。x实现内存对齐主要原因是牺牲空间换取时间的方法。

对齐规则

x86linux默认#pragma(4),window默认#pragma pack(8)。linux最大支持4字节对齐。
方法:

1.取pack(n)的值(n=1 2 4 8--),去结构体中类型最大值m,两者取小即为外对齐 大小Y=(m<n?m:n);
2.将每一个结构体的成员大小与Y比较取较小者为X作为内对其的大小。
3.所谓按X对齐,即为地址(设起始地址为0)能被X整除的地方开始存放数据。
4.外部对其原则是依据Y的值(Y的最小整数倍),进行补空操作。

例如:以上述为例

typedef struct _staff{
    char sex;
    short num;
    int age;
}Stu;
1.外对齐:pack(8)默认为8 n=8,则按照1,m=4,即Y=4,----外对齐。(sex与age直接空了三个字节)
2.内对齐:上述Y=4,与成员大小1,2,4比较取小值(x=1,2,4)---内对齐
3.按照X对齐:
0-1 2-4 4-8-----8(外侧是Y的最小整数倍补齐) 
    例如:成员大小1,4,2比较后(X=1,4,2)则:
    0-1 4-8 8-10(10不是Y的最小整数倍取12)---大小为12
    eg:pack(1), n=1,m=4,Y=1,
    成员:1 4 2,则X=1 1 1,
    外对齐:0-1 1-5 5-7(7是1的倍数)---大小为7

eg:

#pragma pack(4)
typedef struct _stu{
    char sex;
    double num;
    int age;
}Stu;
int main() {
    printf("sizeof(Stu) = %d\n",sizeof(Stu));
}
/*
sizeof(Stu) = 16
sizeof(s) = 16
*/
/*pack(4),n=4,m=8--->Y=4
成员:1 8 4与Y比较取小者X=1 4 4
0-1 4(能与X整除放成员个大小)-12 12-16-----大小为16
*/

结构体注意事项

typedef struct _stu{
    char name[20];
    int score;
}Stu;
int main() {
    Stu s;
    strcpy(s.name,"bob");
    s,score = 99;
}

typedef struct _stu{
    char *name;
    int score;
}Stu;//只有8个字节,name未初始化
int main() {
    Stu s;
    strcpy(s.name,"bob");//Error 会挂机
    s.name = (char*)malloc(100);//解决办法放在堆上
    strcpy(s.name,"jim");
    printf("name = %s score = %d\n",s.name,s.score);
    free(s.name);//必须要释放
    s.score = 99;
}

申请空间的时候从外至内,释放空间的时候从内之外。

typedef struct _stu{
    char *name;
    int score;
}Stu;//只有8个字节,name未初始化
int main() {
    Stu *ps = (Stu *)malloc(sizeof(Stu));//ps实在栈上申请了8,结构体在堆上,没有初始化。并没有指向。解决办法,再次申请一个堆空间存放地址
    ps->name = (char*)malloc(100);
    strcpy(ps->name,"assassin");
    ps->score = 200;
    printf("name = %s score = %d\n",ps->name,ps->score);
    free(ps->name);//
    free(ps);
    return 0;
}

数据类型:内存是以字节为单位进行线性编址的硬件基础,对内存进行格式化。

posted @ 2018-01-21 23:44  seafwg  阅读(392)  评论(0编辑  收藏  举报