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.

猜你喜欢

转载自blog.csdn.net/Shayne_Lee/article/details/81274892