计算机是如何给人类解决问题?
答: 问题域的数据保存起来,然后通过某些运算从而得到结果。
程序 = 算法 + 数据结构
首先考虑保存数据,在保存数据之前要知道数据的大小、范围和属性等,即数据类型
1. 数据类型
typeof(x) => 求对象x的类型
typeof(3) => int
typeof(3.0) => double
可以利用typoeof对变量进行定义。
#include <stdio.h>
int main()
{
typeof(1) a; // int a;
return 0;
}
在c语言中,有如下数据类型:
基本类型:
C语言中已经定义好的类型,主要用来保存整数、小数和浮点数。
整数
(signed) char/unsigned char 字符型 保存ASCII的值(整数值)
signed: 有符号,可省略,下文均省略
short/unsigned short 短整型
int/unsigned int 整型
long/unsigned long 长整型
…
上面的类型都是用来描述整数
区别:
a、signed/unsigned
有符号(signed):
符号位(最高位) + 数值位
1 --> 负数
0 --> 正数/0
无符号(unsigned):
所有bit位全部都是数值位(直接参与位运算)
整数用8bits来表示
如
1111 1110
=> unsigned(无符号):254
1111 1110
0000 0001 1
--------- +
1111 1111
0000 0001 1
--------- +
10000 0000 256
所以:1111 1110 = 256-1-1 = 254
=> signed(有符号):
计算方法如下:
负数 => |负数| => 取反 => +1 => 负数补码
负数补码 => -1 => 取反 => |负数| => 负数
1111 1110
0000 0001 1
--------- -1
1111 1101
0000 0010 取反(~)
0000 0010 |2|
1111 1110 -2
1111 1110
-1: 1111 1101
取反: 0000 0010
|2|: 0000 0010
-2: 1111 1101
b、char/short/int/long/…
所占内存空间大小不同
数据类型 | 所占空间大小 | 取值范围 | 二进制取值范围 |
---|---|---|---|
char | 8bits(1byte) | [-128,127] | 1000 0000 – 0111 1111 |
unsigned char | 8bits(1byte) | [0,255] | 0000 0000 – 1111 1111 |
short/unsigned short | 在32位机器下,一般为32bits(4bytes);在64位机器下,一般为32bits(4bytes) | - | - |
long/unsigned long | 在32位机器下,一般为32bits(4bytes);在64 位机器下,一般为64bits(8bytes) | - | - |
c、在不同的编译器下面,同一类型的范围也不一样
keil
int 16bits(2bytes)
unbuntu 18.04
int 32bits(4bytes)
sizeof(x) 求类型x所占的字节数,默认为长整型(%ld)
1bytes = 8bits
一般整型类型的所占内存空间大小关系
sizeof(long) >= sizeof(int) >= sizeof(short) >= sizeof(char)
d、在gcc编译器中,整数默认类型为 int
typeof(3) => int
typeof(3+4) => int
typeof(3+4.0) +> double
小数(浮点数)
数据类型 | 名称 |
---|---|
float | 单精度浮点型 |
double | 双精度浮点型 |
long double | 长双精度浮点型 |
区别:
a、类型不同,所占空间不同,保存精度也不同
一般来说:
sizeof(float) => 4bytes
sizeof(double) => 8bytes
sizeof(long double) => 16bytes(64位机器)
b、在gcc编译器中,浮点数默认类型为double
typeof(2…0) => double
typeof(2.0f) => float
typeof(2) => int
typeof(2u) => unsigned int
typeof(2.0 + 3) => double
构造类型
自定义类型
数组: 一组相同类型元素的数据集合
int a[10]; //定义一个数组,数组名为a,含有10个int类型的元素
定义变量: 变量类型 + 名字
定义一个像a一样的对象m
typeof(a) m;
结构体
自定义的一种组合类型
struct 结构体名
{
成员类型1 成员名1;
成员类型2 成员名2;
…
成员类型n 成员名n;
};
结构体类型是由多个成员构成,而且每个成员都有自己的类型(只要是合法的C语言类型都可以),当然不同的成员,类型可以相同,成员名不能相同。
后续会详细讲解结构体
联合体(共用体)
联合体(公用体)的存储空间是各成员之间公用的
同一时刻只能用一个成员变量,为了节省空间才提出 共用体
union 共用体名
{
成员类型1 成员名1;
成员类型2 成员名2;
…
成员类型n 成员名n;
};
后续会详细讲解共用体
共用体和结构体最大的区别:
结构体所占内存大小是各成员变量值和
共用体所占内存大小是各成员变量之间那个最大成员所占的内存空间
枚举
把该类型变量所有可能的值一一列举出来。
所以枚举类型一定是可以一一列举的值,也就是整数值。
enum 枚举类型名
{
枚举具体值
};
C语言实现枚举时,枚举的具体的值是用一个整数值来表示的。
后续会详细讲解枚举
指针类型
指针变量: 保存一个对象的地址的变量
指针变量也是一个变量,在定义指针变量的时候,为了区分,在指针变量的前面加一个"*"表示是一个指针变量
指向的对象的类型 * 指针类型名
后续会详细讲解指针
void类型
void 在C语言中,有三个作用:
1. void*
通用指针,用来存放任何数据类型的引用(指针)
当不清楚所需指针类型时使用
void*
char*
int*
short*
…
malloc(100); //与C++中new相同 返回void*类型指针,占空间用
开辟100字节大小的空间
在指针中会详细讲解malloc这个函数
2. void当作函数的参数
表示此函数没有参数,不需要传参,可省略
int func(void){}
调用
函数名(实参)
func(3, 4); //ERROR func函数的参数为void 不需要穿参
func(); //TRUE 正确调用
3. void 当作函数的返回值类型
表示函数没有返回值,不需要返回值
void func(int m, int n)
{
return ; //TRUE
return 0; //ERROR 该句话表示,退出函数,并返回0,func函数的返回值类型为void,不需要返回值
}
2.变量和常量
在C语言中,数据分为两大类:变量和常量
变量
什么是变量?
在程序运行期间,可以改变其值的数据对象
存储变量会对应一个存储单元且存储单元是可写的
变量的定义
变量在使用前必须定义
语法
变量类型 变量名 (= 初值);
可以赋值也可以不赋值
变量类型
基本类型、构造类型、指针类型
变量名
一个对象的名字,标识符(对象的名字,可以是变量、数组或函数)
在C语言中,C语言标识符: 只能由字母、下划线和数组组成,但是数字不能开头
为什么数字不能开头
int 2E3; //2E3 科学记数法 2000
如果在定义变量时,没有给变量赋初值,编译器会随机赋一个值给这个变量
int a;
printf("%d\n, a"); //打印随机值,由编译器决定
变量的属性
int a = 1024;
int: 变量类型
a: 变量名
1024: 变量值(存储空间中保存的内容)
&a: 变量的地址
一个变量有如下属性:
变量类型
变量名
变量值(保存到存储空间中的内容)
变量的存储空间中的内容,不管是否初始化或赋值,空间中一定会有一个值,因为存储空间中所有的bit为都有值,只为0或1
变量的地址:
在程序运行时,程序会为每一个变量分配一个存储空间,用来保存这个变量的值,且这个存储空间一定会有一个唯一的编号,那么这个编号就是变量的地址
变量的访问(read/write)
int b, a=5;
a = 1024; //把1024写到变量a所对应的地址中 将数值写到a的地址 ---- (1)
b = a*5; //把a的值乘5,赋值给b 读取变量a的值 ---- (2)
读(read): 从变量的存储单元中读取存储单元中的内容,读变量的值
写(write): 把一个值写到变量所对应的存储单元中 赋值
在C语言中,任何变量有且只有两层含义:
1. 变量的地址
lvalue:
location value 可寻址(可写)的值
left value 左值
1. 变量的值:
rvalue:
readable value 可读的值
right value 右值
一般情况
变量的左值:
变量的地址,"=“的左边
变量的右边:
变量的值,”="的右边
整数
在计算机中整数是以其补码形式保存的
整数有正数、0和负数
正数/0
整数的补码就是其原码的本身
原码:就是相对应的数值转换为二进制形式
13: int 32bit
13=8+4+1
00000000 00000000 000000000 00001101(在后续为了方便会对部分0进行省略)
int a=13;
a:
00000000 00001101
负数
负数的补码 其原码的绝对值 取反 +1
-13 int 32bits
|-13|: 00000000 0000000 0000000 00001101
取反: 11111111 11111111 11111111 11110010
+1: 11111111 11111111 11111111 11110011
-13的补码: 11111111 11111111 11111111 11110011
8bit 存储:
-2: 1111 1110
254: 1111 1110
-3: 1111 1101
253: 1111 1101
-4: 1111 1100
252: 1111 1100
一个负数会和一个正整数以一样的形式存放在计算机中
即
-x 和 (2^n - x) 相同 n表示用n个bits存储一个整数
n=32时
-1和2^32-1相同
CPU内无符号位,对CPU来说,所有的bit位都是数值位参与运算
int a = -13;
a在计算机中的存储形式为
11111111 11111111 11111111 11110011
printf("%d\n", a); //-13
%d:有符号数
符号位: 1
负数补码: 11111111 11111111 11111111 11110011
减1: 11111111 11111111 11111111 11110010
取反: 00000000 00000000 000000000 00001101
|-13|: 00000000 00000000 000000000 00001101
负数: -13
printf("%u\n", a); //2^32 - 13
%u: 无符号数 正数/0
1111111 11111111 11111111 11110011 既是补码也是原码
2^32 - 13
unsigned int a = -1u;
0u表示无符号整型0,1u表示无符号整型1
-1u表示无符号整型的最大值,即32个bit为全为1
-1u: 11111111 11111111 11111111 11111111
a: 11111111 11111111 11111111 11111111
printf("%d\n", a); //-1
printf("%u\n", a); //2^32-1
untsigned int a = 1<<31;
1<<31: 10000000 00000000 00000000 00000000
printf("%d\n", a); //-2^31
%d: 有符号数
-1: 01111111 11111111 11111111 11111111
取反: 10000000 00000000 00000000 00000000
|-2^31|
-2^31
printf("%u\n", a); //2^31
%u: 无符号数
10000000 00000000 00000000 00000000
2^31 = 2^32 - 2^31
在GNU有一个标准头文件 stdint.h 中定义了几种扩展的整数类型和宏
路径: /user/include/stdint.h
整数之间赋值
char a = 123;
int b = 1024;
a = b; //不会报错,但结果会出错
在C语言中,允许不同类型之间整数相互赋值,不同类型的整数,存储空间大小不同
char 8bits
short 16bits
int 32bits
…
C标准建议
1. 长变短
低字节直接拷贝,高字节部分全部舍弃 无法保存
2.短变长
低字节直接拷贝
高字节:如果短的是无符号为,高位全部补0;如果短的是有符号数,高位补符号位
从右往左运算
char a = 127;
int b = 1024;
a = b;
printf(%d\n", a); //0
printf("%u\n", a); //0
a: 01111111
127: 00000000 00000000 00000000 01111111
长变短 高字节直接舍弃
b: 1024 2^10
b: 00000000 00000000 00000100 00000000
a=b
a:char类型 b:int类型
int -> char 长变短
a: 00000000
%d:有符号位,符号位位0 正数 值为0
%u:无符号 值为0
char a= 250;
char d;
d = a+8;
printf("%d\n", d); //2
printf("%u\n", d); //2
250: 256 - 2 - 4
00000000 00000000 00000001 00000000 256
00000000 00000000 00000000 00000010 2
--------------------------------- -
00000000 00000000 00000000 11111110
00000000 00000000 00000000 00000100 4
--------------------------------- -
00000000 00000000 00000000 11111010
250: 0000000 00000000 0000000 11111010
a:11111010 长变短
d = a+8;
a char 与 8 int型进行加减 先短变长
11111111 11111111 11111111 11111010 a (int)
00000000 00000000 00000000 00001000 8
----------------------------------- +
00000000 00000000 00000000 00000010 a+8
d是char型 长变短
d: 00000010
%d: 2
%u: 2
常量
常量是在程序运行期间,值不能改变
1. 整型常量
代表整数的常量值
八进制常量: 以0开头,后接多个0-7字符组成
0[0-7]+ (正则表达式的表示方法)
如:
0000
0123
0923 //ERROR 八进制中无9
int a = 0123;
printf(“%d\n”, a); //八进制转换十进制
十六进制常量: 以0x和0X开头,后面接多个或者一个0-9, a-f,A-F字符
0[x/X][0-9, a-f, A-F]+
如:
0xF
012F //ERROR 八进制中无F
int a = 0xf3
printf(“%d\n”, a); //十六进制转十进制 253
十进制常量:
[0-9]+
2. 字符常量
字符常量是用单引号引起来的一个或多个字符的序列
如:
‘a’
‘A’
…
‘\n’ 换行
‘\r’ 回车
‘\t’ 制表
…
‘\0’ ASCII为0 对应的字符
在计算机中,保存一个字符,保存的是字符对应的ASCII码值
ASCII:
American Standard Code for Information Interchange
漂亮国把每个字符给唯一的整数值来表示,这个整数值就是ASCII码。由于漂亮国使用字符不超过256个,所以只需要8bits就可以保存。
使用man ASCII
可查看相关信息
ASCII码的主要内容有:
Oct: 八进制
Dec: 十进制
Hex: 十六进制
Char: 字符
“A” - “Z” : 65-90
“a” - “z” : 97-122
“0” - “9” : 48-57
字符分为两种
a、普通字符
可以打印的字符,有形状的字符
b、 特殊字符(转义字符)
不可打印的字符
如:
‘\n’ 换行符
‘\t’ 制表符
‘\r’ 回车符
…
‘\000’:由\
后面接一个、两个或三个八进制的数字组成,这些八进制数字用来制定所期望的字符的ASXII码值
如: ‘\101’ <=> 65 <=> 0x41 <=> ‘A’
‘\0x00’: 由\x
后面接一个或两个十六进制数字组成。十六进制数字用来制定所期望的字符的ASCII值
3.浮点常量
由整数部分、小数点、小数部分、一个e/E、一个可选的带符号的整数指数 和 一个可选的表示类型的后缀(f/F/l/L)组成
f/F: float
l/L: long double
没有后缀: double
整数部分: 可省略
小数部分: 可省略
但是小数部分 和 整数部分不能同时省略
如:
float f = 2.3E3; // 2.3*10^3
float f = .3E3; // 0.3*10^3
float f = 5E-5; //5.0*10^-5
float f = E5; //ERROR 小数和整数部分不能同时省略(与变量无法区分)
4. 枚举常量
枚举类型是整数值,可列举的常量值
5. 符号常量
宏
#define 宏名 替换对象
如: #define PI 3.14