跳转至

常量和变量

常量

  • 在程序执行过程中值不会发生变化的量。

整型常量

  • 整型数,例如:1,10,20,100
  • 整型字面值如:123,10,-20。默认情况下如是int类型。
  • 如果要显示的指定整型常量为其它类型,要在其后面添加后缀。指定整型常量为long类型时,要在后面加上l或者L,例如:12L,19l。
  • 指定整型常量为long long类型时,要在后面加上ll或者LL,例如:38LL,100LL。

浮点型常量

  • 浮点型数字,例如:3.14,1.99
  • 浮点型字面值如:3.14159,1.2,5.9。默认情况下是double类型。
  • 与整型常量类似,指定浮点型常量为float类型,要在后面加上f或F。例如:1.23f,3.14F,1.0f。
  • 指定浮点型常量为long double类型,要在后面加上l或者L。例如:12.7L。
  • 除了使用小数点表示浮点型外,还可以使用E表示法。比如:0.618可以表示为6.18E-1,6000000可以表示为6E+6。

字符常量

  • 由单引号引起来的单个的字符或转义字符,例如:'a','b','\n','\t'
  • 反斜杠后跟随三个字符表示三位八进制。'\015'这是一个字符。原型:\ddd
  • 反斜杠后跟随x和两位字符表示两位十六进制。'\x7F'这是一个字符。原型:\xhh
  • 以上8进制和16进制都代表着【一个字节大小的整数值字符】(一个字节8位)。
  • 都是单个字符,都是一个字节八位。

  • 字符串常量

  • 由双引号引起来的一个或多个字符组成的序列。

    * 如:""(这是一个空串,它依然占空间,因为它包含一个尾0),"hello"
    * "abc\n\021\018"这个字符串常量包含8个字符。a、b,c,\n,\021,\01,8。因为\018非法,所以只会识别\01。
    * sizeof("")显示1个字节
    * sizeof("abc\n\021\018")显示8个字节
    

标识常量

  • 宏定义只是单纯的宏体替换宏名,不进行语法检查,一改全改。
  • 其占用编译时间,不占用运行时间。
  • 宏定义的内容是常量,不可改变。

  • #define 基本示例

    #include <stdio.h>
    #define PI 3.14
    int main(void)
    {
        double r, s;
    
        r = 5;
        s = r * PI;
        printf("%lf\n", s);
    
        return 0;
    }
    

  • #define 优先级易错示例

    /*
    - 以下代码中,ADD被原样替换,被预处理为
        printf("%d\n", 2 + 3 * 2 + 3);
    - 所以在处理类似宏时,要清楚自己处理的内容。考虑是否使用括号
        来指定优先级。可以修改为:
        #define ADD (2 + 3)
        printf("%d\n", (2 + 3) * (2 + 3));  
    */
    
    #include <stdio.h>
    #define ADD 2 + 3
    int main(void)
    {
        printf("%d\n", ADD * ADD);
    
        return 0;
    }
    

  • #define 带参宏示例

    #include <stdio.h>
    
    // #define MAX(a, b) (a > b ? a : b)  
    
    // 更为严谨的处理
    #defeine MAX(a, b) ((a) > (b) ? (a) : (b))
    
    int main(void)
    {
        int a = 3, b = 4;
        printf("max = %d\n", MAX(a, b));
        return 0;
    }
    

  • #define 在标准c中无解的带参宏

    #include <stdio.h>
    #define MAX(i, j) ((i) > (j) ? (i) : (j))
    
    int main(void)
    {
        int i = 3, j = 4;
    
        printf("i = %d\tj = %d\n", i, j);
        printf("max = %d\n", MAX(i++, j++));
        printf("i = %d\tj = %d\n", i, j);
    
        return 0;
    }    
    /*
        执行结果:
        i = 3   j = 4
        max = 5
        i = 4   j = 6
        比较发现,j的值经过自增为6,比预期 +1。原因是,替换后的结果是:
        ((i++) > (j++) ? (i++) : (j++))
        那么较大的值一定在后面又自增了一次。
    */
    

  • gcc拓展了标准c的宏的使用

    // 以下gcc拓展部分,在gcc、clang中编译通过,msvc不支持。
    // typeof clan中 \Wall 给出警告,mscv不支持。
    // clang 中 \Wall 下将给出警告:-Wgnu-statement-expression-from-macro-expansion
    //      (warning: use of GNU statement expression extension from macro expansion 从宏扩展中使用 GNU 语句表达式扩展)
    
    #include <stdio.h>
    
    #define MAX(a, b) ({int A = a, B = b; ((A) > (B) ? (A) : (B));})
    // #define MAX(a, b) ({typeof(a) A = a, B = b; ((A) > (B) ? (A) : (B));})
    
    
    int main(void)
    {
        int i = 3, j = 4;
    
        printf("i = %d\tj = %d\n", i, j);
        printf("max = %d\n", MAX(i++, j++));
        printf("i = %d\tj = %d\n", i, j);
    
        return 0;
    }
    /*
        执行结果:
        i = 3   j = 4
        max = 4
        i = 4   j = 5
    
        这样的表达下,较大的值不会出现两次自增
    */
    

变量

  • 用来保存一些特定的内容,并且在程序执行过程中值随时发生变化的量。

  • 定义:

    [存储类型] 数据类型 标识符 = 值
                TYPE    NAME = VALUE;
    

  • 标识符:由字母、数字、下划线组成且不能以数字开头的一个标识序列。

  • 写标识符尽量做到见名知意

  • 数据类型:基本数据类型 或 构造类型

  • 存储类型:auto(自动) static(静态型) register(建议型) extern(说明型)

    • auto:默认,自动分配空间,自动回收空间。auto自动变量,存储在stack(栈)中,函数内有效,函数结束时自动销毁。初始值是随机分配的。
    • register:(建议型)寄存器类型,只能定义局部变量,不能定义全局变量;大小有限制,只能定义32位大小的数据类型。如果double就不可以,寄存器没有地址,所以一个寄存器类型的变量无法打印地址查看或使用。
    • static:(静态型)自动初始化为0或空值,并且其变量的值有继承性。另外,常用于修饰变量或函数。
    • extern:(说明型)意味着不能改变说明变量的值或类型。
  • 变量的生命周期和作用范围

    • 全局变量的作用域从定义的地方开始,直到当前程序结束。
    • 局部变量从当前定义位置开始到所在的块结束。
    • 代码在大括号中时是一个语句体,这个代码块有局部作用域。

变量的存储类型示例表格

局部变量 局部变量 局部变量 外部变量 外部变量
存储类别 auto register 局部 static 外部 static 外部
存储方式 动态 动态 静态 静态 静态
存储区 动态区 寄存器 静态存储区 静态存储区 静态存储区
生存期 函数调用开始至结束 函数调用开始至结束 程序整个运行期间 程序整个运行期间 程序整个运行期间
作用域 定义变量的函数或复合语句内 定义变量的函数或复合语句内 定义变量的函数或复合语句内 本文件 其他文件
赋初值 每次函数调用时 每次函数调用时 编译时赋初值,只赋值一次 编译时赋初值,只赋值一次 编译时赋初值,只赋值一次
未赋初值 不确定 不确定 自动赋初值0或空字符 自动赋初值0或空字符 自动赋初值0或空字符
  • 局部变量默认为 auto 类型
  • register 型变量个数受限,且不能为 long、double、float 型
  • 局部 static 变量具有全局寿命和局部可见性
  • 局部 static 变量具有可继承性
  • extern 不是变量定于,可拓展外部变量作用域

变量的使用示例

  • auto 类型变量示例

    #include <stdio.h>
    void func(void)
    {
        auto int x = 0;
        x += 1;
        printf("%p -> %d\n", &x, x);
    }
    int main(void)
    {
        func();
        func();
        func();
    
        return 0;
    }
    /*
        执行结果:
        000000B8A9CFF594 -> 1
        000000B8A9CFF594 -> 1
        000000B8A9CFF594 -> 1
    */
    

    • 上述代码执行后,因为函数内使用auto,所以函数结束即释放,每次都是新的变量x。
  • static 类型变量示例(一):变量定义一次,下次不会生成新的地址空间。

    #include <stdio.h>
    void func(void)
    {
        static int x = 0;
        x += 1;
        printf("%p -> %d\n", &x, x);
    }
    int main(void)
    {
        func();
        func();
        func();
    
        return 0;
    }
    /*
        执行结果:
        00007FF6677EC1B4 -> 1
        00007FF6677EC1B4 -> 2
        00007FF6677EC1B4 -> 3
    */
    

    • 上述代码执行后,因为函数内使用static,第一次x定义并赋值后,下次不会再生成新的地址空间,x的值每次自增;并且地址一定是相同的。
  • static 类型变量示例(二):修饰变量或函数,static 修饰的变量或函数只能在本文件范围,类似访问控制。

    /*
        MSVC 编译环境
        - 包含文件 main.c tools.c tools.h
    */
    
    /* ---------- main.c ---------- */
    #define _CRT_SECURE_NO_WARNINGS
    #include "tools.h"
    
    int numbers = 10;
    
    int main(void)
    {
    
        printf("[%s] numbers = %d\n", __FUNCTION__, numbers);
        call_test_func();
        printf("[%s] numbers = %d\n", __FUNCTION__, numbers);
    
        return 0;
    }
    
    /* ---------- tools.h ---------- */
    #ifndef TOOLS
    #define TOOLS
    
    #include <stdio.h>
    #include <stdbool.h>
    #include <math.h>
    
    void call_test_func(void);
    static void test_func(void);
    
    #endif // !TOOLS
    
    /* ---------- tools.c ---------- */
    #include "tools.h"
    
    static void test_func(void)
    {
        extern int numbers;
        numbers += 1;
        printf("[%s] numbers = %d\n", __FUNCTION__, numbers);
    }
    
    void call_test_func(void)
    {
        printf("RUN IN [%s]\n", __FUNCTION__);
        test_func();
    }
    
    /*
        执行结果
        [main] numbers = 10
        RUN IN [call_test_func]
        [test_func] numbers = 11
        [main] numbers = 11
    */
    

    • 使用 static 可以隔离不同文件中同名的全局变量。
    • 进行访问控制。
  • extern 类型变量示例

    /*
        MSVC 编译环境
        - 包含文件 main.c tools.c tools.h
    */
    
    /* ---------- main.c ---------- */
    #define _CRT_SECURE_NO_WARNINGS
    #include "tools.h"
    
    int numbers = 10;
    
    int main(void)
    {
        printf("[%s] numbers = %d\n", __FUNCTION__, numbers);
        test_func();
        printf("[%s] numbers = %d\n", __FUNCTION__, numbers);
    
        return 0;
    }
    
    /* ---------- tools.h ---------- */
    #ifndef TOOLS
    #define TOOLS
    
    #include <stdio.h>
    #include <stdbool.h>
    #include <math.h>
    
    void test_func(void);
    
    #endif // !TOOLS
    
    /* ---------- tools.c ---------- */
    #include "tools.h"
    
    void test_func(void)
    {
        extern int numbers;
        numbers += 1;
        printf("[%s] numbers = %d\n", __FUNCTION__, numbers);
    }
    
    /*
        执行结果
        [main] numbers = 10
        [test_func] numbers = 11
        [main] numbers = 11
    */
    

  • tools.c 中 extern 声明了变量 numbers 在外部。
  • numbers 要使用全局变量。
  • extern 声明时不允许修改外部变量的值(不允许对外部变量的局部声明使用初始值设定项)。