结构体与位字段

定义

        类似于java中的bean,只不过里面的属性都是public,不需要setter,getter,并且不能赋初值。

        定义结构体使用struct关键字。如下:

struct Person{
    int age;
    char * name;
};

        该声明描述了一个由char*与int组成的结构体,并没有创建一个实际的数据对象。

        首先使用struct表示接下来的是一个结构体;后面跟的是一个可选的标记(Person),它是用来引用该结构体的快速标识,在声明该结构体变量时可以用struct Person p;形式。

        再接下来花括号中的内容是结构体的成员列表。每一个成员的类型是不固定的,可以是基本数据类型,也可以是另外一个结构体。各个成员之间用分号隔开

        花括号外面的分号表示结构体定义结束。在定义结构体的最后需要加分号

比较

        如果两个结构体的声明都不使用标识,并且使用相同的成员(成员名与类型都匹配),那么两个结构体具有相同的类型。

引用

        对于变量来说,可以通过extern来引用定义在虽的c/cpp文件中的非static变量,但它也只能是引用变量,不能用来引用结构体,如果想使用,必须将结构体的定义复制到当前文件中来参考

        当然,可以将结构体定义在一个头文件中,然后通过不同的文件引用相同的头文件,即可将结构体的定义引入到自己的文件中。

声明变量

        使用结构体声明变量时前面必须加上struct关键字。对结构体中的变量进行赋值时,可以通过.来引用。如下:

    struct Person p;//前面必须加上struct关键字,否则报错
    p.age=12;
    p.name="zhangsan";
    printf("%d %s",p.age,p.name);

        结构体本身不占用任何内存,只有当用定义的结构体声明结构体变量时,系统才会分配内存。结构体所占的内存大小为结构体中各个成员变量所占内存大小之和。如对上面的变量p执行sizeof()方法返回的结果是8,因为int占4个字节,name占4个字节。

        也可以在定义结构体的时候进行声明。如下:

struct book{
	char name[100];
	double price;
} b1;
struct {
	char name[100];
	int age;
} p1;
        第一个定义了一个标识为book的结构体,并且同时定义了一个值为b1的结构体变量,而且在别的地方可以使用struct book b2;进行再次定义。

        第二个在声明一个结构体的同时也进行了定义p1变量,但无法在别的地方使用该结构体模板。因为它没有定义标识。

        因此,如果想多次使用一个结构模板,就需要使用带有标记的形式

初始化与赋值

        可以通过.引用变量的各个字段,然后分别进行赋值。也可以将一个结构体变量直接赋值给另一个。

    struct Person p;
    p.age=12;
    p.name="zhangsan";
    struct Person p2 = p;
    printf("%d %s %p %p",p2.age,p2.name,&p2,&p);

        前两个值分别为12,zhangsan,但后两个值不相同。

        还可以直接在声明变量的时候赋值,使用{}将各个字段的值括起来。如下

struct Person{
    int age;
    char * name;
    int score;
};

int main()
{
    struct Person p={19,"lisi",10};
    printf("%d %s %d",p.age,p.name,p.score);//和变量一一对应
    return -1;
}

        在赋值时,{}内的值和成员变量一一对应。

        另外,在c99中还支持指定初始化项目,使用点与成员名来标识具体的字段。可以按照任何顺序指定初始化的项目,指定初始化字段之后的值就按顺序为下面的字段进行初始化。另外,对特定成员变量的最后一次赋值才是它的真正的值。如下:

struct book{
	char name[100];
	double price;
};
void test() {
	struct book b1 = {
			.price = 349;
			.name = "test";
			432;
	};
}
        上述先初始化了price,再初始化了name,后面又初始化了price(因为price在name后面),所以最终price的值为432,而不是349。

结构数组

        和别的数组一样定义,访问方式也一样。可以简单的将struct book理解为一种数据类型。如下:

	struct book bs[10];//定义
	bs[0].name = "name";//访问变量,并为字段赋值

结构体的嵌套

        声明结构体变量一样,只需要用struct name arg即可。

struct person {
	char first[40];
	char second[40];
};
struct book {
	char name[100];
	struct person author; //引用上面定义的结构体
	double price;
};
struct book bs = { "book.name", { "first", "second" }, 123.0 };
        在初始化时,需要按结构体初始化方式将内部结构体给初始些。引用方式为:bs.author.first。

结构指针

        指向结构体变量的指针叫结构指针。如

    struct Person p;
    p.age=12;
    p.name="zhangsan";
    struct Person * pp = &p;//结构指针,指向了结构体变量p
    printf("%d %s %d",pp->age,pp->name,sizeof(pp));//使用指针访问结构体中的属性时,使用的是->,而不是.

        有一点需要注意:使用结构指针访问结构体中的属性时,使用->,而不是点"."。上述程序的输出结果为12 zhangsan。使用结构变量名访问字段时,使用点。

        与数组不同,一个结构的名字不是该结构的地址,必须使用&取地址

复合文字

        与数组类似,结构体也可以使用复合文字进行描述。语法是:把类型名写在圆括号中,后跟一个用花括号括起来的初始化项目列表。

	(struct book){"nmame",432.01};

        其中book是一个结构体标识。

        我们知道,在声明结构体变量的时候,可以使用{}为该变量进行初始化。但,不能使用该种方式为结构体进行赋值(非初始化)。如:

	b1 = {
			"name",
			234
	};
        这段代码是错误的。但如果b1前面有结构体类型就可以,如struct book b1,这种代码是正确的。

        同时,如果使用复合文字,就可以为b1直接赋值,在{}前面加上(struct book)即可。

伸缩型数组成员

        c99中允许结构体的最后一个成员是具有特殊属性的数组:它不存在,至少应该是不立即存在;可以在代码中指定该数组的大小。有以下规则:

        1,必须是最后一个成员,且类型必须是数组。

        2,结构中必须至少有一个其他的成员。

        3,声明数组的时候,方括号中是空的。如下:

struct book {
	char name[100];
	double price;
	int test[];
};
        使用该种结构时,应该声明了一个指向struct book类型的指针,然后使用malloc()为该指针进行赋值和分配足够的空间。如:
	struct book* b1;
	b1 = malloc(sizeof(struct book)+5*sizeof(int));//分配足够的空间
	b1->test[1] = 3;
	printf("%d",b1->test[1]);
        使用malloc()分配空间时,sizeof(struct book)为book中的常规成员分配了足够的空间,使用5*sizeof(int)为结构体中的test成员分配了5个int所占的字节。

        该语句声明之后,b1中的test成员的长度就为5。

        声明之后,可以按正常的成员进行访问与修改。

说明

        1,如果结构中有一个字段为字符串,使用字符数组,而不是使用指向的指针。如下:

struct book {
	char* name;
	double price;
};
void test() {
	struct book b1;
	scanf("%s",b1.name);//这里有一个潜在的问题
}
        上述代码使用指针来存储字符串,因为指针在声明的时候不会分配用于存储其值的空间,所以name指向的位置是不固定的。当使用scanf()为b1.name赋值时,用户输入的信息就会被存储到的位置不固定,这将有可能导致程序崩溃。

        如果使用字符数组就没事,因为声明数组的时候就为该数组分配了存储空间,使用scanf()时能保证数据存储到正确的位置中。

        如果确定想使用指针,必须先使用malloc()为该成员分配内存空间。当然,在使用完毕之后必须调用free()对malloc()分配的内存进行释放。

位字段

基础

        基本数据类型的大小,都是以字节为单位的。如果只是用来表示一个是或否,其余用一个位就行了。这样就能节约内存空间。而位字段就是来实现该功能的:它可以使用位,而不是使用字节。

        位字段是一个unsigned int或者signed int中一组相邻的位,它由使用一个结构体进行声明,并为每一个字段指定一个标签,用于标识该字段占据几位。该结构体中的成员只能是int类型或unsigned int类型的。如:

struct testBit{
	unsigned int test1:1;
	unsigned int test2:2;
} bit;

        上述结构体中,test1占一个bit位,test2占两个bit位。而bit被存储在一个unsigned int大小的存储单元中,但仅有其中的3(test1占1个,test2占2个,共3个)个bit位被使用了。

说明

        1,不允许一个字段跨越两个unsigned int之间的边界,如果位字段的总位数超过unsigned int的大小,那编译器会自动将一个字段移动到下一个unsigned int中。

        2,使用宽度为0的字段将迫使下一个字段与下一个整数对齐

        3,即使一个结构中只有一个1bit位的字段,该结构的大小也与unsigned int的大小相同。

        4,可以使用未命名字段来填充或者调字段之间的位置,未命令的字段是不能使用的。如:

struct testBit{
	unsigned int test1:1;
	unsigned int      :2;
	unsigned int test3:3;
};
其中第二个字段是未命名的,那么test1与test3中间就隔着两个bit位的间隔。

        5,由于位字段存储时是按bit位进行的,而地址是按字节排列的,所以无法对位字段使用取地址符&,它本身就没有地址

        6,通常可以使用位运算符来代替位字段,因为通过位操作可以取出任何位上的值。只不过这种方法通常较为麻烦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值