主要回顾 C 数据类型、C 变量、C 常量三个知识点。

# 概念

  • C 的令牌:可以是关键字、标识符、常量、字符串值,或者是一个符号
  • 分号结束
  • 注释
    • 单行注释 ///* 单行注释 */
    • 多行注释:/* 多行注释 */
  • 标识符: 区分大小写
  • 关键字 (保留字): break,case,const,double,enum,... (C99 新增关键字,C11 新增关键字)
  • 空格
  • 一个正规程序可以有多个函数,但是有且只有一个主函数
  • 函数只有在被调用的时候才执行,主函数由系统调用执行
  • 数据类型:
    • 基本类型 (算术类型)
      • 整数类型
      • 浮点类型
    • 枚举类型 (算术类型:只能赋予其一定的离散整数值的变量)
    • void 类型
    • 派生类型
      • 指针类型
      • 数组类型
      • 结构类型
      • 共用体类型
      • 函数类型
  • 整数类型 (各种类型的存储大小与系统位数和系统类型有关)
类型存储大小值范围
char1 字节-128 到 127 或 0 到 255
unsigned char1 字节0 到 255
signed char1 字节-128 到 127
int2 或 4 字节-32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
unsigned int2 或 4 字节0 到 65,535 或 0 到 4,294,967,295
short2 字节-32,768 到 32,767
unsigned short2 字节0 到 65,535
long4 字节-2,147,483,648 到 2,147,483,647
unsigned long4 字节0 到 4,294,967,295
  • 可以使用 sizeof 运算符得到对象或类型的存储字节大小
  • 浮点类型
类型存储大小值范围精度
float4 字节1.2E-38 到 3.4E+386 位有效位
double8 字节2.3E-308 到 1.7E+30815 位有效位
long double16 字节3.4E-4932 到 1.1E+493219 位有效位
  • char:通常是一个字节(八位), 这是一个整数类型。

  • int:整型,4 个字节,取值范围 -2147483648 到 2147483647。

  • float:单精度浮点值。单精度是这样的格式,1 位符号,8 位指数,23 位小数。

  • double:双精度浮点值。双精度是 1 位符号,11 位指数,52 位小数。

  • c 语言中内存区域划分

    1. 栈(stack):由编译器进行管理,自动分配和释放,存放函数调用过程中的各种参数、局部变量、返回值以及函数返回地址。操作方式类似数据结构中的栈。
    2. 堆(heap):用于程序动态申请分配和释放空间。C 语言中的 malloc 和 free,C++ 中的 new 和 delete 均是在堆中进行的。正常情况下,程序员申请的空间在使用结束后应该释放,若程序员没有释放空间,则程序结束时系统自动回收。注意:这里的 “堆” 并不是数据结构中的 “堆”。
    3. 全局(静态)存储区:分为 DATA 段和 BSS 段。DATA 段(全局初始化区)存放初始化的全局变量和静态变量;BSS 段(全局未初始化区)存放未初始化的全局变量和静态变量。程序运行结束时自动释放。其中 BBS 段在程序执行之前会被系统自动清 0,所以未初始化的全局变量和静态变量在程序执行之前已经为 0。
    4. 文字常量区:存放常量字符串。程序结束后由系统释放。
    5. 程序代码区:存放程序的二进制代码。
  • c 语言中常量又叫字面量,定义后不能进行修改。常量有

    • 整数常量:十进制、八进制或十六进制的常量等
    • 浮点常量:由整数部分、小数点、小数部分和指数部分组成
    • 字符常量:括在单引号中,含转义符
    • 字符串常量:括在双引号中
  • 定义常量的方式

    1. 使用 #define 预处理器。
    2. 使用 const 关键字。
  • C 语言中没有专门的字符串类型,因此双引号内的字符串会被存储到一个数组中,这个字符串代表指向这个数组起始字符的指针;

# 代码

# friday.c

#include <stdio.h>
#include <float.h>
#include <stdlib.h>
#include "constants.h"
/*
演示输出各类型占用存储空间
*/
int main1(){
    //% lu 为 32 位无符号整数
    //% E 为以指数形式输出单、双精度实数
    printf("-------- 各数据类型及其存储字节数 ---------\n");
    printf("char 存储字节数 : %lu \n", sizeof(char));
    printf("unsigned char 存储字节数 : %lu \n", sizeof(unsigned char));
    printf("signed char 存储字节数 : %lu \n", sizeof(signed char));
    printf("int 存储字节数 : %lu \n", sizeof(int));
    printf("unsigned int 存储字节数 : %lu \n", sizeof(unsigned int));
    printf("short 存储字节数 : %lu \n", sizeof(short));
    printf("unsigned short 存储字节数 : %lu \n", sizeof(unsigned short));
    printf("long 存储字节数 : %lu \n", sizeof(long));
    printf("unsigned long 存储字节数 : %lu \n", sizeof(unsigned long));
    printf("float 存储字节数 : %lu \n", sizeof(float));
    printf("float 最小值: %E\n", FLT_MIN );
    printf("float 最大值: %E\n", FLT_MAX );
    printf("精度值: %d\n", FLT_DIG );
    return 0;
}
/*
演示各类型定义
*/
int s;
int t;
int m = 16;
int n = 17;
extern double PI;
extern float E;
// 尽量不要写这种定义方式,有警告,extern 只做声明,而非定义
//extern int p = 999;  
int main2(){
    // 字符型常量 (含转义字符)
    char character = 'a';
    char character1 = '\n';
    //10 进制数
    int decimal_num = 12;
    //8 进制数
    int octal_num = 045;
    //2 进制数
    int binary_num = 0b11011;
    //16 进制数
    int hexadecimal_num = 0x21458adf;
    // 单精度常量
    float pi = 3.14f;  
    // 默认为双精度
    float e = 2.71828;  
    // 双精度浮点 (double) 型
    double g = 9.8;
    // 字符串常量
    char str[5] = "Jalen";
    //
    int i;
    i = 12;
    PI = 13;
    s = 14;
    t = 15;
    m = 116;
    n = 117;
    E = 666;
    extern float G;
    extern int AGE;
    AGE = 22;
    printf("i=%d,PI=%f,s=%d,t=%d,m=%d,n=%d,E=%f,G=%f,age=%d", i, PI, s, t, m, n, E, G, AGE);
    return 0;
}
//c 没有变量提升
// int m = 16;
// int n = 17;
// extern int o;
// extern int p;
/*
演示类型转换
*/
int main3(){
    // 浮点数赋给整型,该浮点数小数被舍去;下面输出圆周长 = 37
    float pi = 3.1415926;
    int circleRadius = 6;
    int circlePerimeter = 2 * pi * circleRadius;
    printf("圆周长是 %d \n", circlePerimeter);
    // 整数赋给浮点型,数值不变,但是被存储到相应的浮点型变量中;
    int age = 20;
    int monthPerYear = 12;
    float totalMonth = age * monthPerYear;
    printf("你已存活了 %f 个月\n", totalMonth);
    // 向下取整
    float weight = 8.8;
    int intWeight = (int)8.8;
    printf("重量是 %d KG\n", intWeight);
    // 两个整数相除,结果仍为整数 2,把 2 赋给浮点数 f
    int num1 = 10;
    int num2 = 4;
    float num3 = num1/num2;
    printf("商是 %f \n", num3);
    // 求和
    float a = 2.8, b = 2.9;
    float c = a + b;
    int d = a + b;
    printf("和转浮点数是 %f \n", c);
    printf("和转整数是 %d \n", d);
}
/*
输出变量地址
内存寻址
*/
int main4(){
    int a;
    int b;
    int c;
    int d;
    // 变量地址的获取方式:& 变量名
    //a 的地址是 00000015667ffafc
    //b 的地址是 00000015667ffaf8
    //c 的地址是 00000015667ffaf4
    //d 的地址是 00000015667ffaf0
    //a 的地址比 b 的地址大 4 字节,依次类推
    printf("a的地址是%p\nb的地址是%p\nc的地址是%p\nd的地址是%p\n",&a,&b,&c,&d);
    //a 的值是 0
    printf("a的值是%d\n",a);
    return 0;
}
/*
C 语言经过编译之后将内存中的区别
*/
int k1 = 1;
int k2;
static int k3 = 2;
static int k4;
int main5(){
    static int m1 = 2, m2;
    int i = 1;
    char *p;
    char str[10] = "hello";
    char *q = "hello";
    p = (char *)malloc(100);
    free(p);
    printf("栈区-变量地址  i:%p\n", &i);
    printf("                p:%p\n", &p);
    printf("              str:%p\n", str);
    printf("                q:%p\n", &q);
    printf("堆区地址-动态申请:%p\n", p);
    printf("全局外部有初值 k1:%p\n", &k1);
    printf("    外部无初值 k2:%p\n", &k2);
    printf("静态外部有初值 k3:%p\n", &k3);
    printf("    外静无初值 k4:%p\n", &k4);
    printf("  内静态有初值 m1:%p\n", &m1);
    printf("  内静态无初值 m2:%p\n", &m2);
    printf("文字常量地址    :%p, %s\n",q, q);
    printf("程序区地址      :%p\n",&main5);
    return 0;
}
/*
等号赋值
*/
int main6(){
    int a, b;
    // 等同 a=b=3
    a = (b = 3);
    printf("%d\n",a);
    printf("%d\n",b);
    return 0;
}
/*
定义常量
(1)#define 定义常量格式:#define identifier value
(2)注意使用 #define 定义常量没有 “=” 号赋值,而且结尾没有分号
(3)#define 是宏定义,它不能定义常量,但可以实现在字面意义上和其它定义常量相同的功能
(4)define 定义的是不带类型的常数,只进行简单的字符替换。在预编译的时候起作用,不存在类型检查。
(5)const 定义常量格式:const type variable = value;
(6)const 定义的是变量不是常量,const 只是把该变量所占的内存变为只读
(7)区别:
     - 编译器处理方式不同:#define 宏是在预处理阶段展开;const 常量是编译运行阶段使用
     - 类型和安全检查不同:#define 宏没有类型,不做任何类型检查,仅仅是展开;const 常量有具体的类型,在编译阶段会执行类型检查
     - 存储方式不同:#define 宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存;const 常量会在内存中分配 (可以是堆中也可以是栈中)
     - const 可以节省空间,避免不必要的内存分配(内存地址 & amp; 立即数,#define 在内存中有若干个拷贝)
     - const 提高了效率
     - #define 宏替换只作替换,不做计算,不做表达式求解
     - const 定义变量时就需要初始化赋值
*/
#define LENGTH 5
#define WIDTH 10
#define NEWLINE '\n'
int main7(){
    int area;
    area = LENGTH * WIDTH;
    printf("value of area : %d", area);
    printf("%c", NEWLINE);
    const int  LENGTH1 = 20;
    const int  WIDTH1  = 10;
    const char NEWLINE1 = '\n';
    area = LENGTH1 * WIDTH1;
    printf("value of area : %d", area);
    printf("%c", NEWLINE1);
    return 0;
}
/*
define 注意 “边缘效应”
*/
#define N 2+3
#define M (2+3)
int main8(){
    printf("N 的值为 : %d\n", N); 
    printf("M 的值为 : %d\n", M);
    double a, b;
    a = (float)N / (float)2;  // 2+3/2=2+1.5=3.5
    b = (float)M / (float)2;  // (2+3)/2=2.5
    printf("a 的值为 : %.2f\n", a);   
    printf("b 的值为 : %.2f\n", b); 
    return 0;  
}

# constants.h

double PI = 3.1415926;
float E = 2.71828;
float G = 9.8;
int AGE = 12;
int plus(int x, int y){
    return x + y;
}