想学好嵌入式,那么就从c语言开始学习!
0.问题引入
面对过程(c语言)
程序组成:
程序 = 算法 + 数据结构
计算机首先要解决数据保存的问题,在数据保存之前,
我们是不是要知道这个数据的大小,取值的范围。。。。
不然我们怎么知道要开辟多大的空间呢?
数据的大小,取值范围。。。都是我们数据类型要讨论的问题。
1.数据类型
typeof(x) => 求对象x的类型 //typeof(运算符)
x:可以是常量,变量,函数。。。
如: 3,3.0,a,func
例子:
typeof(3) =>求常量3的类型
=> int
typeof(3.0) =>求常量3.0的类型
=> double
#include <stdio.h>
int main()
{
int a;
//定义一个和a一样类型的变量b,该如何定义?
typeof(a) b; // int b;
}
在c语言中,有如下数据类型:
(1)基本类型:
c语言已经为我们定义好了的类型
主要用来保存数(整数/浮点数)
a,整数类型
signed char/unsigned char (字符型 -保存的是字符对应的ASCII的值)
(什么是ASCII码? 是字符的整数形式,是另外的一种标准吗,任意一个字符都对应一个整数)
short / unsigned short (短整型)
long /unsigned long (长整型)
int /unsigned int (整型)
上面的这些类型都是用来保存整数值的,
那么他们之间有什么区别呢?
1) signed/unsigned
有符号(signed) :符号位(最高位) +数值位
1 - 负数
0 - 正数
无符号位(unsigned): 没有符号位的区别,所有的bit都是数值位
2)char /short /int ....
所占的内存空间大小不一样
那么他们的取值范围也是不一样
char : 8bits ->1Bytes
取值范围
[-128,127]
127:0111 1111
-128:1000 0000
unsigned char:8bits ->1Bytes
取值范围:
[0,255]
255:1111 1111
0: 0000 0000
short /unsigned short:一般占16bits(一般是2个字节)
int / unsigned int:占4个字节
int :
[-2^31,2^31-1]
-2^31:
10000000 00000000 00000000 00000000
2^31-1:
01111111 11111111 11111111 11111111
unsigned int:
[0,2^32-1]
0: 00000000 00000000 00000000 00000000
2^32-1:
11111111 11111111 11111111 11111111
long /unsigned long:
32bits的机器上面,一般占4个字节,32bits
64bits的机器上面,一般占8个字节,64bits
3)在不同编译器下面,同一类型的范围(所占空间)可能不一样
如:
keil:
int 16bits
ubuntu:
int 32bits
sizeof(x):求类型x的所占字节数, 1Byte = 8bits
sizeof(char) == 1
sizeof(short) ==2
sizeof(int) == 4
sizeof(long) == 4/8
4)在GCC编译器,整数默认类型
整数默认类型: int
int a;
typeof(a) => int
typeof(3+4) => int
typeof(3+4.0) =>double
"自动向高精度转换" =>自动向所占空间大的转换
b,浮点类型:用来保存小数的
float : 单精度浮点类型
double : 双精度浮点类型
long double: 长双精度浮点类型
上面的这些类型都是用来保存小数值的,
那么他们之间有什么区别呢?
所占空间大小不一样,保存的小数的精度也不一样
一般来说:
sizeof(float) == 4
sizeof(double) == 8
sizeof(long double) ==16
练习:
请大家讲上述所学的基本类型,
在ubuntu(64bits)中所占的字节数打印出来!
注意:打印sizeof的返回值时使用 %ld 打印!!!
#include <stdio.h> --->头文件,包含了printf,scanf等函数
int main()
{
printf("typeof(int)=%ld\n",sizeof(int));
printf("typeof(char)=%ld\n",sizeof(char));
printf("typeof(long int)=%ld\n",sizeof(long int));
printf("typeof(short)=%ld\n",sizeof(short));
printf("typeof(float)=%ld\n",sizeof(float));
printf("typeof(double)=%ld\n",sizeof(double));
printf("typeof(long double)=%ld\n",sizeof(long double));
return 0; ---->返回给计算机看的,有没有return 0,都没有关系
}
(2)构造类型:
c语言允许程序员自定义类型
数组: 一组相同类型元素的数据集合,用一组连续的存储空间来保存的
int a[10];//定义了一个数组,数组名为a,里面有10个int类型的元素
//定义a的同时,其实也声明了一个新的类型(像a一样的类型)
typeof(a[0]) => int
typeof(a):求数组a的类型
a是一个里面含有十个int类型数据元素的数组
在笔记中,咱们可以这样来描述a的类型:
typeof(a)
==> int[10]
结构体
联合体/共用体
枚举
(3)指针类型
先wait,wait,后面的专题讲
(4)void类型
void在c语言中,有三个作用
a,void*
通用指针,用来存放任何数据类型的指针
b,void当作函数的参数
表示此函数无需参数
如:
int func(void)
{
}
调用:函数名 (实际函数)
func()
c,void当作函数的返回值的时候
表示此函数没有返回值
如:
void func(int a,int b)
{
int sum;
sum = a+b;
return sum;//???ERROR ,因为void作为函数的返回值类型,表示此函数没有返回值
}
在研究了数据类型后,下面要保存数据
在C语言中,数据分为两大类:
(1) 变量
(2) 常量
2.变量
what is 变量?
在程序运行期间,可以改变其值的数据对象!
保存一个变量,那么这个变量就需要一个对应的存储单元
当然这个存储单元一定是可写(因为我们需要去改变这个变量的值)
任何一个对象的名字,必须要在定义这个对象之后才能去使用
所以咱们想要使用一个变量的话,首先就应该去定义它。
2.1变量的定义
语法:
变量的类型 变量名 ; //没有初始化
int a;
变量的类型 变量名 = 变量的初始值 ; //初始化了的
例子:
char c;
or
char c = 'A';
“变量的类型” :只要是合法的c语言类型都可以
变量名 : 一个对象的名字,标识符
标识符其实是一个对象的名字
标识符的规则;
只能由字母,下划线和数字组成,并且不能做开头
float 2E3; //2乘以10的3次幂
例子:
A,CAIXUNKUN_666 //OBJK
B,666_CAIXUNKUN //不可以
C,_CAIXUNKUN-666 //不可以
D,-CAIXUNKUN666 //不可以
”名如其意“
int sum; // 求和
int sum_array ; //求数组元素之和
int he;
int shuzu_he;
变量的初始值:
定义变量时,赋值给变量的值,初始值
例子:
int a;
printf("a==%d\n",a); // a==? undefine 未知的
//现在的编译器聪明了,要是你没有初始化,他会自动给你初始化为0.
2.2变量的属性
变量的类型
变量名
变量的存储单元(变量的地址)
在程序运行时,系统会给每一个变量分配一个存储空间,
用来保存这个变量的值,并且这个存储空间会有一个唯一
的编号,这个编号,称之为变量的地址
变量的值:
变量的存储单元中的内容,
每一个变量都会有一个确定的值(不管你对这个变量有没有进行赋值)
因为在存储单元中,bit要么是1/0
2.3变量的访问
读/写
读:从变量的存储单元中读取变量的内容
写:把一个数值写到变量所对应的存储单元中去。
”赋值“
如:
int a =5; //将5,写道变量a对应的存储单元中去
int b = a; //读取变量a的值,赋值给b
上面的两个语句中的a都是同一个,但是他们所表达的含义是不一样的!!!
结论:
在c语言中,任何的变量,都有且只有两层含义:
(1)代表变量的地址
lvalue:
左值
left value
(2)代表变量的值
rvalue
右值
right value
变量的左值:
变量的地址,一般是在”=“的左边
变量的右值:
变量的值,一般是在”=“的右边
例子;
int a =5; //a代表的是lvalue
printf("%d\n",a);//a代表的是右值 rvalue
3.整数的存储问题
整数在计算机中是如何存放的呢?
整数是以二进制的补码形式存放的
正数/0;
正数的补码就是其原码本身
原码:
就是把对应的数值转换成二进制形式
13:
00000000 00000000 00000000 00001101 <- 13的原码,也是13的补码,也就是13在计算机中的
存放形式
9:用8bits
0000 100
负数:
负数的补码是其 绝对值的原码 取反 +1 得到
-13:8bits
|-13|=13的原码
0000 1101 -> 绝对值的原码
1111 0010 -> 取反(0->1,1->0)
1111 0011 -> +1
就是-13的补码,也就是-13在计算机中的存储形式
一个有趣的现象,假设8bits来保存一个整数
-2的存放形式
254的存放形式
1111 1110
-3的存放形式
253的存放形式
1111 1101
-4的存放形式
252的存放形式
1111 1100
结论1:
一个负数会和一个比较大的正整数的补码形式一样。
说白了就是他们在计算机中的存储形式是一样的
-x 和 (2^n -x)一样
n:表示用多少个bits来存储一个整数
结论2;
CPU内部是没有符号位的概念的,对于CPU来说,所有的
bit位都是数值位,都参与运算,至于是有符号的,还是无符号的
就看编译器。(意思就是说,你把它当作是一个有符号的还是无符号的)
例子
int a = -4;
a在计算机中的存储形式; 2^32 -4
11111111 11111111 11111111 11111100
printf("%d\n",a);// -4
%d:有符号输出
练习:分析如下程序输出结果
1.
int a = -2;
printf("%d\n",a); // -2
%d :
有符号的数
printf("%u\n",a); //2^32 -2
%u :
无符号的数
2.
int a =-56;
printf("%d\n",a); // -56
printf("%u\n",a); // 2^32-56
4.整数之间赋值的问题
C语言中,允许不同类型之间的整数进行赋值
char ->int
int ->char
char ->unsigned short
不同类型的整数,存储空间大小是不一样的
char 8bits
short 16bits
这个问题该怎么解决呢?
C标准建议:
(1)长->短
长的赋值给短的,低字节直接拷贝,
高字节全部丢弃,
例子:
char c =250;
typeof(250) => int
typeof(c) =>char
250:
00000000 00000000 00000000 11111010
c: 11111010
(2)短->长
短的赋值给长的,低字节直接拷贝
高字节补什么?
如果短的是无符号的数,高位就直接补0
如果短的是有符号的数,高位补符号位
例子:
int d;
char c =250;
d = c+8;
typeof(c+8) =>int
c(char) ->int;
c:11111010
->11111111 11111111 11111111 11111010
8:
00000000 00000000 00000000 00001000
5.常量
常量是指在程序运行期间,其值不能够改变的数据对象
常量在代码中有几种情况出现:
(1)整型常量:
在代码中,代表整数的常量值
八进制常量:
0[0-7]
以字符0开头后面接0个或者多个0~7字符
如:
0123
0777
0128 ->不是
八进制与二进制对应的一个关系
一个八进制的位 对应三个二进制位
0 000
1 001
7 111
十六进制:
0[xX][0-9 a-f/A-F]
以0x 或者0X开头,后面接一个或者是多个0~9 A~F/a~f
int a=83;
<=> int a= 0x53;//十六进制
十六进制与二进制对应的一个关系
一个十六进制的位 对应 四个二进制位
a(A) 1010
b(B) 1011
在c语言代码中,不可能出现二进制形式
int a =01010011;//计算机会将其认为是 八进制形式
int a =11111111;//计算机会将其认为是 十进制形式
(2)字符型常量
字符型常量是用 ‘’(单引号)引起来的一个或者多个字符的序列
如:
‘a’ =>97
'A' =>65
'\n' 换行符
‘\r’ 回车符
人为把字符分成了两类:
a.普通字符
可以打印的字符,有形状的字符
‘0’ -‘9’
‘A’ -‘Z’
‘a’ -‘z’
b.特殊字符(转义字符)
如:
‘\n’ :换行符
‘\r’ : 回车符
‘\t’ : 制表符
....
(3)浮点常量
由整数部分,小数点,小数部分;一个e/E,一个可以选的带符号的整数指数 和
一个可选的表示类型的后缀(f/l)
f/F : float
l : long double
如;
float f = 2.3E3; //OBJK 2.3乘以10^3
float f = .3E3; //OBJK 0.3乘以10^3
(4)枚举常量
wait wait 等等
练习:
1.
char c = 250;
int d;
d = c+8;
250(int):
00000000 00000000 00000000 11111010
c:
长->短 , 1111 1010
printf("%d\n",c); //-6
d=c+8;
c+8 =>int:
c(char) ->c(int):
11111111 11111111 11111111 11111010
8:
00000000 00000000 00000000 00001000
+
1 00000000 00000000 00000000 00000010
d(int)
:00000000 00000000 00000000 00000010 <---d的最终存放形式
printf("%d\n",d);//2
printf("%u\n",d);//2
2.
unsigned char c=250;
int d;
d = c+8;
250:
00000000 00000000 00000000 11111010
c : 长 ---> 短
1111 1010
c+8 ==> int
c(u char) --> c(int)
00000000 00000000 00000000 11111010
8:
00000000 00000000 00000000 00001000
+
00000000 00000000 00000001 00000010
d(int):00000000 00000000 00000001 00000010 <----最终存放形式
printf("%d\n",d);//258
printf("%u\n",d);//258
3.
char c = -3;
printf("%d\n",c);//-3
printf("%u\n",c);//2^32-3
4.
unsigned char c = -3;
-3(int):
11111111 11111111 11111111 11111101
c(u char):
长--> 短
11111101
%d : int
%u : unsigned int
c(u char)->c(int):短--->长
00000000 00000000 00000000 11111101 <---c的最终存放形式
printf("%d\n",c);//253
printf("%u\n",c);//253
6.总结
(1)c语言中数据类型 64位机器下
a,基本类型
整数类型
char /unsigned char 8bits
short 16bits
int 32bits
long 64bits
有符号和无符号的区别
最高位!!!
每一个类型对应的取值范围
整数默认的类型:
int
浮点类型
float 32bits
double 64bits
long double 128bits
浮点数的默认类型:
double
b,构造类型
数组
结构体
共用体
枚举
c,指针类型
d,void类型
a,void* 通用指针
b, void当作函数的参数
c, void当作函数的返回值
(2)变量
2.1变量的定义
2.2变量的属性
2.3变量的访问
read/write
read:读变量的值,(rvalue)
write: 写到变量的地址中去,代表的是变量的地址(lvalue)
(3)整数的存储问题
正数
负数
结论一;
-x和2^n-x的补码形式一致
结论二
在CPU内部没有符号位的概念
(4)整数之间赋值的问题
长->短:
低字节直接拷贝,高字节舍弃
短->长:
低字节拷贝,空余的高字节部分补
看短的数据是无符号的数,还是有符号的数
如果是无符号的数,直接补 0
如果是有符号的数,直接补符号位
(5)常量
a,整型常量
八进制以0开头
十六进制以0x开头
十进制
b,字符常量
保存的不是字符的形状,而是这个字符对应的ASCII码的值!
1)普通字符
2)转义字符
c,浮点常量
d,枚举常量
小练习:假设是在64bits机器下面
分析如下程序的输出结果
(1)
printf("%d\n",-1);//-1
printf("%u\n",-1);//2^32 -1
printf("%d\n",(char)-1);//-1
(char)-1 ==>将-1(int)强制转换为char
长-->短的(低字节直接拷贝,高字节舍弃)
printf("%u\n",(char)-1);//2^32 -1
printf("%d\n",(unsigned char)-1); //255
printf("%u\n",(unsigned char)-1); //255
(2)
printf("%d\n",255); //255
printf("%u\n",255); //255
printf("%d\n",(char)255); // -1
printf("%u\n",(char)255); // 2^32 -1
printf("%d\n",(unsigned char)255); // 255
printf("%u\n",(unsigned char)255); // 255