跳转至

常量和变量

常量

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

整型常量

  • 整型数,例如: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 CPU寄存器 函数执行期间 定义它的块内 每次调用都重新初始化;未初始化则值不确定
局部静态 static 静态数据区 程序整个运行期 定义它的文件内 只初始化一次;未初始化则自动赋0
外部静态 static 静态数据区 程序整个运行期 定义它的文件内 只初始化一次;未初始化则自动赋0
全局外部 extern 静态数据区 程序整个运行期 所有文件(需声明) 只初始化一次;未初始化则自动赋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 声明时不允许修改外部变量的值(不允许对外部变量的局部声明使用初始值设定项)。