黑马程序员——C语言基础---复杂数据类型(变量,结构体,枚举)

来源:互联网 发布:java 内存管理 编辑:程序博客网 时间:2024/05/24 15:37

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

C语言有丰富的数据类型和运算符,因此计算能力非常强大,计算过程中使用的值一般用变量来存储。变量也是有分类型的,不同类型的变量有不同的存储类型、不同的生命周期、不同的作用域,C语言也提供了一些关键字来设置变量的属性(比如设置存储类型、生命周期)



 一、变量的作用域


C语言根据变量作用域的不同,将变量分为局部变量和全局变量。


1.局部变量

1>定义:在函数内部定义的变量,称为局部变量。形式参数也属于局部变量。


2>作用域:局部变量只在定义它的函数内部有效,即局部变量只有在定义它的函数内部使用,其它函数不能使用它。


 


2.全局变量

1>定义:在所有函数外部定义的变量,称为全局变量。


2>作用域:全局变量的作用范围是从定义变量的位置开始到源程序结束,即全局变量可以被在其定义位置之后的其它函数所共享。


示例:

/*根据变量的作用域,可以分为: 1.局部变量: 1> 定义:在函数(代码块)内部定义的变量(包括函数的形参) 2> 作用域:从定义变量的那一行开始,一直到代码块结束 3> 生命周期:从定义变量的那一行开始分配存储空间,代码块结束后,就会被回收 4> 没有固定的初始值  2.全局变量 1> 定义:在函数外面定义的变量 2> 作用域:从定义变量的那一行开始,一直到文件结尾(能被后面的所有函数共享) 3> 生命周期:程序一启动就会分配存储空间,程序退出时才会被销毁 4> 默认的初始值就是0*/#include <stdio.h>int age;void test(){    int age;    age = 10;}int main(){    printf("%d\n", age);// 0        int age = 20;        printf("%d\n", age);// 20        test();        printf("%d\n", age);// 20    return 0;}

运行结果:


 



二、变量的存储类型


*变量的存储类型就是指变量存储在什么地方。有3个地方可以用于存储变量:普通内存、运行时堆栈、硬件寄存器。变量的存储类型决定了变量何时创建、何时销毁以及它的值能保持多久,也就是决定了变量的生命周期。


* C语言根据变量的存储类型的不同,可以把变量分为:自动变量、静态变量、寄存器变量。


1.自动变量

1>定义:自动变量是存储在堆栈中的。


2>哪些是自动变量:被关键字auto修饰的局部变量都是自动变量,但是极少使用这个关键字,基本上是废的,因为所有的局部变量在默认情况下都是自动变量。


3>生命周期:在程序执行到声明自动变量的代码块(函数)时,自动变量才被创建;当自动变量所在的代码块(函数)执行完毕后,这些自动变量就会自行销毁。如果一个函数被重复调用,这些自动变量每次都会重新创建。


1 void test(int a, int b) {

2     int c = a + b;

3     

4     auto int d;

5 }

1行的变量ab,第2行的变量c、第4行的变量d都是自动变量。


 


2.静态变量

1>定义:静态变量是存储在静态内存中的,也就是不属于堆栈。


2>哪些是静态变量:


所有的全局变量都是静态变量

被关键字static修饰的局部变量也是静态变量

3>生命周期:静态变量在程序运行之前创建,在程序的整个运行期间始终存在,直到程序结束。


 1 #include <stdio.h>

 2 

 3 int a;

 4 

 5 void test() {

 6     static int b = 0;

 7     b++;

 8     

 9     int c = 0;

10     c++;

11     

12     printf("b=%d, c=%d \n", b, c);

13 }

14 

15 int main() {

16     int i;

17     // 连续调用3test函数

18     for (i = 0; i<3; i++) {

19         test();

20     }

21     

22     return 0;

23 }


*3行的变量a、第6行的变量b都是静态变量,第9行的变量c、第16行的变量i是自动变量。


*因为第6行的变量b是静态变量,所以它只会被创建一次,而且生命周期会延续到程序结束。因为它只会创建一次,所以第6行代码只会执行一次,下次再调用test函数时,变量b的值不会被重新初始化为0


*注意:虽然第6行的变量b是静态变量,但是只改变了它的存储类型(即生命周期),并没有改变它的作用域,变量b还是只能在test函数内部使用。


*我们在main函数中重复调用test函数3次,输出结果为:


 


3.寄存器变量

1>定义:存储在硬件寄存器中的变量,称为寄存器变量。寄存器变量比存储在内存中的变量访问效率更高(默认情况下,自动变量和静态变量都是放在内存中的)


2>哪些变量是寄存器变量:


被关键字register修饰的自动变量都是寄存器变量

只有自动变量才可以是寄存器变量,全局变量和静态局部变量不行

寄存器变量只限于intchar和指针类型变量使用

3>生命周期:因为寄存器变量本身就是自动变量,所以函数中的寄存器变量在调用该函数时占用寄存器中存放的值,当函数结束时释放寄存器,变量消失。


4>使用注意:


由于计算机中寄存器数目有限,不能使用太多的寄存器变量。如果寄存器使用饱和时,程序将寄存器变量自动转换为自动变量处理

为了提高运算速度,一般会将一些频繁使用的自动变量定义为寄存器变量,这样程序尽可能地为它分配寄存器存放,而不用内存

1 int main() {

2     register int a;

3     return 0;

4 }

2行的变量a是个寄存器变量。


示例:

// 全局变量:a、b、c// 局部变量:v1、v2、e、f#include <stdio.h>// 变量a的初值是10int a = 10;// 变量b的初值是0// 变量c的初值是20int b , c = 20;int sum(int v1, int v2){    return v1 + v2;}void test(){    b++;        int i = 0;    i++;        printf("b=%d, i=%d\n", b, i);}int main(){    test();    test();    test();        int e = 10;        {        {            int f = 30;        }    }        return 0;}

运行结果:




一、什么是结构体


当一个整体由多个数据构成时,我们可以用数组来表示这个整体,但是数组有个特点:内部的每一个元素都必须是相同类型的数据。
在实际应用中,我们通常需要由不同类型的数据来构成一个整体,比如学生这个整体可以由姓名、年龄、身高等数据构成,这些数据都具有不同的类型,姓名可以是字符串类型,年龄可以是整型,身高可以是浮点型。
为此,C语言专门提供了一种构造类型来解决上述问题,这就是结构体,它允许内部的元素是不同类型的。


二、结构体的定义


1.定义形式
 结构体内部的元素,也就是组成成分,我们一般称为"成员"。


结构体的一般定义形式为:


 1 struct 结构体名{
 2     
 3     类型名1 成员名1;
 4     
 5     类型名2 成员名2;
 6     
 7     ……
 8     
 9     类型名n 成员名n;   
10     
11 };
struct是关键字,是结构体类型的标志。



三、结构体变量的定义


前面只是定义了名字为Student的结构体类型,并非定义了一个结构体变量,就像int一样,只是一种类型。

接下来定义一个结构体变量,方式有好多种。

1.先定义结构体类型,再定义变量
1 struct Student {
2     char *name;
3     int age;
4 };

6 struct Student stu;
第6行定义了一个结构体变量,变量名为stu。struct和Student是连着使用的。

2.定义结构体类型的同时定义变量
struct Student {
    char *name;
    int age;
} stu;
结构体变量名为stu

3.直接定义结构体类型变量,省略类型名
struct {
    char *name;
    int age;
} stu;
结构体变量名为stu

示例:

/* 数组:只能由多个相同类型的数据构成  结构体:可以由多个不同类型的数据构成 */#include <stdio.h>int main(){    //int ages[3] = {[2] = 10, 11, 27};            //int ages[3] = {10, 11, 29};        // 1.定义结构体类型    struct Person    { // 里面的3个变量,可以称为是结构体的成员或者属性        int age; // 年龄        double height; // 身高        char *name; // 姓名    };        // 2.根据结构体类型,定义结构体变量    struct Person p = {20, 1.55, "jack"};    p.age = 30;    p.name = "rose";        printf("age=%d, name=%s, height=%f\n", p.age, p.name, p.height);        /* 错误写法    struct Person p2;    p2 = {30, 1.67, "jake"};    */        struct Person p2 = {.height = 1.78, .name="jim", .age=30};    //p2.age = 25;        return 0;}


四、结构体的注意点

1.定义结构体变量的3种方式

 1> 先定义类型,再定义变量(分开定义)

 struct Student

 {

    int age;

 };

 struct Student stu;

 

 2> 定义类型的同时定义变量

 struct Student

 {

    int age;

 } stu;

 struct Student stu2;

 

 3> 定义类型的同时定义变量(省略了类型名称)

 struct

 {

    int age;

 } stu;

 

 2.结构体类型的作用域

 1> 定义在函数外面:全局有效(从定义类型的那行开始,一直到文件结尾)

 2> 定义在函数(代码块)内部:局部有效(从定义类型的那行开始,一直到代码块结束


示例:

#include <stdio.h>struct Date{    int year;    int month;    int day;};int a;void test2(){    struct Date    {        int year;    };    // 这里使用的是test2函数内部的struct Date类型    struct Date d1 = {2011};            // 结构体类型也是有作用域,从定义类型的那一行开始,一直到代码块结束    struct Person    {        int age;    };        struct Person p;        a  = 10;}int main(){    struct Date d1 = {2009, 8, 9};            test2();        // 不能使用test2函数中定义的类型    // struct Person p2;        return 0;}// 定义结构体变量void test(){    // 定义结构体变量的第3种方式    struct {        int age;        char *name;    } stu;        struct {        int age;        char *name;    } stu2;            /*结构体类型不能重复定义     struct Student     {     int age;     };          struct Student     {     double height;     };          struct Student stu;     */        /* 错误写法:结构体类型重复定义     struct Student     {     int age;     double height;     char *name;     } stu;          struct Student     {     int age;     double height;     char *name;     } stu2;c     */        /*     这句代码做了两件事情     1.定义结构体类型     2.利用新定义好的类型来定义结构体变量     */    // 定义变量的第2种方式:定义类型的同时定义变量    /*     struct Student     {     int age;     double height;     char *name;     } stu;          struct Student stu2;     */        /*     // 定义变量的第1种方式:     // 1.类型     struct Student     {     int age;     double height;     char *name;     };          // 2.变量     struct Student stu = {20, 1.78, "jack"};     */}



五、结构体的初始化


将各成员的初值,按顺序地放在一对大括号{}中,并用逗号分隔,一一对应赋值。


比如初始化Student结构体变量stu

1 struct Student {
2     char *name;
3     int age;
4 };

6 struct Student stu = {"MJ", 27};
只能在定义变量的同时进行初始化赋值,初始化赋值和变量的定义不能分开,下面的做法是错误的:
struct Student stu;
stu = {"MJ", 27};
 
六、结构体的使用


1.一般对结构体变量的操作是以成员为单位进行的,引用的一般形式为:结构体变量名.成员名
1 struct Student {
2     char *name;
3     int age;
4 };

6 struct Student stu;

8 // 访问stu的age成员
9 stu.age = 27;
第9行对结构体的age成员进行了赋值。"."称为成员运算符,它在所有运算符中优先级最高


2.如果某个成员也是结构体变量,可以连续使用成员运算符"."访问最低一级成员
 

3.相同类型的结构体变量之间可以进行整体赋值
 
七、结构体数组


1.定义
跟结构体变量一样,结构体数组也有3种定义方式


struct Student {
    char *name;
    int age;
};
struct Student stu[5]; //定义1
 


struct Student {
    char *name;
    int age;
} stu[5]; //定义2
 


struct {
    char *name;
    int age;
} stu[5]; //定义3
上面3种方式,都是定义了一个变量名为stu的结构体数组,数组元素个数是5

示例:

int main(){    struct RankRecord    {        int no; // 序号  4        int score; // 积分 4        char *name; // 名称 8    };    /*    struct RankRecord r1 = {1, "jack", 5000};    struct RankRecord r2 = {2, "jim", 500};    struct RankRecord r3 = {3, "jake",300};    */        //int ages[3] = {10, 19, 29};        //int ages[3];    // 对齐算法    // 能存放3个结构体变量,每个结构体变量占16个字节    // 72    /*     int no; // 序号  4     char *name; // 名称 8     int score; // 积分 4     */    // 48    /*     int no; // 序号  4     int score; // 积分 4     char *name; // 名称 8     */    struct RankRecord records[3] =    {        {1, "jack", 5000},                {2, "jim", 500},                {3, "jake",300}    };        records[0].no = 4;    // 错误写法    //records[0] = {4, "rose", 9000};        for (int i = 0; i<3; i++)    {        printf("%d\t%s\t%d\n", records[i].no, records[i].name, records[i].score);    }        //printf("%d\n", sizeof(records));            return 0;}


 


2.初始化
struct {
    char *name;
    int age;
} stu[2] = { {"MJ", 27}, {"JJ", 30} };
也可以用数组下标访问每一个结构体元素,跟普通数组的用法是一样的

八、结构体作为函数参数


将结构体变量作为函数参数进行传递时,其实传递的是全部成员的值,也就是将实参中成员的值一一赋值给对应的形参成员。因此,形参的改变不会影响到实参。

 示例:

#include <stdio.h>struct Student{    int age;    int no;};// 如果结构体作为函数参数,只是将实参结构体所有成员的值对应地赋值给了形参结构体的所有成员// 修改函数内部结构体的成员不会影响外面的实参结构体void test(struct Student s){    s.age = 30;    s.no = 2;}// 会影响外面的实参结构体void test2(struct Student *p){    p->age = 15;    p->no = 2;}void test3(struct Student *p){    struct Student stu2 = {15, 2};    p = &stu2;    p->age = 16;    p->no = 3;}int main(){    struct Student stu = {28, 1};        //test(stu);    //test2(&stu);    test3(&stu);        printf("age=%d, no=%d\n", stu.age, stu.no);        return 0;}

运行结果:


九、指向结构体的指针


* 每个结构体变量都有自己的存储空间和地址,因此指针也可以指向结构体变量


* 结构体指针变量的定义形式:struct 结构体名称 *指针变量名


* 有了指向结构体的指针,那么就有3种访问结构体成员的方式


示例:

#include <stdio.h>/* 1.指向结构体的指针的定义 struct Student *p;  2.利用指针访问结构体的成员 1> (*p).成员名称 2> p->成员名称  */int main(){    struct Student    {        int no;        int age;    };    // 结构体变量    struct Student stu = {1, 20};        // 指针变量p将来指向struct Student类型的数据    struct Student *p;        // 指针变量p指向了stu变量    p = &stu;        p->age = 30;        // 第一种方式    printf("age=%d, no=%d\n", stu.age, stu.no);        // 第二种方式    printf("age=%d, no=%d\n", (*p).age, (*p).no);        // 第三种方式    printf("age=%d, no=%d\n", p->age, p->no);                    return 0;}

运行结果:




十、结构体补齐算法

#include <stdio.h>int main(){    char c = 'A';        int a = 10;        printf("a=%p\n", &a);    printf("c=%p\n", &c);            /*    struct Student    {        int age;// 4        int score;// 4                char *name;//8    };        struct Student stus[3];            printf("%ld\n", sizeof(stus));*/        return 0;}

十一、结构体的嵌套定义

#include <stdio.h>int main(){    char c = 'A';        int a = 10;        printf("a=%p\n", &a);    printf("c=%p\n", &c);            /*    struct Student    {        int age;// 4        int score;// 4                char *name;//8    };        struct Student stus[3];            printf("%ld\n", sizeof(stus));*/        return 0;}


一、枚举的概念


枚举是C语言中的一种基本数据类型,并不是构造类型,它可以用于声明一组常数。当一个变量有几个固定的可能取值时,可以将这个变量定义为枚举类型。比如,你可以用一个枚举类型的变量来表示季节,因为季节只有4种可能的取值:春天、夏天、秋天、冬天。


 

二、枚举类型的定义


一般形式为:enum 枚举名 {枚举元素1,枚举元素2,……};


enum Season {spring, summer, autumn, winter}; 
 
三、枚举变量的定义


前面只是定义了枚举类型,接下来就可以利用定义好的枚举类型定义变量。


跟结构体一样,有3种方式定义枚举变量


1.先定义枚举类型,再定义枚举变量
enum Season {spring, summer, autumn, winter};

enum Season s;
 
2.定义枚举类型的同时定义枚举变量
enum Season {spring, summer, autumn, winter} s;
 
3.省略枚举名称,直接定义枚举变量
enum {spring, summer, autumn, winter} s;
上面三种方式定义的都是枚举变量s

四、枚举使用的注意

1> C语言编译器会将枚举元素(spring、summer等)作为整型常量处理,称为枚举常量。

2> 枚举元素的值取决于定义时各枚举元素排列的先后顺序。默认情况下,第一个枚举元素的值为0,第二个为1,依次顺序加1。

enum Season {spring, summer, autumn, winter};
也就是说spring的值为0,summer的值为1,autumn的值为2,winter的值为3


3> 也可以在定义枚举类型时改变枚举元素的值

enum season {spring, summer=3, autumn, winter};
没有指定值的枚举元素,其值为前一元素加1。也就说spring的值为0,summer的值为3,autumn的值为4,winter的值为5



五、枚举变量的基本操作


1.赋值
可以给枚举变量赋枚举常量或者整型值


enum Season {spring, summer, autumn, winter} s;


s = spring; // 等价于 s = 0;


s = 3; // 等价于 s = winter;
 


2.遍历枚举元素
enum Season {spring, summer, autumn, winter} s;


// 遍历枚举元素
for (s = spring; s <= winter; s++) {
    printf("枚举元素:%d \n", s);
}


示例:

#include <stdio.h>int main(){    enum Sex { Man, Woman, Unkown};        // 0男  1女 -1不详    //int sex = 3;    //enum Sex s = Unkown;        // 1.定义枚举类型    enum Season    {        spring = 1,        summer,        autumn,        winter    };        // 2.定义枚举变量    enum Season s = 100000;            printf("%d\n", s);            return 0;}

运行结果:



0 0
原创粉丝点击