c语言 - 变量与数据类型

c语言 - 变量与数据类型,全局变量,局部变量,常量,基本数据类型,各种运算符,存储类,自动/强制类型转换

全局变量

全局变量: 定义在所有函数之外的变量,属于 static 存储类,默认初始值为 0
局部变量: 函数的形参,函数体内定义的变量,代码块内定义的变量,属于 auto 存储类,需手动初始化,否则为垃圾值

注意:函数体外只能进行全局变量的初始化,不能进行赋值运算。

定义常量

定义常量有两种方式

  • #define宏替换
  • const关键字

基本类型

描述 数据类型
字符 char
短整数 short
整数 int
长整数 long
单精度浮点数 float
双精度浮点数 double
无类型 void

每种数据类型占用的内存大小(这是在 64 位的 Linux 下占用的大小,其它系统会有所不同):

NULL 值

NULLstdio.h实际上是#define NULL ((void *) 0),而在 C++ 中则直接被定义为了 0,#define NULL 0

浮点数

无符号数

char short int long默认都是有符号的,首位用来存储符号位。
如果不需要使用负数,则可以使用无符号数,只要在前面加上unsigned即可。
unsigned char unsigned shortunsigned intunsigned long,其中unsigned int可以简写为unsigned

运算符

  • 算术运算符
    + - * / %取余 ++自增 --自减
  • 关系运算符
    == != > >= < <=,结果为布尔值true/false
  • 逻辑运算符
    &&||!
  • 位运算符
    假设 A = 60,B = 13。二进制表示如下:
    A = 0011 1100
    B = 0000 1101
    A & B = 0000 1100,按位与
    A | B = 0011 1101,按位或
    A ^ B = 0011 0001,按位异或
    ~A = 1100 0011,按位非
    A << 2 = 1111 0000,左移
    A >> 2 = 0000 1111,右移(有符号)
  • 赋值运算符
    = += -= *= /= %= >>= <<= &= |= ^=
  • 其它运算符
    sizeof(int)返回变量长度(字节)
    &返回变量地址
    ?:条件表达式,如int a = (b > 10) ? 100 : 0,如果 b 大于 10,则 a 为 100,否则为 0

运算符优先级(从高到低)
后缀、一元、乘除、加减、移位、关系、相等、位与、位异或、位或、逻辑与、逻辑或、条件运算符?:、赋值

自增,自减

  • ++在前面叫做前自增(例如 ++a);前自增先进行自增操作,再进行其他操作;
  • ++在后面叫做后自增(例如 a++);后自增先进行其他操作,再进行自增操作;
  • 自减同理。

其实这样描述不好理解,我把前自增和后自增分别比喻为两个函数(自减同理):

例如:

存储类

存储类定义 C 程序中变量/函数的范围(可见性)和生命周期。

auto

所有局部变量的默认存储类,只能修饰局部变量

static

static 有两个作用:

  • 指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。
  • static 也可以应用于全局变量函数全局变量属于 static 存储类,当 static 修饰全局变量函数时,会将其作用域限制在声明它的文件内(默认的作用域是工程内的所有文件)。

编译并运行

register

用于定义存储在寄存器中而不是 RAM 中的局部变量,这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用&运算符(因为它没有内存位置)。用了 register 修饰符,并不意味着该变量就存储在寄存器中,这取决于硬件和实现的限制。

extern

放在函数或者变量前,以标示函数或者变量的定义(实现部分)在别的文件(也可以在当前文件),用来声明全局变量或函数。

a.c

b.c

函数默认都隐式声明为 extern,所以可以省略 extern

a.c

b.c

注意变量声明定义的区别,函数声明定义很容易区分:
定义: 分配内存空间,如int i,分配 4 个字节的内存
声明: 不需要分配内存空间,如extern int i,这只是告诉编译器,i这个变量已经在别的文件定义了,不用为其分配内存,直接用就行

函数或者变量的声明都是为了提前使用,如果不需要提前使用,没有提前声明的必要性

变量初始值

  • static 存储类的变量(全局变量,static 变量),其初始值为 0
  • auto 存储类的变量,系统并不会对其进行初始化,它的值是垃圾值,所以在使用前一定要进行手动初始化

编译并运行

数据类型转换

自动类型转换

  1. 若参与运算的数据类型不同,则先转换成同一类型,然后进行运算。
  2. 转换按数据长度增加的方向进行,以保证精度不降低。例如 int 型和 long 型运算时,先把 int 转成 long 型后再进行运算。
  3. 所有的浮点运算都是以双精度进行的,即使仅含 float 单精度量运算的表达式,也要先转换成 double 型,再作运算。
  4. char 型和 short 型参与运算时,必须先转换成 int 型。
  5. 在赋值运算中,赋值号两边的数据类型不同时,需要把右边表达式的类型将转换为左边变量的类型。如果右边表达式的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度。

强制类型转换

#include <stdio.h>

int main() {
    int a=10, b=3;
    double result;
    result = (double) a / b;
    printf("(double) a / b = %f\n", result);
    result = (double) (a / b);
    printf("(double) (a / b) = %f\n", result);
    return 0;
}

注意,(double) 的优先级更高,所以第一个结果是这样算的:先将 a 转换为 double 类型,然后再和 b 相除,这时,b 也自动转换为 double 类型,除出来的结果是 3.333333…
而第二个结果是先将 a 和 b 相除,因为都是 int 类型,所以后面的小数部分直接舍去了,然后再转换为 double 类型,结果就是 3.000000