目录
一、第一个C程序
嵌入式为什么学习C语言?
C语言可以操作内存,C语言中有指针
C语言的源文件xxx.c,vim xx.c
编程语言的分类
编译型语言:C,C++
解释型语言:shell,python
#include <stdio.h>
int main(int argc, const char *argv[])
{
printf("hello world\n");
return 0;
}
#:预处理标识符
include:包含头文件的标准格式
<>:从C语言的标准库中找头文件
"":从指定的路径中找头文件,一般用于导入自己写的头文件,如果找不到指定的头文件就去标准库中找
stdio.h:标准输入输出的库文件
int:函数的返回值类型
main:函数名,主函数,是程序的入口,每一个C程序有且仅有一个主函数
(int argc, const char *argv[]):()函数的参数列表,int argc, const char *argv[]主函数的外部参数
{}:函数体
printf("hello world\n"); //printf是标准输出函数
printf中""内的内容,除了转义字符和占位符外,都原封不动的输出
\n是转义字符,换行
;每一条语句,都要有一个;,C语言中语句结束的标志
return:返回函数的返回值
0:函数的返回值
//单行注释
/*
多行注释
*/
#if 0
条件注释
#endif
二、gcc编译器
gcc编译器,是GNU的一个产品
【1】一步编译
gcc xxx.c ---->默认生成一个名为a.out的可执行文件 -----> ./可执行文件名 (./a.out)
-o参数
多个C程序只能保留同一个a.out,生成其他的可执行文件,实现可以同时保留多个源文件可执行文件。
gcc xxx.c -o hello ----->gcc编译后生成指定的hello可执行文件 -o<==>out
./hello
gcc -o hello xxx.c ---->生成指定的hello文件,./hello
【2】分步编译 ESc->iso
预处理---->编译----->汇编----->链接
i)预处理 -E -o xx.i
gcc -E hello.c -o hello.i
展开头文件,替换宏定义,删除注释,不检查语法错误,生成编译文件
ii)编译 -S -o xx.s
gcc -S hello.i -o hello.s
检查语法错误,生成汇编文件
iii)汇编 -c -o xx.o
gcc -c hello.s -o hello.o
生成二进制文件
iv)链接
gcc hello.o -o hello
链接到程序执行所需要的动态库/静态库,生成可执行文件
运行程序, ./hello
三、计算机中的数据存储
【1】计算机中的存储单位
存储的最小单位1bit
内存分配的最小单位1Byte
1Byte = 8bit
1Kb = 1024Byte
1Mb = 1024Kb
1Gb = 1024Mb
1Tb = 1024Gb
【2】进制转换
二进制、八进制、十进制、十六进制
二进制 | 八进制 | 十进制 | 十六进制 | |
基数 | 2 | 8 | 10 | 16 |
数码 | 0、1 | 0--7 | 0--9 | 0--16 |
位权 | 2^0,2^1··· | 8^0,8^1··· | 10^0,10^1··· | 16^0,16^1··· |
前导符 | 0b | 0 | 0x |
i)二进制转十进制
二进制转十进制:用数码*该位的位权求和
8421
1001 ----> 1*2^0+1*2^3 = 9
1101 1110 = 14+64+128+16 = 222
0110 0011 = 99
ii)二进制转八进制
1111 1111
三位二进制数可以换一位八进制数
0010 1101 -------> 055
0111 1001 -------> 0171
1111 0010 -------> 0362
iii)二进制转16进制
每四位二进制换一位16进制
1111 0010 0xF2
0111 1001 0x79
0010 1101 0x2D
iv)十进制转N进制
利用辗转相除法,N是除数,拿到每次的余数
四、数据类型
给不同的数据,提供一个模板,决定分配空间的大小
数值类型数据,非数值类型的数据
【1】C语言中的数据类型
基本数据类型:int、float、long int·····
构造数据类型:数组、结构体、共用体···
指针:*
viod:空类型
【2】整形
数据类型 | 大小 | 占位符 |
int | 4Byte | %d |
long int | 64位占8Byte | %ld |
long long int | 8Byte | %ld |
short int | 2Byte | %d |
【3】浮点型
浮点型数据在计算机中存储的是近似值,常规代码中使用printf输出,默认只能输出小数点后6位
数据类型 | 大小 | 占位符 |
float(单精度浮点型) | 4Byte,可以表示小数点后6位 | %f |
double(双精度浮点型) | 8Byte,可以表示小数点后16位 | %lf |
【4】字符类型
数据类型 | 大小 | 占位符 |
char | 1Byte | %c,输出字符类型'a''A' %d,输出字符的ASCII码 |
非数值型的数据,在计算机中存储时,会转换成ASCII码存储。
在虚拟机中直接man ASCII就能显示ASCII码表
【5】enum枚举类型
五、常量
常量指的是,程序运行过程中不能改变的量
【1】整型常量
100、19····整数
整形常量默认是int
占位符 %d
【2】浮点型常量
3.14·····常见的小数
浮点型常量默认是double类型
占位符 %lf
【3】字符型常量
'a'、'b'、'c'·····
由单引号括起来的单个字符
占位符:%c 输出字符
%d 输出ASCII码
【4】字符串常量
C语言中不支持字符串类型,字符串一般使用字符数组存储
"hello world","h"······
由双引号括起来的一个或多个字符
占位符 %s
【5】宏定义常量
i)无参宏定义
#define 宏名 宏体
宏定义就是宏体简单替换宏名的过程
#define N 10
#预处理标识符,说明宏定义在预处理阶段被替换
注意事项:
- 宏定义的位置一般在头文件下方,主函数的上方
- 宏名一般大写
- 宏定义是简单替换的过程
- 如果宏定义的参数想要替换成字符串,需要在宏体中给参数加#
- 宏定义不需要加;
ii)有参宏定义
#define 宏名(参数1,参数2,····) 宏体
#defin ADD(a,b) a+b ---->可以实现简单的运算
#在宏定义中的使用,把带参宏的参数替换成字符串
当给带参宏的参数,添加#后,带参宏的参数就会被替换成字符串
六、变量
程序运行过程中,能够改变的值
【1】定义
存储类型 数据类型 变量名;
存储类型:(C基础阶段一般不写存储类型)
- auto:自动类型,局部变量默认是auto,全局变量不能使用auto修饰
- extern:引入外部文件中的变量,全局变量默认是extern类型
- static:静态,
- const:给变量常属性,const修饰的变量值不能被修改
- register:寄存器类型,把变量定义到寄存器中,会提高访问速度,寄存器类型的变量不能取地址
- volatile:每次从内存中取值
数据类型:基本数据类型
变量名: 也叫标识符,要符合标识符的命名规范.
标识符的命名规范
- 由数字、字母和下划线构成
- 不能由数字开头
- 严格区分大小写
- 不能和C语言的32个关键字重复
【2】变量的初始化和赋值
初始化:定义的同时给初始值
赋值:给已有变量赋值
#include <stdio.h>
int main(int argc, const char *argv[])
{
int var1; //定义了一个整形的变量var1,并且没有初始化,var1存的是随机值
printf("var1=%d\n",var1);
int var2 = 90; //定义了一个整形变量var2并初始化为90
var1 = var2; //使用已有的变量var2给变量var1赋值
var2 = 89; //给变量var2赋值为89
printf("var1=%d\n",var1);
printf("var2=%d\n",var2);
int a1=100,a2=79,a3=3,a4; //连续定义了多个整形变量,并给某些变量初始化
return 0;
}
变量使用前一定要定义出来,如果不确定变量的值,可以初始化为0
【3】强制类型转换
i)隐式的强制类型转换
看不见转换过程的强转,不安全的,由编译器主动来做的,是否报错,取决于编译器是否严格
#include <stdio.h>
int main(int argc, const char *argv[])
{
/* int a = 9.8; //隐式的强制类型转换,看不见强转的过程
printf("%d\n",a); //9,小数部分发生数据的截取
*/
int var = 0x12345678;
printf("%#x\n",var); //0x12345678
short a1 = var;
printf("%#X\n",a1); //0x5678
//大的数据类型int,向小的数据类型short强转,会发生数据的丢失
return 1.89; //返回值是int类型的,返回1.89会发生数据的截取
}
ii)显式的强制类型转换
看得见强转过程
(强转的类型)数据;
int a = 0;
short b = (short)a; //把a显性转换成short类型并且赋值给b
总结强转的注意事项:
- 大的数据类型转小的数据类型是不安全的,会发生数据的丢失
- 小的数据类型转大的数据类型是安全的
【4】变量的取值范围
数据在计算机中存储的都是补码
i)原码、反码和补码
原码:数据的二进制 ----->给人能看懂的
反码:原码转补码的中间形式
补码:机器能读懂的
ii)有符号数和无符号数
signed修饰有符号数,二进制数的最高位表示符号位,0是正数,1是负数 0正1负
unsigned修饰无符号数
负数的原码、反码和补码:
原码:数据的二进制
反码:原码除符号位其余位取反
补码:反码+1
正数和无符号数的原码、反码和补码: 原反补都是数据的二进制
iii)char的取值范围
0000 0000 ----- 1111 1111
无符号数:[0,2^8-1]
有符号正数:0000 0000 ---- 0111 1111 [0,2^7-1]
有符号负数:[-2^7,0]
有符号数的表示范围: [-2^7,2^7-1]
-0的补码:存在没有正负0的分别,-0的补码,并没有被利用到
人为的把-0的补码,用作-128的补码,最高位既是数据位也是符号位。
-128没有原码和反码
-127-1
1000 0000 ----->反:1111 1111 ----> 补:1000 0000
iv)int类型取值范围
1000 0000 0000 0000 0000 0000 0000 0000-------> -2^31
有符号数:[-2^31,2^31-1]
short类型有符号数的表示范围:1000 0000 0000 0000 -----> [-2^15,2^15-1]
【5】计算机中数据的存取问题
计算机中存的都是数据的补码格式
存储数据时,看数据的正负;
取出数据时,看数据的类型。
#include <stdio.h>
int main(int argc, const char *argv[])
{
signed char c = 129;
//存储看正负:
//1000 0001
//取出看类型:
//补码:1000 0001
//反码:1000 0000
//原码:1111 1111
printf("%d\n",c);
c = 255;
//存储看正负:
//1111 1111
//取出看类型:
//补:1111 1111
//反:1111 1110
//原:1000 0001
printf("%d\n",c);
unsigned char p = -1;
//存储看正负:
//原:1000 0001
//反:1111 1110
//补:1111 1111
//取出看类型:unigned
//补:1111 1111
//255
//无符号的输出
printf("%u\n",p);
return 0;
}
作业
求下列数据的原码、反码和补码:
-35 -124 -85 -113
//-35
原码:1010 0011
反码:1101 1100
补码:1101 1101
//-124
原码:1111 1100
反码:1000 0011
补码:1000 0100
//-85
原码:1101 0101
反码:1010 1010
补码:1010 1011
//-113
原码:1111 0001
反码:1000 1110
补码:1000 1111
求下列数据的输出结果:
unsigned char c = -10; %u无符号输出结果
signed char p = 213;%d有符号输出的结果
//unsigned charc = -10; %无符号输出结果
1000 1010
1111 0101
1111 0110 --->246
所以输出结果为246
//signed char p = 213; %d有符号输出结果
1101 0101
1010 1010
1010 1011 --->-43
所以输出结果为-43
第一个C语言程序,分步编译并查看每一步的结果
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
//终端输入
gcc -E hello.c -o hello.i
gcc -S hello.i -o hello.s
gcc -c hello.s -o hello.o
gcc hello.o -o hello
求下列宏定义的输出结果:
#define N 10
#define M N*2+4
#define NUM M*N/M*3+M
NUM = 10*2+4*10/10*2+4*3+10*2+4 = 20+8+12+20+4 = 64