(四)结构体、共用体、枚举、字节对齐

一、结构体
1、概念:
结构体是复合的数据类型,是不同类型 数据的集合
在C语言中没有具体的结构体类型,但是C语言给出了定义结构体类型的语法格式,所以结构体也成为自定义类型
2、结构体类型定义的语法格式
struct  <结构体标签>{
成员类型  成员名;
成员类型  成员名;
....    ....
成员类型  成员名;
};
例如:定义一个学生的类型
struct student{
int sno;
char name[20];
int age;
float score;
};
3、结构体变量的定义
1》常规定义:
struct student{
int sno;
char name[20];
int age;
float score;
};
struct student  st1;  //st1为结构体变量
2》与类型同时定义
struct student{
int sno;
char name[20];
int age;
float score;
}st2; /st2为结构体变量
3》直接定义
struct {
int sno;
char name[20];
int age;
float score;
}st3; /st3为结构体变量
4、结构体变量的初始化
1》完全初始化

按顺序给结构体中的每个成员赋值,例如

struct student  st1 = {1001,"张飞",25,98.1}; 
2》部分初始化

按顺序给结构体中的前若干个成员赋值,例如:

struct student  st2 = {1001,"张飞"};  	//st2为结构体变量
3》指定成员初始化
不按顺序给结构体中某一些成员赋值,例如:

struct student  st3 = {.name="张飞",25,.sno = 1002};  	//st2为结构体变量
5、结构体变量的使用:
1》赋值:
除了初始化之外,不能像 .name="张飞",25,.sno = 1002 这样赋值,例如
struct student st;
st = {100,"abc",22,95.7};    //错误
但是:同类型的结构体变量之间可以相互赋值,例如:
struct student st;
//st = {1001,"张飞",25,98.1};   //错误的
st = st1;   //正确的
另外,也可以分别给每一个成员赋值,例如:
st.sno = 1001;
strcpy(st.name,"赵云");
st.age = 26;
st.score = 95.7;

2》打印:
不能整体打印,必须分别打印每一个成员,例如:
printf("st1:%d,%s,%d,%f\n",st1.sno,st1.name,st1.age,st1.score);
printf("st2:%d,%s,%d,%f\n",st2.sno,st2.name,st2.age,st2.score);
printf("st3:%d,%s,%d,%f\n",st3.sno,st3.name,st3.age,st3.score);
printf("st:%d,%s,%d,%f\n",st.sno,st.name,st.age,st.score);
3》传参:
1)值传递

void fun1(struct student st)
{
	printf("st:%d,%s,%d,%f\n",st.sno,st.name,st.age,st.score);
	st.sno = 1001;
	strcpy(st.name,"赵云");
	st.age = 26;
	st.score = 95.7;
}
2)地址传递

void fun2(struct student * pst)
{
	printf("in_fun2:%d,%s,%d,%f\n",(*pst).sno,(*pst).name,(*pst).age,(*pst).score);
	(*pst).sno = 1001;
	strcpy((*pst).name,"赵云");
	(*pst).age = 26;
	(*pst).score = 95.7;
}
int main(void)
{
	struct student  st1 = {1001,"张飞",25,98.1};  	//st2为结构体变量
	
#if 0
	fun1(st1);   //值传递
	printf("st1:%d,%s,%d,%f\n",st1.sno,st1.name,st1.age,st1.score);
#else
	fun2(&st1);  //地址传递
	printf("st1:%d,%s,%d,%f\n",st1.sno,st1.name,st1.age,st1.score);
#endif
	return 0;
}
6、结构体数组
元素为结构体类型的数组称为结构体数组,例如:
#include <stdio.h>
#include <string.h>

//结构体类型定义
struct student{
	int sno;
	char name[20];
	int age;
	float score;
};

int main(void)
{
	struct student st[50] = {{1001,"张三",25,99},{1002,"李四",22,95},{1003,"王五",26,97}};
	int i;

	for(i = 0; i < 3; i++)
		printf("%d,%s,%d,%f\n",st[i].sno,st[i].name,st[i].age,st[i].score);
	
	return 0;
}
7、结构体指针
指向结构体变量的指针称为结构体指针,例如:

#include <stdio.h>

//结构体类型定义
struct student{
	int sno;
	char name[20];
	int age;
	float score;
};

int main(void)
{
	struct student st = {1001,"张三",25,99};
	struct student *pst;

	pst = &st;
	printf("%d,%s,%d,%f\n",st.sno,st.name,st.age,st.score);
	printf("%d,%s,%d,%f\n",(*pst).sno,(*pst).name,(*pst).age,(*pst).score);
	printf("%d,%s,%d,%f\n",pst->sno,pst->name,pst->age,pst->score);
	
	return 0;
}
8、结构体成员类型:
1》基本类型:
struct A{
int a;
float b;
};
2》数组:
struct A{
int a[5];
float b;
};
3》结构体:
#include <stdio.h>

struct A{
	int a;
	float b;
};

#if 0
struct B{
	int x;
	struct A y;   //用已有的结构体类型定义成员变量
};
#else
struct B{
	int x;
	//定义一个新的结构体类型同时定义该结构体变量
	struct {
		int a;
		float b;
	}y;
};
#endif

int main(void)
{
	struct B st = {100,{123,34.56}};

	printf("%d,%d,%f\n",st.x,st.y.a,st.y.b);
	return 0;
}
4》指针:

#include <stdio.h>

struct A{
	int a;
	int *p;
};

int main(void)
{
	int x = 123;
	struct A st = {100,&x};
	printf("%d,%d\n",st.a,*st.p);
	return 0;
}
5》结构体指针:
例1:

#include <stdio.h>

struct A{
	int a;
	float b;
};

struct B{
	int x;
	struct A *pst;
};

int main(void)
{
	struct A sta = {123,3.5};
	struct B stb = {100,&sta};

	printf("%d\n",stb.x);
	printf("%d,%f\n",stb.pst->a,stb.pst->b);
	return 0;
}
例2:

#include <stdio.h>

typedef struct B{
	int x;
	struct B *pst;
}ST;

typedef struct node{
	int data;
	struct node *next;
}list,*plist;


int main(void)
{

	ST  st1 = {100,NULL};
	ST  st2 = {200,&st1};
	ST  st = {300,&st};
	list nd = {1000};
	plist pnd = &nd;

	printf("pnd->data = %d\n",pnd->data);
	printf("%d\n",st2.x);
	printf("%d\n",st2.pst->x);
	printf("%d,%d\n",st.x,st.pst->x);
	return 0;
}
二、共用体
也叫联合体,是一种复合数据类型,C语言中也没有该类型的具体定义,但是给出了定义该类型的语法格式
也是由不同类型数据组成,但是共用体中的多个数据共同使用同一块内存空间。
1、共用体类型的定义
union <共用体标签>{
成员类型  成员名;
成员类型  成员名;
....    ....
成员类型  成员名;
};
例如:定义一个共用体的类型
union student{
int sno;
char name[20];
int age;
float score;
};
2、共用体变量的定义
1》常规定义:
union student{
int sno;
char name[20];
int age;
float score;
};
union student  st1;  //st1为共用体变量
2》与类型同时定义
union student{
int sno;
char name[20];
int age;
float score;
}st2; /st2为共用体变量
3》直接定义
union {
int sno;
char name[20];
int age;
float score;
}st3; /st3为共用体变量


3、使用
1》赋值和打印与结构体类似,例如:

#include <stdio.h>
#include <string.h>

//共用体类型的定义
union A{
	short a;
	char b[3];
	char c[5];
};

int main(void)
{
	union A u1;
	printf("sizeof(union A) = %d\n",sizeof(union A));
	printf("sizeof(u1) = %d\n",sizeof(u1));
	printf("sizeof(u1.a) = %d\n",sizeof(u1.a));
	printf("sizeof(u1.b) = %d\n",sizeof(u1.b));
	printf("sizeof(u1.f) = %d\n",sizeof(u1.c));

#if 0
	/*************************************/
	u1.b = 12.34;
	strcpy(u1.c,"hello");
	u1.a = 123;
	printf("u1.a = %d\n",u1.a);
	printf("u1.b = %f\n",u1.b);
	printf("u1.c = %s\n",u1.c);
	/*************************************/
	strcpy(u1.c,"hello");
	u1.a = 123;
	u1.b = 12.34;
	printf("u1.a = %d\n",u1.a);
	printf("u1.b = %f\n",u1.b);
	printf("u1.c = %s\n",u1.c);
	/*************************************/
#endif
#if 0
	u1.a = 123;
	u1.b = 12.34;
	strcpy(u1.c,"hello");
	printf("u1.a = %d\n",u1.a);
	printf("u1.b = %f\n",u1.b);
	printf("u1.c = %s\n",u1.c);
#endif

	return 0;
}
总结:
1》共用体的成员共同使用同一块内存空间
2》在程序中,某一时刻只有一个成员的值有效,即最后被赋值的成员有效
实例:
判断机器的字节序?

#include <stdio.h>
#include <string.h>

union A{
	unsigned int word;
	unsigned char byte;
};

int main(void)
{
	union A u1;

	u1.word = 0x12345678;
	if(u1.byte == 0x12)
		printf("大端序!\n");
	else
		printf("小端序!\n");

	return 0;
}
三、枚举类型
该类型的本质是整型,为了提高程序额可读性,同时为了人为的程序中的一些数据规定一个取值的范围,而设计出这样的枚举类型,例如
在程序中表示一周的每一天,或者一年中的每个月都可以定义成枚举类型
1、枚举类型的定义
enum <枚举标签>{
枚举常量,
枚举常量,
.....
枚举常量
};
例如:
enum phone_color{
white, //枚举常量
black, //枚举常量
golden //枚举常量
};
默认情况下,枚举常量的值,从第一个开始依次是:0,1,2,3,....
enum phone_color   color;    //color为枚举变量,一般在程序中取枚举常量的值

#include <stdio.h>
#include <string.h>

enum phone_color{
	white,			//枚举常量
	black,			//枚举常量
	golden			//枚举常量
};

enum A{
	a1 = 100,
	a2,
	a3,
	a4 = 99,
	a5,
	a6	
};

struct phone{
	float value;
	enum phone_color color;
};

int main(void)
{
	printf("white : %d\n",white);
	printf("black: %d\n",black);
	printf("golden : %d\n",golden);
	
	printf("a1 = %d\n",a1);
	printf("a2 = %d\n",a2);
	printf("a3 = %d\n",a3);
	printf("a4 = %d\n",a4);
	printf("a5 = %d\n",a5);
	printf("a6 = %d\n",a6);
	
	struct phone  apple = {5000,white};
	return 0;
}
四、地址对齐
1、自然对齐
在数据分配空间时,如果该数据的起始地址能够被它的长度整除,则就是自然对齐
2、数据的M值
如果该数据的长度小于机器字长,则M值为该数据的长度
如果该数据的长度大于等于机器字长,则M值为机器字长。
3、适当对齐
在数据分配空间时,如果该数据的起始地址能够被它的M值整除,则就是适当对齐
4、不同类型数据的对齐
1》基本类型数据
按M值适当对齐
2》数组:
按元素的M值对齐
4》共用体:
按共用体变量的M值对齐,即:成员中M值最大的
5》结构体:
按结构体变量的M值对齐,即:成员中M值最大的

五、LinuxC语言中的一些特殊函数
1、递归函数
如果一个函数体内部,调用它自己,则该函数称为递归函数,例如:

#include <stdio.h>
#if 0
void fun(int x,int y)
{
	printf("%d\n",x+y);
}

void test(void)
{
	int a = 3,b = 5;
	fun(a,b);
}
#else
void test(int i)
{
	printf("i = %d\n",i++);
	if(i < 5)
		test(i);
}
#endif

int fun(int n)
{
	if(n == 1)
		return 1;
	else if(n > 1)
		return n*fun(n-1);
	else
		return 1;
}
int main(void)
{
	int n;
	scanf("%d",&n);
	printf("%d\n",fun(n));
	return 0;
}
2、变参函数
调用时,可以传不同类型的参数或者传不同个数的参数,这样的函数称为变参函数。
 int printf(const char *format, ...);
#include <stdio.h>
#include <stdarg.h>

void myprint(int n,...)
{
	va_list p;
	int i,t;

	va_start(p,n);  //对p初始化
	for(i = 0; i < n; i++){
		t = va_arg(p,int);
		printf("%d\t",t);
	}
	printf("\n");

	va_end(p);

}

int main(void)
{
	myprint(5,1,2,3,4,5);
	myprint(5,111,222,333,444,555);
	myprint(10,1,2,3,4,5,6,7,8,9,10);
	return 0;
}
3、回调函数
指的是函数调用的一种方式,简单来说:间接调用的函数称为回调函数,例如:


4、内联函数









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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值