C编程基础day11
1、struct 是关键字,
2、例子中struct 和Student合起来才是结构体类型
3、结构体内部定义的变量不能直接赋值。
4、结构体只是一个类型,没有定义变量前没有分配空间,没有空间就不能赋值。
struct Student
{
int age;
char name[50];
int score;
}; //必须有分号。
定义结构体变量格式仍是:类型名 变量名;例子如下:
struct Student student2; //struct别忘了,否则在C语言下编不过。
结构体变量初始化和数组一样,要使用大括号{}。
结构体只有在定义时才能初始化。
struct Student student2 ={18, "Jack", 88};
如果是普通变量使用点运算符".",使用结构体成员需要借助结构体变量来引用。
struct Student tmp;
tmp.age =19;
tmp.name = "Mike";//err 因为name是数组名,数组名是常量不能修改。
strcpy(tmp.name,"Mike");
tmp.score =99;
如果是指针变量使用->运算符。如果是指针,指针有合法指向,才能操作结构体成员。
struct Studdent *p;
p = &tmp;
p->age =19;
strcpy(p->name,"Mike");
tmpp->score =99;
任何结构体变量,都可以用.和->两种方法操作成员
&(tmp)->age=20;
(*p).age=21; //(*p)必须加括号,因为.运算符比较高。
下图中p[0]等价于*(p+0)即*p。
结构体数组 struct Student a[5];
第一种方法初始化结构体数组
//操作元素
a[0].age=18;
strcpy(a[0].name,"mike");
a[0].score=99;
//操作元素的地址
(a+1)->a[0].age=18;
strcpy((a+1)->name,"Jack");
(a+1)->score=99;
//操作元素
( *(a+2) ).age=18;
strcpy(( *(a+2) ).name,"John");
( *(a+2) ).score=99;
struct Student *p=a;
p= &(a[0]);
p[3].age=18;
strcpy(p[3].name,"mike");
p[3].score=100;
(p+4)->a[0].age=18;
strcpy((p+4)->name,"Jack");
(p+4)->score=92;
第二种方法初始化结构体数组
struct Student b[5]=
{
{18,"aaa",60},
{18,"bbb",61},
{19,"ccc",70},
{18,"ddd",69},
{18,"eee",66}
};
第三种方法初始化结构体数组
struct Student b[5]=
{
18,"aaa",60,
19,"bbb",61,
19,"ccc",76,
18,"ddd",69,
18,"eee",66
};
结构体嵌套
struct Info
{
int age;
char name[50];
};
struct Student
{
struct Info info;
int score;
}; //必须有分号。
struct Student s;
s.info.age=18;
strcpy(s.info.name, "aaa");
s.score=100;
struct Student *p = &s;
p->info.age=18;
strcpy(p->info.name, "aaa");
p->score=100;
struct Student tmp={19,"bbb",88};
同类型的两个结构体变量可以相互赋值。 和其他变量可以相互赋值性质一样。赋值后两个结构体变量尽管内容一样,但是两个结构体变量是没有关系的独立内存。
struct Student s1={18,"abc",88};
struct Student s2;
s2 =s1;
结构体打印的时候,最好是地址传递而不是值传递, 因为地址传递只传一个4字节,然后就可以打印了。 而值传递需要将结构体所有成员变量都传递,效率太低。
void fun (struct Student tmp)//值传递
{
printf("%d %s %d \n", tmp.age , tmp.name, tmp.score); //打印的调用的结构体赋值给tmp后,tmp的值。
}
void fun2 (struct Student *p) //地址传递
{
printf("%d %s %d \n", p->age , p->name, p->score);
}
打印函数继续完善,可以让打印函数设置为只读不能修改。
void fun2 (const struct Student *p) 或者 void fun2 (struct Student const *p) // 这两者const修饰的都是* 即修饰的是指向的内容能改, 而p指向的内存地址可以改。
结构体指向堆区空间
struct Student *p
p = (struct Student*) malloc(sizeof(struct Student));
if(p != NULL)
{
printf("Error \n");
}
。。。。。。
if(p!=NULL)
{
free(p);
p=NULL:
}
非法使用内存的说明, 以下例子有些编译器检测不到错误,但是后来增加成员变量的某些时候会出现段错误,让你误以为是新增加成员引起的。所以写代码时候一定要规规矩矩写,不要认为编译通过就没事。
struct Test
{
char *str;
}
struct Test obj;
strcpy(obj.str, "MikeWang"); //str是没有指针指向的,但有些系统检测不到,能编译通过,也能执行。
printf("%s \n", obj.str);
但是修改Test结构体,就会出现段错误。
struct Test
{
char *str;
int a;
int b;
int c
}
struct Test obj;
strcpy(obj.str, "MikeWang"); //str是没有指针指向的,这个时候执行的时候才出错,让你误以为是新增加的结构体成员以前你的。
printf("%s \n", obj.str);
解决结构体套一级指针可以有三种方法:
struct Student
{
int age;
char *name;
int score
};
1、指针指向文字常量区
s.name = "MikeWang"; //指针变量指向的是文字常量区保存的相关字符串的首地址。
2、指针指向栈区
char buf[100];
str.name = buf;
strcpy(str.name,"MikeWamg");
3、指针指向堆区
s.name = (char *) malloc(strlen("MikeWang")+1); //注意这里不能使用sizeoof,因为sizeof计算的是指针的长度。
if (s.name !=NULL)
{
free(s.name);
s.name = NULL:
}
3.1让整个结构体指针指向堆区时候,要注意还要再给成员指针也申请堆区空间。
struct Student *p;
p = (struct Student *) malloc (sizeof(strcut Student))'
p->name = (char *) malloc(strlen("MikeWang")+1);//如果缺少这一句,后边使用strcpy(p->name, "Mikeabcd")时候会出错,段错误。以为我们只是给整个结构体分配了堆空间,但name指针还没有指向。
释放的时候应该先释放p->name 再释放p,否则先释放p就找不到p->name了。
//先释放 p->name
if(p->name != NULL)
{
free(p->name);
p->name = NULL;
}
//再释放 p
if(p != NULL)
{
free(p);
p = NULL;
}
指针可不初始化指向,然后直接指向文字常量区,因为这时保存的是文字常量区相关字符串的首地址。 但指针不初始化指向时不能直接对它进行拷贝。
char *p;
p = "Hello"; //OK , 此时p指向文字常量区。
char *q;
strcpy(q,"hello"); //Error, 因为指针q没有指向。
共用体(联合体)的关键字为union
union Test
{
unsigned char a;
unsigned short b;
unsigned int c;
};
1、结构体大小为所有成员大小的和。
2、共用体的大小为最大的那个成员的大小。
3、共用体所有成员共用一块内存,所有成员的首地址都一样。
4、给某个成员赋值,会影响到其他成员,以小端为例(高位放高地址, 低位放低地址),如下图所示
枚举的关键字为enum
1、里面的成员是一个标示符, 枚举常量。
2、第一个成员如果没有被赋值,默认为0,后边的成员比前面的成员一次大1。
3、枚举类型
4、成员成为 枚举成员或者枚举常量
5、枚举变量,enum Color flag2; 可以使用枚举成员给flag2赋值。 也可以使用常量给枚举变量赋值(不推荐)
enum Color
{
pink, red, green , white, blue, yellow
};
int flag=1;
if(flag == red)
{
printf("red \n");
}
enum Color flag2; //枚举变量。
typedef 的使用
1、将一个已存在的类型 去一个别名。
2、typedef 不能创建新类型。
3、#define宏定义发生在预处理阶段, typedef发生在编译阶段。
typedef int int64; // 有分号
int64 a; // 等价于int a;
struct Test
{
int a;
};
struct Test obj; //定义一个结构体变量
typedef struct Test2 // 相当于给 struct Test2起一个别名叫Test2.
{
int a;
}Test2;
Test2 tmp; //可以省去一个关键字struct.