C语言基础知识

1 计算机基本结构

约翰·冯·诺依曼提出了计算机制造的三个基本原则,即采用二进制逻辑、程序存储执行以及计算机由五个部分组成(运算器、控制器、存储器、输入设备、输出设备)。

1.1 CPU

CPU的主频:粗略的说,CPU的主频就是时钟的震荡的每秒次数,可近似的看作每秒执行的指令数。简单的说就是主频决定了CPU的运算速度。

1.1.1 逻辑门

电子开关-机械继电器(Mechanical Relay),通过电子开关,我们可以实现 1 位(bit) 的逻辑运算。

1.1.2 门电路

可以实现 1 位(bit) 的基本逻辑运算。

1) 非门(NOT GATE):输入为 true 输出 false,输入false 则输出 true;

2) 与门(AND GATE):两个都为 true才输出true,否则输出false;

3) 或门(OR GATE):一个为 1则为1,两个为 false 则为false;

4) 异或门(XOR GATE):通过非门与门和或门相结合就形成了异或门,相同为 false,不相同则为 true

1.1.3 算数逻辑单元ALU(Arithmetic & Logic Unit)

ALU 是计算机中进行算数、逻辑运算的核心部件。

1.1.3.1 算数单元

  1. 半加器:半加器就是通过一个与门和异或门,来计算两个 1位(bit)数的相加,既要考虑到和也要考虑到进位;
  2. 全加器:全加器是通过两个半加器进行 3个1位(bit)的数 进行运算,同样要考虑计算出来的和是否要进位;
  3. 加法器:通过1个半加器和7个全加器就能组成一个能计算 8个位 1位(bit) 的全加器。

1.1.3.2 逻辑单元

逻辑单元主要用来进行逻辑操作,最基本的操作就是 与、或、非操作,但不只是一位(bit)数的比较

1.2 存储器
  1. 主存储器即内存:程序中待处理的数据和处理的结果都存储在内存中;
  2. 外存储器:用来长期保存数据的大容量存储器;
  3. 寄存器是: CPU 内部的高速存储器,速度快,数目少;
1.3 操作系统OS

操作系统是一个"搞管理" 的软件管理硬件设备与软件资源(包括文件、进程),管理指描述和组织。

1.3.1 进程

进程是操作系统中非常核心的一个概念,进程其实是计算机完成工作的一个"过程",CPU的核数相当于CPU的分身数,电脑上的多任务系统其实就是基于进程调度这样的机制来完成的。

并发式执行:1个CPU运行多个进程,由于CPU的运行速度极快,虽然CPU在一直进行切换,但是咱们坐在电脑前的用户,是感知不到这个切换的过程的。

并行式执行:多个CPU,运行多个进程,进程1和进程2无论是微观还是宏观,都是同时执行的。

1.3.2 进程的属性

1)进程的状态:

    1. 运行状态:进程正在运行;
    2. 就绪状态:进程已经做好准备,随时准备被CPU调度执行;
    3. 阻塞状态:进程在此状态不能执行,只有等阻塞该进程的事件完成后才能执行,比如编程时等待我们输入;

2) 进程的优先级:给进程安排不同的优先级,优先级越高的进程,更容易被CPU调度执行。

3) 进程的上下文:进程的上下文,主要是存储调度出CPU之前寄存器中的信息(把寄存器信息保存到内存中),等到这个进程下次恢复到CPU上执行的时候,就把内存保存好的数据恢复到寄存器中。(进程本身是感知不到自己啥时候被调度出CPU的);

4)进程的记账信息:记录进程在CPU上执行了多久了,用来辅助决定这个进程是继续执行,还是要调度出CPU了。

1.3.3 虚拟地址空间

一个进程想要运行,就需要给它分配一些系统资源,其中内存就是一个最核心的资源,物理地址就是真实的内存的地址。

虚拟地址空间:每个进程所能访问的内存地址范围,是一段连续的虚拟内存空间。

好处就是让进程之间独立性提高了,不至于相互影响,整个系统更加稳定;缺点是两个进程需要相互配合的时候,进程间通信变难,通信需要使用一些特殊的手段,比如:文件,管道(是内核中提供的一个队列),消息队列,信号量等。

2 C语言知识
2.1 数据类型

送入计算机的数字,字母,符号等信息必须转换成 0、 1 组合的数据形式才能被计算机识别。能够进行算术运算得到明确数值概念的信息成为计算机数值数据,其余的信息成为非数值数据。

数值数据类型:包括十进制、二进制、十六进制(0x)和八进制(0);

非数值数据类型:非数值数据包括文字、符号、图像、语言和逻辑信息等,也都是以 0 、 1 形式存在。字符数据在机器内也被变换成二进制编码的形式。国际上普遍采用的一种编码是美国国家信息交换标准代码,简称为 ASCII 码。

C语言中基本数据类型如下:

    1. 整数类型:short int、int、long int、long long int
    2. 浮点数类型:float、double、long double
    3. 字符类型:char
    4. 布尔类型:_Bool或bool(定义时需要引用stdbool.h头文件)
    5. 枚举类型:enum
2.1.1 比特位

CPU能读懂的最小单位是:比特位,bit,b。每个比特位只能存放二进制数,即0和1.

2.1.2 字节

内存机构最小寻址单位:字节,Byte,B,注:1Byte = 8 bit

2.1.3 符号位

存放signed类型的存储单元中,左边第一位表示符号位。如果该位为0,表示该整数是一个正数;如果该位为1,表示该整数是一个负数。一个32位的整型变量,除去左边第一位符号位,剩下表示值的只有31个比特位。

2.1.4 补码

数据在计算中以二进制补码的形式进行存储:

    1. 正数的补码是该数的二进制形式;
    2. 负数的补码需要通过以下几步获得:先取得该数的绝对值的二进制形式,再将第1步的值按位取反(除符号位),最后将第2步的值加1。
2.1.5 整型类型

标准整数类型的存储大小和值范围的细节:

类型

存储大小

值范围

char

1 字节

-128 到 127 或 0 到 255

unsigned char

1 字节

0 到 255

signed char

1 字节

-128 到 127

int

2 或 4 字节

-32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647

unsigned int

2 或 4 字节

0 到 65,535 或 0 到 4,294,967,295

short

2 字节

-32,768 到 32,767

unsigned short

2 字节

0 到 65,535

long

4 字节

-2,147,483,648 到 2,147,483,647

unsigned long

4 字节

0 到 4,294,967,295

2.1.6 浮点类型类型

标准浮点类型的存储大小、值范围和精度的细节:

类型

存储大小

值范围

精度

float

4 字节

1.2E-38 到 3.4E+38

6 位有效位

double

8 字节

2.3E-308 到 1.7E+308

15 位有效位

long double

16 字节

3.4E-4932 到 1.1E+4932

19 位有效位

2.1.7 void类型

void 类型指定没有可用的值。它通常用于以下三种情况下:

    1. 函数返回为空:C 中有各种函数都不返回值,或者您可以说它们返回空。不返回值的函数的返回类型为空。例如:void exit (int status);
    2. 函数参数为空:C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如 int rand(void);
    3. 指针指向 void:类型为 void * 的指针代表对象的地址,而不是类型。例如,内存分配函数 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。
2.1.8 类型转换

类型转换是将一个数据类型的值转换为另一种数据类型的值。C 语言中有两种类型转换:

    • 隐式类型转换:隐式类型转换是在表达式中自动发生的,无需进行任何明确的指令或函数调用。它通常是将一种较小的类型自动转换为较大的类型,例如,将int类型转换为long类型或float类型转换为double类型。隐式类型转换也可能会导致数据精度丢失或数据截断。
    • 显式类型转换:显式类型转换需要使用强制类型转换运算符(type casting operator),它可以将一个数据类型的值强制转换为另一种数据类型的值。强制类型转换可以使程序员在必要时对数据类型进行更精确的控制,但也可能会导致数据丢失或截断。
2.2 变量
2.2.1 变量的声明

变量在程序中使用时 , 必须预先说明它们的存储类型和数据类型。

变量说明的一般形式是:

< 存储类型 > < 数据类型 > < 变量名 > ;

      • < 存储类型 > 是关键词 auto 、 register 、 static 和 extern;
      • < 数据类型 > 可以是基本数据类型,也可以是自定义的数据类型;
      • < 变量名 > 变量名由字母、数字、下划线组成,不能以数字开头,不能和 C 的关键字重名;
2.2.2 变量的存储类型

变量的存储类型分为auto 、 register 、 static 和 extern四类:

    1. auto(自动存储):修饰的变量存储在堆栈区,习惯上省略不写,如没有其他存储类型修饰则默认为自动存储;auto型的局部变量随函数调用而生成,函数调用结束而释放。如修饰的变量不进行初始化,则系统会随机给一个值。(auto) int i;
    2. static(静态存储):修饰的局部变量用固定地址存储在数据区,函数运行结束不会被释放,直至程序结束才会释放,因此有记忆功能,每次调用都保留上一次操作的值;修饰的全局变量只能被本文件内的函数使用,其他源文件不可访问。若修饰的变量不进行初始化,则系统默认赋为0。static int i;
#include <stdio.h>

/* 函数声明 */
void func1(void);

static int count=10;        /* 全局变量 - static 是默认的 */

int main()
{
    while (count--) {
        func1();
    }
    return 0;
}

void func1(void)
{
    /* 'thingy' 是 'func1' 的局部变量 - 只初始化一次
每次调用函数 'func1' 'thingy' 值不会被重置。*/                
    static int thingy=5;
    thingy++;
    printf(" thingy 为 %d , count 为 %d\n", thingy, count);
}
    1. extern(外部存储):用来修饰全局变量,修饰的变量是其他的源文件的全局变量,表示在该文件下引用了其他源文件定义的全局变量,extern声明不是定义,即不分配存储空间,但用static修饰的全局变量不可被其他文件访问。extern int i;
#include <stdio.h>

int count ;
extern void write_extern();

int main()
{
    count = 5;
    write_extern();
}
#include <stdio.h>

extern int count;

void write_extern(void)
{
    printf("count is %d\n", count);
}
    1. register(寄存器存储):修饰的局部变量存储在寄存器中,若变量被频繁使用,则可缩短变量的操作时间,加快程序的运行速度。一般修饰的变量不能太多且数据长度应该小于或等于整数的长度,因为寄存器数量有限且主要工作不是用来存储的,定义多了系统会自动把多出的register变量当做auto处理。只能应用于auto变量或者函数的形参,且不能用&取地址。register int i;
2.2.3 变量初始化注意事项

在 C 语言中,如果变量没有显式初始化,那么它的默认值将取决于该变量的类型和其所在的作用域。对于全局变量和静态变量(在函数内部定义的静态变量和在函数外部定义的全局变量),它们的默认初始值为零。以下是不同类型的变量在没有显式初始化时的默认值:

    • 整型变量(int、short、long等):默认值为0。
    • 浮点型变量(float、double等):默认值为0.0。
    • 字符型变量(char):默认值为'\0',即空字符。
    • 指针变量:默认值为NULL,表示指针不指向任何有效的内存地址。
    • 数组、结构体、联合等复合类型的变量:它们的元素或成员将按照相应的规则进行默认初始化,这可能包括对元素递归应用默认规则。

需要注意的是,局部变量(在函数内部定义的非静态变量)不会自动初始化为默认值,它们的初始值是未定义的(包含垃圾值)。因此,在使用局部变量之前,应该显式地为其赋予一个初始值。

2.3 常量

常量可以是任何的基本数据类型,比如整数常量、浮点常量、字符常量,或字符串字面值,也有枚举常量。常量的值在定义后不能进行修改。常量可以直接在代码中使用,也可以通过定义常量来使用。

2.3.1 整型常量

整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。

整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。

2.3.2 浮点常量

浮点常量由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。

    1. 当使用小数形式表示时,必须包含整数部分、小数部分,或同时包含两者。
    2. 当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者。带符号的指数是用 e 或 E 引入的。
2.3.3 字符常量

字符常量是括在单引号中,例如,'x' 可以存储在 char 类型的简单变量中。字符常量可以是一个普通的字符(例如 'x')、一个转义序列(例如 '\t'),或一个通用的字符(例如 '\u02C0')。

在 C 中,有一些特定的字符,当它们前面有反斜杠时,它们就具有特殊的含义,被用来表示如换行符(\n)或制表符(\t)等。下表列出了一些这样的转义序列码:

转义序列

含义

\\

\ 字符

\'

' 字符

\"

" 字符

\?

? 字符

\a

警报铃声

\b

退格键

\f

换页符

\n

换行符

\r

回车

\t

水平制表符

\v

垂直制表符

\ooo

一到三位的八进制数

\xhh . . .

一个或多个数字的十六进制数

2.3.4 字符串常量

字符串字面值或常量是括在双引号 " " 中的。一个字符串包含类似于字符常量的字符:普通的字符、转义序列和通用的字符。可以使用空格做分隔符,把一个很长的字符串常量进行分行。字符串常量在内存中以 \0 结尾。

2.3.5 定义常量

在 C 中,有两种简单的定义常量的方式:

    1. 使用 #define 预处理器: #define 可以在程序中定义一个常量,它在编译时会被替换为其对应的值。用法:#define 常量名 常量值
    2. 使用 const 关键字:const 关键字用于声明一个只读变量,即该变量的值不能在程序运行时修改。const 声明常量要在一个语句内完成定义和初始化赋值。

#define 与 const 这两种方式都可以用来定义常量,通常情况下,建议使用 const 关键字来定义常量,因为它具有类型检查和作用域的优势,而 #define 仅进行简单的文本替换,可能会导致一些意外的问题。#define 预处理指令和 const 关键字在定义常量时的区别:

    • 替换机制:#define 是进行简单的文本替换,而 const 是声明一个具有类型的常量。#define 定义的常量在编译时会被直接替换为其对应的值,而 const 定义的常量在程序运行时会分配内存,并且具有类型信息。
    • 类型检查:#define 不进行类型检查,因为它只是进行简单的文本替换。而 const 定义的常量具有类型信息,编译器可以对其进行类型检查。这可以帮助捕获一些潜在的类型错误。
    • 作用域:#define 定义的常量没有作用域限制,它在定义之后的整个代码中都有效。而 const 定义的常量具有块级作用域,只在其定义所在的作用域内有效。
    • 调试和符号表:使用 #define 定义的常量在符号表中不会有相应的条目,因为它只是进行文本替换。而使用 const 定义的常量会在符号表中有相应的条目,有助于调试和可读性。
2.4 运算符

运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C 语言内置了丰富的运算符,并提供了以下类型的运算符:算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、杂项运算符。

2.4.1 算术运算符

下表显示了 C 语言支持的所有算术运算符。假设变量 A 的值为 10,变量 B 的值为 20,则:

运算符

描述

实例

+

把两个操作数相加

A + B 将得到 30

-

从第一个操作数中减去第二个操作数

A - B 将得到 -10

*

把两个操作数相乘

A * B 将得到 200

/

分子除以分母

B / A 将得到 2

%

取模运算符,整除后的余数

B % A 将得到 0

++

自增运算符,整数值增加 1

A++ 将得到 11

--

自减运算符,整数值减少 1

A-- 将得到 9

2.4.2 关系运算符

下表显示了 C 语言支持的所有关系运算符。假设变量 A 的值为 10,变量 B 的值为 20,则:

运算符

描述

实例

==

检查两个操作数的值是否相等,如果相等则条件为真。

(A == B) 为假。

!=

检查两个操作数的值是否相等,如果不相等则条件为真。

(A != B) 为真。

>

检查左操作数的值是否大于右操作数的值,如果是则条件为真。

(A > B) 为假。

<

检查左操作数的值是否小于右操作数的值,如果是则条件为真。

(A < B) 为真。

>=

检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。

(A >= B) 为假。

<=

检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。

(A <= B) 为真。

2.4.3 逻辑运算符

下表显示了 C 语言支持的所有关系逻辑运算符。假设变量 A 的值为 1,变量 B 的值为 0,则:

运算符

描述

实例

&&

逻辑与运算符。如果两个操作数都非零,则条件为真。

(A && B) 为假。

||

逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。

(A || B) 为真。

!

逻辑非运算符。如果条件为真则逻辑非运算符将使其为假。

!(A && B) 为真。

2.4.4 位运算符

位运算符作用于位,并逐位执行操作。下表显示了 C 语言支持的位运算符。假设变量 A 的值为 60,变量 B 的值为 13,则:

运算符

描述

实例

&

对两个操作数的每一位执行逻辑与操作,如果两个相应的位都为 1,则结果为 1,否则为 0。

按位与操作,按二进制位进行"与"运算。

(A & B) 将得到 12,即为 0000 1100

|

对两个操作数的每一位执行逻辑或操作,如果两个相应的位都为 0,则结果为 0,否则为 1。

按位或运算符,按二进制位进行"或"运算。

(A | B) 将得到 61,即为 0011 1101

^

对两个操作数的每一位执行逻辑异或操作,如果两个相应的位值相同,则结果为 0,否则为 1。

异或运算符,按二进制位进行"异或"运算。运算规则:

(A ^ B) 将得到 49,即为 0011 0001

~

对操作数的每一位执行逻辑取反操作,即将每一位的 0 变为 1,1 变为 0。

取反运算符,按二进制位进行"取反"运算。

(~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。

<<

将操作数的所有位向左移动指定的位数。左移 n 位相当于乘以 2 的 n 次方。二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。

A << 2 将得到 240,即为 1111 0000

>>

将操作数的所有位向右移动指定的位数。右移n位相当于除以 2 的 n 次方。二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补 0,负数左补 1,右边丢弃。

A >> 2 将得到 15,即为 0000 1111

2.4.5 赋值运算符

下表列出了 C 语言支持的赋值运算符:< 左值表达式 > = < 右值表达式 >、< 变量 > < 操作符 >= < 表达式 >

运算符

描述

实例

=

简单的赋值运算符,把右边操作数的值赋给左边操作数

C = A + B 将把 A + B 的值赋给 C

+=

加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数

C += A 相当于 C = C + A

-=

减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数

C -= A 相当于 C = C - A

*=

乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数

C *= A 相当于 C = C * A

/=

除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数

C /= A 相当于 C = C / A

%=

求模且赋值运算符,求两个操作数的模赋值给左边操作数

C %= A 相当于 C = C % A

<<=

左移且赋值运算符

C <<= 2 等同于 C = C << 2

>>=

右移且赋值运算符

C >>= 2 等同于 C = C >> 2

&=

按位与且赋值运算符

C &= 2 等同于 C = C & 2

^=

按位异或且赋值运算符

C ^= 2 等同于 C = C ^ 2

|=

按位或且赋值运算符

C |= 2 等同于 C = C | 2

2.4.6 特殊运算符

1)条件运算符 "? :"是三目运算符 , 其运算的一般形式是 :< 表达式 1> ? < 表达式 2> : < 表达式 3>

int x=82, y=101;
x >= y ? x+18 : y-100         // 运算结果为 1
    x < (y-11) ? x-22 : y-1       // 运算结果为 60

2) 逗号运算符:从左往右分别求两个表达式的值,并以表达式2(即最右边的一个表达式)的值作为整个逗号表达式的值。

i =(a=2*3, a*5), a+6;        //i最终结果为 30
/*
step1:a = 2*3; 结果a=6
step2:计算a*5; 结果是30,此时a的值是不变的,仍旧是6
step3:i=(a=2*3,a*5);得到i=30,因为赋值符号=的优先级比逗号的高,所以直接把30赋值给了i,不执行a+6。 
*/
2.4.7 运算符优先级

下表将按运算符优先级从高到低列出各个运算符,具有较高优先级的运算符出现在表格的上面,具有较低优先级的运算符出现在表格的下面。在表达式中,较高优先级的运算符会优先被计算。

优先级

运算符

名称或含义

使用形式

结合方向

说明

1

[]

数组下标

数组名[常量表达式]

左到右

--

()

圆括号

(表达式)/函数名(形参表)

--

.

成员选择(对象)

对象.成员名

--

->

成员选择(指针)

对象指针->成员名

--

2

-

负号运算符

-表达式

右到左

单目运算符

~

按位取反运算符

~表达式

++

自增运算符

++变量名/变量名++

--

自减运算符

--变量名/变量名--

*

取值运算符

*指针变量

&

取地址运算符

&变量名

!

逻辑非运算符

!表达式

(类型)

强制类型转换

(数据类型)表达式

--

sizeof

长度运算符

sizeof(表达式)

--

3

/

表达式/表达式

左到右

双目运算符

*

表达式*表达式

%

余数(取模)

整型表达式%整型表达式

4

+

表达式+表达式

左到右

双目运算符

-

表达式-表达式

5

<<

左移

变量<<表达式

左到右

双目运算符

>>

右移

变量>>表达式

6

>

大于

表达式>表达式

左到右

双目运算符

>=

大于等于

表达式>=表达式

<

小于

表达式<表达式

<=

小于等于

表达式<=表达式

7

==

等于

表达式==表达式

左到右

双目运算符

!=

不等于

表达式!= 表达式

8

&

按位与

表达式&表达式

左到右

双目运算符

9

^

按位异或

表达式^表达式

左到右

双目运算符

10

|

按位或

表达式|表达式

左到右

双目运算符

11

&&

逻辑与

表达式&&表达式

左到右

双目运算符

12

||

逻辑或

表达式||表达式

左到右

双目运算符

13

?:

条件运算符

表达式1?

表达式2: 表达式3

右到左

三目运算符

14

=

赋值运算符

变量=表达式

右到左

--

/=

除后赋值

变量/=表达式

--

*=

乘后赋值

变量*=表达式

--

%=

取模后赋值

变量%=表达式

--

+=

加后赋值

变量+=表达式

--

-=

减后赋值

变量-=表达式

--

<<=

左移后赋值

变量<<=表达式

--

>>=

右移后赋值

变量>>=表达式

--

&=

按位与后赋值

变量&=表达式

--

^=

按位异或后赋值

变量^=表达式

--

|=

按位或后赋值

变量|=表达式

--

15

逗号运算符

表达式,表达式,…

左到右

--

2.5 关键字

C语言有32个关键字,关键字类型关键字列表:

    • 基本数据类型: signed unsigned char int float double short long void
    • 构造数据类型: struct union enum
    • 数据存储类别: auto static extern register
    • 数据优化: const volatile
    • 四种结构: if else switch case break default while do for return continue goto
    • 其它: typedef sizeof
2.5.1 基本数据类型
> 1、unsigned :无符号的         //用来声明一个无符号的变量。
    unsigned char var;        //var的范围:0~255

> 2、signed :有符号的(可以省略不写)        //用来声明一个有符号的变量。
    signed char var;             //var的范围:-128~127

> 3、char :字符型        //用来声明一个字符型变量,占一个字节空间。
    char var;

> 4、int :整型    //用来声明一个整型变量。C51:占两个字节空间,ARM:占四个字节
    //大小由编译器来决定(32位或64位操作系统下为4字节)
    int var; 

> 5、float :浮点型
    //用来声明一个浮点型(实型)变量。
    //最多能表示到7个有效数据位。占四个字节空间
    float var;

> 6、double :双精度型
    //用来声明一个双精度实型变量。
    //最多能表示到15~16个有效数据位。
    //占八个字节
    double var; 

> 7、short :短整型
    //用来声明一个短整型变量。
    //C51:跟int一样,ARM:占两个字节
    short var; 

> 8、long :长整型 
    //用来声明一个长整型变量。
    //ARM:跟int一样,C51:占四个字节
    long var; 

> 9、void :空型
    //表示一个函数没有返回值,或者无形参。
    void function(void);
2.5.2 构造数据类型
> 1、struct
    //用来声明一种结构体类型。里面的变量有顺序要求,字节对齐;
    struct stu{
        char sex;
        int age;
        float score;
        struct stu *Next;
    };
struct stu var; 

> 2、union
    //用来声明一种共用体类型。
    //该类型的变量所在空间的大小以其成员占最大的那个为准,
    //存入该变量中的值以程序中最后存入的数值为当前值
    union non{
        char sex;
        int age;
        float score;
    };
union non var; 

> 3、enum
    //用来声明一种枚举类型(整型常量集合)。
    //规定枚举类型的变量,只能在限定的范围内取值
    //否则,编译会出现警告(达到数据安全的效果)
    enum em 
    {a = 23,b,c,d = 56,e}; //其中b=24,c=25,e=57
enum em var;
2.5.3 数据的存储类别
> 1、auto :自动的(可省略不写)
    定义一个局部变量,默认为auto类型的,当它所在的函数调用结束时,释放内存
    使用时才分配内存,用完即释放
    auto char var; 

> 2、static :静态的
    //①定义一个局部变量,该变量在定义时只进行一次初始化,以后每次调用它所在的函数,其值
    //都会保持上一次调用的结果,它所在的空间不会被释放;   
    //②被static修饰的全局变量,则只能在它所在的C源文件中使用,其它文件不能调用(内部全局变量)
    //③被static修饰的函数,只能在该函数所在的C源文件中被调用,其它文件不能调用(内部函数)
    static char var;
static void function();

> 3、extern :外部的 
    //①想要调用别的C源文件中的某一个全局变量,可以使用该关键字在该文件中修饰声明该变量
    //即可调用(前提是该变量没有被static修饰),该类型的变量也是一直占着内存不释放   
    //②想要调用别的C源文件中的某一个函数,可以使用该关键字在该文件中修饰声明该函数
    //即可调用(前提是该函数没有被static修饰)
    extern char var;
extern void function();

> 4、register :寄存器的
    //被这个关键字修饰的变量,建议编译器将该变量在
    //使用时放到CPU内部寄存器中,以提高执行效率
    //注意:该关键字只是"建议",到底有没有将变量放到
    //寄存器中无从得知。一般使用在循环次数比较多的地方。
    //在使用时才分配内存,用完即释放
    register long i = 30000;
2.5.4 数据优化
> 1、const :常的
    //常变量:被const关键字声明的变量,其值不能被改变。
    //即在声明该变量时必须要初始化该变量。
    //var本身还是一个变量。(数据安全检测)
    const char var = 100;
char arr[var]; //试图声明一个长度为100的字符型数组
//在MDK (ARM)中可以使用常变量来声明数组的长度
//在VC++编译环境中也可以。
//在C51-keil中不可以使用常变量来声明数组的长度
char *const p;
//指针变量p不可改变,但是它指向的地址里面的值可变 
char const *p; 或 const char *p;
//指针变量p可以改变,但是它所指向的地址里面的值不能改变 
const char * const p; //p地址不可改变,里面的值也不能变

> 2、volatile :随时会改变的
 
    //被volatile修饰的变量或寄存器会意想不到地发生改变。
    //①某些变量或寄存器(如状态寄存器)可能会受硬件影响;
    //②在多线程任务中,被其它线程修改(共享内存段);
    //③一个在中断服务中使用到的全局变量
    //④编译器会对C程序进行优化;
    //为了保证程序不被优化,保证CPU取到的数据是最新的
    //(即提醒CPU每次都必须到内存中取出变量当前的值而不
    //是取cache或者寄存器中的备份),使用该关键字修饰,如:
    int *p; *p = 1; *p = 2;
//编译时,编译器会对上面的语句进行优化,
//会直接优化成:
int *p; *p = 2;
//为了保证上面的语句不被优化,加volatile修饰变量:
int * volatile p;
2.5.5 四种结构
四种结构:
> 1、顺序结构:0条 //声明语句、运算语句、赋值语句等等
 
> 2、选择结构:2条 //多选一 
    ①、if -else if -else if ... else
        if(表达式1) 
        {语句s;}
else if(表达式2) 
{语句s;}
else if(表达式3) 
{语句s;}
else
{语句s;}
//用法:顺序判断if后的"表达式"是否为真
//如果碰到为真的情况,则执行其下面的{}里的"语句"
//执行完后,即退出这个"多选一"的结构 

②、switch-case-break
    switch(变量) 
    {
        case 常量1:语句;...;break;
        case 常量2:语句;...;break;
        case 常量3:语句;...;break;
        default:语句; 
    }
//用法:顺序判断"变量"是否与"常量"相等,
//如果相等,则执行该常量:后的"语句s",遇到break即跳出这个结构
unsigned char i = 6;
unsigned char dat;
switch(i)
{
    case 3:dat = 5;break;
    case 5:dat = 34;break;
    case 6:dat = 99;break;
    case 7:dat = 56;break;
    case 6:dat = 100;break;
    default:dat = 68; //默认执行
}
//注:如果少了break,则顺序判断i的值与case后面的常量是否相等,如果相等,则执行其
//后面的语句,以后不再判断,再继续执行下面的每一条case 后面的语句,直到default.
//这种用法不使用!


> 3、循环结构:3条
 
    ①、for
    for(语句1;语句2;语句3) 
    {
        语句4;
        语句...;
    }
//用法:语句1:条件初始化
//     语句2:判断语句,判断条件是否成立
//     语句3:修改条件语句
//先执行语句1,再进行对语句2的判断,如果成立
//则执行{}里的语句4...,再执行语句3,在判断
//语句2是否依然成立,。当语句2不成立时,结束循环

②、while
    while(表达式) 
    {
        语句;
        ....;
    }
//用法:先判断“表达式”是否成立,如果成立
//则执行{}里的语句,执行完再次判断“表达式”
//是否依然成立,成立则继续执行{},不成立则结束
//此循环结构。

如何来设计一个死循环?两种方法:
    for(;;)
        while(1)

            ③、do-while
                do{
                    语句1;
                    ...;
                }while(表达式);
//用法:先执行{}里的语句,执行完后,判断
//"表达式"是否成立,如果成立,继续执行{};
//如果不成立,则结束循环

> 4、转移结构:4条 
    ①、break
    //仅用于跳出循环结构
    //且仅能跳出一层循环结构
    for(i=10;i>0;i--)
    {
        t = 10;
        while(t--)
        {
            dat++;
            if(dat == 3)
                break;//跳出while()结构。
        } 
    }

②、continue
    //用于终止本次循环,继续从下次循环开始,正式程序中不使用,仅用于调试程序
    char buf = 10;
while(buf--)
{ 
    a++;
    b++;
    continue;//遇到continue则结束这次循环
    d++; //这条永远都不会执行到 
    e++; //这条永远都不会执行到 
}

③、goto
    //无条件转移,一般都不建议在大的程序当中使用
    unsigned char dat=10;
while(dat--)
{ 
    a++;
    b++;
    Lable: c++;
    if(c == 3)
        goto Lable;//直接跳到Lable标号去 
    d++;
}

④、return
    //用于函数返回,
    //在函数调用时,遇到return即返回。
    //如果需要返回某个值,在其后加上返回值。
    //返回值类型必须和函数类型一致。
    void function()
{
    char a,b; 
    a++;
    return; //遇到return即返回调用处
    b++; //不会被执行
    return; //不会被执行
}
2.5.6 其他
> 1、typedef : 类型重定义
    typedef unsigned char uchar;
//用uchar重新定义unsigned char
#define uchar unsigned char
//用uchar 替换unsigned char
跟宏定义的区别:
    ①、typedef 是在编译阶段进行定义,宏定义是在预编译处理阶段完成展开
    ②、typedef 是类型重新定义,不是简单地替换,宏定义只是简单的替换,没有定义
    typedef unsigned char* M;
#define M unsigned char*
M p1,p2;
//对于typedef来说:p1是指针变量,p2也是
//对于宏定义来说:p1是指针变量,p2是普通变量

> 2、sizeof
    //变量类型或变量占据多少内存空间,返回字节数
    int var = 10;
char g; 
g = sizeof(var++);//g = 4; 注意:var++该条语句没有被执行!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值