黑马程序员——C复杂数据类型--变量、结构体、预处理

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

变量

C语言根据变量作用域的不同,将变量分为局部变量和全局变量。
1.局部变量:
1> 定义:在函数(代码块)内部定义的变量(包括函数的形参);
2> 作用域:从定义变量的那一行开始,一直到代码块结束;
3> 生命周期:从定义变量的那一行开始分配存储空间,代码块结束后,就会被回收;
4> 没有固定的初始值。
2.全局变量
1> 定义:在函数外面定义的变量;
2> 作用域:从定义变量的那一行开始,一直到文件结尾(能被后面的所有函数共享);
3> 生命周期:程序一启动就会分配存储空间,程序退出时才会被销毁;
4> 默认的初始值就是0。
所有的全局变量都是静态变量,被关键字static修饰的局部变量也是静态变量。

#include <stdio.h>

void test()
{
    static int a = 0;
    a++;

    int b = 0;
    b++;

    printf("a=%d,b=%d\n", a, b);
}

int main()
{
    //连续调用3此test函数
    for(int i = 0; i<3 ; i++)
    {
        test();
    }

    return 0;
}

其中a为静态变量,只运行一次,所以运行的结果:
这里写图片描述

被关键字register修饰的自动变量都是寄存器变量;只有自动变量才可以是寄存器变量,全局变量和静态局部变量不行;寄存器变量只限于int、char和指针类型变量使用。我们一般会将一些频繁使用的自动变量定义为寄存器变量。

int b;

int main()
{
    register int a;

}

结构体

当一个整体由多个数据构成时,我们可以用数组来表示这个整体,但是数组有个特点:内部的每一个元素都必须是相同类型的数据。C语言专门提供了一种构造类型来解决这个问题,这就是结构体,可以由多个不同类型的数据构成。

  1. 定义结构体类型

struct是关键字,是结构体类型的标志。

struct Person
    { // 里面的3个变量,可以称为是结构体的成员或者属性
        int age; // 年龄
        double height; // 身高
        char *name; // 姓名
    };
  1. 定义结构体变量
    定义结构体变量有三种形式:
    先定义结构体类型,再定义变量
struct Student {
     char *name;
     int age;
 };

 struct Student stu;

定义结构体类型的同时定义变量

struct Student {
    char *name;
    int age;
} stu;

直接定义结构体类型变量,省略类型名

struct {
    char *name;
    int age;
} stu;
  • 结构体作用域

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

  • 结构体内存细节
// 1.定义结构体类型(并不会分配存储空间)
    struct Date
    {
        int year;
        int month;
        int day;
    };

    // 2.定义结构体变量(真正分配存储空间)
    struct Date d1 = {2011, 4, 10};


    struct Date d2 = {2012, 8, 9};

结构体变量占用的内存空间是其成员所占内存之和,而且各成员在内存中按定义的顺序依次排列;但是存在补齐算法,即结构体所占用的存储空间必须是最大成员字节数的倍数。

  • 结构体初始化
    将各成员的初值,按顺序地放在一对大括号{}中,并用逗号分隔,一一对应赋值。需要注意的是只能在定义变量的同时进行初始化赋值,初始化赋值和变量的定义不能分开,下面的做法是错误的:
struct Student stu;
stu = {"MJ", 27};

结构体在使用时需要注意,如果某个成员也是结构体变量,可以连续使用成员运算符”.”访问最低一级成员,如下程序所示:

struct Date {
      int year;
      int month;
      int day;
 };

 struct Student {
     char *name;
     struct Date birthday;
 };

 struct Student stu;

 stu.birthday.year = 1986;
 stu.birthday.month = 9;
 stu.birthday.day = 10;
  • 结构体数组
    结构体数组定义方式和结构体变量相同。在使用方法上也同普通的数组用法是一样的。
struct 
{
    char *name;
    int age;
} stu[2] = { {"MJ", 27}, {"JJ", 30} };
  • 结构体作为函数参数
    结构体变量其实和普通变量类型相同,将结构体变量作为函数参数进行传递时,其实传递的是全部成员的值,也就是将实参中成员的值一一赋值给对应的形参成员,只是简简单单的值传递,所以改变形参的值不会影响到实参。
  • 指向结构体的指针
    两种书写方式:
    struct Student stu;
    struct Student *p;
    访问结构体成员:stu.成员名
    利用指针可以访问相应的结构体成员:
    p->成员名
    (*p).成员名

这个程序中也很好的体现了访问结构体成员的方式:

#include <stdio.h>

int main()
{
    //定义一个结构体类型
    struct Student
    {
        char *name;
        int age;
        float height;
    };

    //定义一个结构体变量
    struct Student stu = {"duo", 26, 1.6};

    //定义一个结构体指针
    struct Student *p;

    //纸箱结构体变量stu
    p = &stu;

    printf("name = %s, age = %d, height = %f\n", stu.name, stu.age, stu.height);

    printf("name = %s, age = %d, height = %f\n",(*p).name, (*p).age, (*p).height);

    printf("name = %s, age = %d, height = %f\n",p->name, p->age, p->height);

    return 0;
}

枚举

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

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

枚举变量的定义和结构体变量定义方式相同,也是三种方式,可以先定义类型再定义变量,也可以在定义类型的同时定义变量,或者是省略枚举名称,直接定义枚举变量。

预处理指令

1.C语言在对源程序进行编译之前,会先对一些特殊的预处理指令作解释(比如之前使用的#include文件包含指令),产生一个新的源程序(这个过程称为编译预处理),之后再进行通常的编译。
2.为了区分预处理指令和一般的C语句,所有预处理指令都以符号”#”开头,并且结尾不用分号。
3.预处理指令可以出现在程序的任何位置,它的作用范围是从它出现的位置到文件尾。习惯上我们尽可能将预处理指令写在源程序开头,这种情况下,它的作用范围就是整个源程序文件。
4.C语言提供的预处理指令主要有:宏定义、文件包含、条件编译。

  • 宏定义
    一、不带参数的宏定义
    #define 宏名 字符串
    它的作用是在编译预处理时,将源程序中所有”宏名”替换成右边的”字符串”,常用来定义常量。
    #undef 宏名
    是结束这个宏定义。
    二、带参数的宏定义
    #define 宏名(参数列表) 字符串
    在编译预处理时,将源程序中所有宏名替换成字符串,并且将 字符串中的参数 用 宏名右边参数列表 中的参数替换。
#include <stdio.h>

 #define average(a, b) (a+b)/2

 int main ()
 {
     int a = average(10, 4);

     printf("平均值:%d", a);
     return 0;
 }

代码中定义了一个带有2个参数的宏average,下面会被替换成:int a = (10 + 4)/2。

从整个使用过程可以发现,带参数的宏定义,在源程序中出现的形式与函数很像。但是两者是有本质区别的:
1> 宏定义不涉及存储空间的分配、参数类型匹配、参数传递、返回值问题
2> 函数调用在程序运行时执行,而宏替换只在编译预处理阶段进行。所以带参数的宏比函数具有更高的执行效率

  • 条件编译
    其实条件编译同流程控制中if得用法相似,不过它是属于预处理。
#if 条件1
...code1...
#elif 条件2
...code2...
#else
...code3...
#endif

有时候我们不仅仅需要判断宏的值,还需要判断是否定义过某个宏,这个功能需要#if defined()和#if !defined()来判断,也可以写成#ifdef和#ifndef。

typedef

在C语言中我们可以使用typedef关键字为各种数据类型定义一个新名字。这个宏定义类似,但是却有本质上的区别。

#include <stdio.h>

#define String2 char *

typedef char * String1;

int main()
{
    //str1、str2是指向char类型的指针变量
    String1 str1, str2;

    //str3是指向char类型的指针变量,str4只是个char类型的变量
    String2 str3, str4;

    return 0;
}

在这个代码中,只有str1、str2、str3才是指向char类型的指针变量,str4只是个char类型的变量。因为经过typedef处理后,String1也算是一种数据类型,
而宏定义纯粹是字符串替换,用char *代替String2。

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值