课程讲解思路

  1. 基本概念
  2. 数据类型,运算符和表达式
  3. 输入输出专题
  4. 流程控制
  5. 数组
  6. 指针
  7. 函数——cplusplus.com
  8. 构造类型
  9. 动态内存管理
  10. 调试工具和调试技巧(gdb ,make)
  11. 常用库函数

1.基本概念

c源文件 — 预处理 — 编译 — 汇编 — 链接 — 可执行文件

2.基本数据类型、运算符、表达式

基本数据类型
数据类型 名称 占用内存 值范围 输出符号
char 字符型 1字节 -128 到127 或 0 到 255 %c
short (short int的缩写) 短整型 2 byte -32,768 到32,757 %d
int 整型 4 byte -2,147,483,648 到 2,147,483,647 %d
long 长整型 4 byte -2,147,483,648 到 2,147,483,647 %d
float 单精度浮点型 4 byte 1.2E-38 到 3.4E+38 %f
double 双精度浮点型 8 byte -1.7E308 到 1.7E+308 %f
unsigned int 无符号整型 4 byte 0~ 4,294,967,295 %d
unsigned short 无符号短整型 2 byte 0~ 65535 %d
unsigned long 无符号长整型 4 byte 0~4,294,967,295 %d

注:一个字节(byte)=8位(bit),值范围就是2的几次方,比如char型是一个字节,也就是8位,所以char型变量的取值最大就是2^8=255

运算符
  • 每个运算符所需要的参与运算的操作个数
  • 结合性
  • 优先级
  • 运算符的特殊用法
  • 位运算的重要意义

算术运算符
运算符种类 符号 作用
算术运算符 + 加(正) ;- 减(负);* 乘;/ 除 正常运算 3+5 +3正3 -5负5
% 模运算符 / 求余运算符 求余,%两侧均为整型数据 5 / 2 =2; 5 % 2=1; 5.0 /2=2.5(隐式转化类型)
++ 自增运算符 ++操作数 —> 先计算,再增值;操作值++ —> 先计算再增值 运算符在前,先进行计算;变量在前,先取变量值使用,再计算
– 自减运算符 与上面一样减值 3–;常量不能进行自增自减的计算

自增自减例如

#include <stdio.h> 
int main()
{
	int i=1,j=10,value;
	/* 
	value = i++ + ++j;
	i;
	j=j+1;
	value = i+j;    1+11 
	i=i+1;
	value输出等于12 
	*/
	
	value = --i + j++;
	//value 输出10 
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("value = %d\n",value);
	return 0;
}
关系运算符

有 < <= == > >= !=

"=“是赋值;”=="是判断是否相等

!= 返回的是逻辑真 1 与 假 0

逻辑运算符
符号 含义 作用
非零 0 即为真 1
&& 全真才真
|| 全假才假

逻辑运算符(&& ,||)的短路特性

(m = a>b) && (n = c>b);这个语句中,逻辑与判断了 m 得出 假 0 从而不影响整个语句判断结果,n不需要计算

#include <stdio.h>
int main()
{
	int i = 0,j = 10,value;
	int a = 1,b = 2,c = 3,d = 4;
	int m = 1,n = 1;
	
	(m = a>b) && (n = c>b);
	printf("m = %d\n n = %d\n",m,n);
 } 
//输出 m = 0,n = 1
赋值计算扩展
	int a = 6;9;81;0
	
	a -= a *= a += 3;
//计算过程	a=a+3=9 -> a=a*9 =81 -> a=a-81=0 
条件运算符 / 三目运算

op1 ? op2 :op3

如果op1值为真则取op2的值,否则取op3的值

相当于

a > b ? a :b
if(a > b)
	return a;
else
	return b;

逗号运算符

通常运用在条件语句中

for(i = 0,j =0;…;)

i初始化一个值,同一时刻j需要附一个什么样的值(并列运行指定)

求字节数:sizeof()
int a = 6;
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(double));
强制类型转换

转换的是中间过程

*位运算

<< >> ~ | ^ &

例如

int i = B 1100 = 12;    //以二进制表示(c语言不能显示二进制)
int j = B 1001 =9;       //假设i和j都是四位的二进制

异或:相同为0,不同为1
	i ^ j
 1 1 0 0
^1 0 0 1
----------
 0 1 0 1
按位或:(双目运算符)表示同一位上全假才假
    i | j
  1 1 0 0
| 1 0 0 1
-------------
  1 1 0 1
按位与:(双目运算符)表示同一位上全真才真
    i & j
  1 1 0 0
& 1 0 0 1
------------
  1 0 0 0 
    
取反:0变为1,1变为0
~i -> 0011

右移,左移
i >> 1  --> 0110  = 6    //i向右移一位
i << 1  --> 1100  = 12   //i又向左移一位

位运算的重要意义

将操作数中第n位置 1 ,其他位不变:num = num | 1 << n;    1左移n位
例如: i = i | 1 << 2;
   
将操作数中第n位清 0  ,其他位不变:num = num & ~(1 << n);

测试第n位:if (num & 1 << n)  //如果第n位为 1 的话这个表达式为真, 0 的话表达式为假
    
从一个指定的宽度的数中取出其中的某几位:

输入、输出 input & output —> I / O

  1. 格式化输入输出函数:scanf,printf
  2. 字符输入输出函数:getchar,putchar
  3. 字符串输入输出函数:gets(!),puts
输出格式字符 含义 例子 输出内容
d,i 十进制整数 int a=567;printf(“%d”,a); 567
x,X 十六进制无符号整数 int a=255;printf(“%x”,a); ff
o 八进制无符号整数 int a=65;printf(“%o”,a); 101
u 不带符号十进制整数 int a=567;printf(“%u”,a); 567
c 单一字符 char a=65;printf(“%c”,a); A
s 字符串 printf(“%s”,“ABC”); ABC
e,E 指数形式浮点小数 float a=567.789;printf(“%e”,a); 5.677890e+02
f 小数形式浮点小数 float a=567.789;printf(“%f”,a); 567.789000
g e 和 f 中较短一种 float a=567.789;printf(“%g”,a); 567.789
%% 百分号本身 printf(“%%”); %
修饰符 功能 例子 输出内容
数字 输出数据域宽,数据长度<m,左补空格,否则按实际输出
.数字 对实数,指定小数点后位数(四舍五入) float f =123.456
对字符串,指定实际输出位数
- 输出数据在域内左对齐(缺省右对齐)
+ 指定在有符号数的正数前显示正号(+)
0 输出数值时指定左而不使用的空位置自动填0
# 在八进制和十六进制数前显示导 0,0x
l 在d,o,x,u前,指定输出精度为long 型
在e,f,g 前,指定输出精度为double 型

数组

  1. 声明

    数据类型 数据名 [数据长度];

    int arr_i_baiyu[10];//声明了一个数组长度为 10 的 int 型数组 arr_i_baiyu
    char arr_c_baiyu[5]; //声明了一个数组长度为 5 的 char 型数组 arr_c_baiyu
    float arr_f_baiyu[7]; //声明了一个数组长度为 7 的 float 型数组 arr_f_baiyu
    double arr_d_baiyu[9]; //声明了一个数组长度为 9 的 double 型数组 arr_d_baiyu

    初始化

    float arr_f_baiyu[7] = {1.1, 2.1, 3.3, 45.4, 5.6, 6.6, 7.8};
    或者
    float arr_f_baiyu[] = {1.1, 2.1, 3.3, 45.4, 5.6, 6.6, 7.8};
  2. 元素的访问与修改

    double arr_d_baiyu[] = {5.0, 4.0, 3.0, 2.0, 1.0};//这里是初始化了一个长度为5的double型数组
索引值 0 1 2 3 4
arr_d_baiyu 5.0 4.0 3.0 2.0 1.0

访问和修改数组中的元素

引用对应数组的下标

//for循环遍历数组元素
int i;
for (i=0; i<5; i++)
{
    printf("arr_d_baiyu[%d]的值:%f\n",i, arr_d_baiyu[i]);
}

//这里是把4.0赋值给 d_baiyu
double d_baiyu = arr_d_baiyu[1];

//修改数组中元素数值
arr_d_baiyu[3] = 6.66;
  1. 结构体——描述一个变量多方面的属性

    如果说数组是一组相同类型的数据的集合,那么结构体是一组不同类型的数据的集合。

struct 结构体名称 {
	int 变量1;
	float 变量2;
	double 变量3;
	... ... ;//标准的变量定义
}结构变量;(定义在结构的末尾,最后一个分号之前,可以指定一个或多个结构变量)
定义结构体
定义的两种方式:
//①定义了一个有三个成员的名为People的结构体,没有声明变量
struct People {
    int age;
    char name[100];
    double height;
};
struct People baiyu;//此处声明了一个People的结构体变量baiyu
 
/*②定义了一个有三个成员的名为PeopleInfo 的结构体,同时声明了结构体变量zhh*/
struct PeopleInfo {
    int age;
    char name[100];
    double height;
}zhh;
初始化结构体
  1. 在定义时指定初始值

    // 定义了一个有三个成员的名为People的结构体,没有声明变量
    struct People {
        int age;
        char name[100];
        double height;
    };
     
    //此处声明了一个People的结构体变量zhh_baiyu 并赋了初始值
    struct People zhh_baiyu = {15,"zhh_baiyu",1.90};
  2. 也可以在声明变量之后赋初始值

    //此处声明了一个People的结构体变量 baiyu
    struct People baiyu;
    //对baiyu的结构体成员进行赋值
    baiyu.age = 18;
    baiyu.name = "baiyu";
    baiyu.height = 1.85;
  3. 引用

    #include<stdio.h>
    int main() {
        // 定义了一个有三个成员的名为People的结构体,没有声明变量
        struct People {
                int age;
                char name[100];
                double height;
        };
        //此处声明了一个People的结构体变量 baiyu
        struct People baiyu;
        //对baiyu的结构体成员进行赋值
        baiyu.age = 18;
        strcpy(baiyu.name , "baiyu");
        baiyu.height = 1.85;
     
        //输出结构体变量 baiyu 的信息 
        printf("%s的age为:%d, height为:%f\n",baiyu.name,baiyu.age,baiyu.height) ;
     
        //此处声明了一个People的结构体变量zhh_baiyu 并赋了初始值
        struct People zhh_baiyu = {15,"zhh_baiyu",1.90};
        //输出结构体变量 zhh_baiyu 的信息 
        printf("%s的age为:%d, height为:%f\n",zhh_baiyu.name,zhh_baiyu.age,zhh_baiyu.height) ;
        /*
        定义了一个有三个成员的名为 PeopleInfo 的结构体,
        同时声明了结构体变量zhh 并且为zhh这个变量赋值初始化
        */
        struct PeopleInfo {
                int age;
                char name[100];
                double height;
        } zhh = {17,"zhh",1.89};
        
        //输出结构体变量 zhh 的信息 
        printf("%s的age为:%d, height为:%f\n",zhh.name,zhh.age,zhh.height) ;
     
    }

指针

变量

变量的生存周期和作用域

变量类型 作用域 生存周期
局部变量 只作用于该函数内部 auto:自动变量,离开定义函数立即消失
register:寄存器变量,离开定义函数立即消失
static:静态变量,离开定义函数仍然存在
全局变量 static:静态变量,仅限本文件内部使用 程序运行期间一直存在
extern:外部存储变量,用于声明本文件将要用到的其他文件的变量 程序运行期间一直存在

分支和循环

关键字

选择:if-else switch-case

循环:while do-while for if-goto

辅助控制:continue break

写if-else即使只有两种情况,也需要if(1) 结果;else if(2)结果;else error;可以防止即使出现程序错误也能正常流程走

分支

  1. 单分支结构
if(表达式)

语句;

或者

if(表达式)

{

​	语句1;

​	语句2;

}
  1. 双分支结构——
if(表达式1)

语句1;

else(表达式2)

语句2;
  1. 多分支——按顺序判断表达式是否满足条件

    else符合最近原则,对应最近的if语句

if(表达式1)

语句1;

else if(表达式2)

语句2;

else if(表达式3)

语句3;

else(表达式4)

语句4;
  1. 逗号表达式——优先级最低的表达式

当没有括号时,第一个表达式为整个表达式的值。

而有括号时,依次执行后,最后一个表达式为整个表达式的值。

而且所有的表达式都依次执行了。

//例一
#include<stdio.h>
void main(){
	int x, y, z;
	o = x = 3, y = 4, z = 5;
	printf("x=%d,y=%d,z=%d,o=%d\n",x,y,z,o);
}
//例二
#include<stdio.h>
void main(){
	int x, y, z, o;
	o = ( x = 3, y = 4, z = 5 );
	printf("x=%d,y=%d,z=%d,o=%d\n",x,y,z,o);
}
  1. 判断等级
#include<stdio.h>
void main(){
    int score;
    scanf("%d",&scanf);
    if(score < 0 || score > 100)
    {
        printf("Input error!\n");
    }
/*  并列判断
    if(score <= 100 && score >90)
        printf("A");
    if(score <= 90 && score >80)
        printf("B");
    if(score <= 80 && score >70)
        printf("C");
    if(score <= 70 && score >60)
        printf("D");
    if(score <= 60 && score >00)
        printf("E");
*/
    //嵌套判断
    if(score > 90)
         printf("A");
    else if (score > 80)
        printf("B");
    else if (score > 70)
        printf("C");
    else if (score > 60)
        printf("D");
    else
        printf("E");
    
}
  1. 判断是否是闰年

判断条件

  1. 公历年份能被4整除但不能被100整除的,为普通闰年(如2004年、2008年、2012年、2016年、2020年);
  2. 公历年份能被400整除的,为世纪闰年(如2000年、2400年)。
#include<stdio.h>
int main()
{
    int year;
    printf("请输入年份:\n");
    scanf("%d",&year);
    
    //   if((year%4==0) && (year%100!=0) ||(year%400==0))
    
    if(year%4==0 && year%100!=0)
    {
    	printf("%d是闰年\n",year);
	}
    else if(year%400==0)
	 	printf("%d是世纪闰年\n",year);
	else
     	printf("%d是平年\n",year);
	
	
    return 0;
}
  1. switch-case 判断等级

格式:

switch(表达式)

{

​ case 常量表达式:

​ break;

​ case 常量表达式:

​ break;

​ …

​ default:

}

#include<stdio.h>
int main(){
    int score,num;
    scanf("%d",&score);
    if(score < 0 || score > 100)
    {
        printf("Input error!\n");
        return 0;//结束程序 
    }

    switch (score/10)//整数/10取整 再判断分数段 
    {
    	case 10://只能是常量或常量表达式,不能是<>= || &&
    	case 9:
    		printf("A");
    		break; 
    	case 8:
    		printf("B");
    		break; 
    	case 7:
    		printf("C");
    		break; 
    	case 6:
    		printf("D");
    		break; 
    	case 5:
		case 4:
		case 3:
		case 2:
		case 1:
		case 0:
    		printf("E");
    		break;
    	default: 
    	break;
	}
    //break:跳出当前的switch
	return 0;
}

"=“是赋值;”=="是判断是否相等

int main()
{
    int num = 4;
    if(num = 5)
    {
        printf("哈哈\n");
    }
    return 0;
}
//可以printf出哈哈
int main()
{
    int num = 4;
    if(5==num)
    {
        printf("哈哈\n");
    }
    return 0;
}
//可以判断出,打印空白

循环

while do-while for if-goto

  1. for格式——初始化条件,变化条件,循环的终止条件都写在括号里面

for:(最少循环0次)

for(初始化表达式;判断表达式;更新表达式)
{
	循环体语句;
}
  1. while:(最少执行次数为0)

while(exp)

​ loop;

  1. do-while:(最少执行次数为1次)

do

{

​ loop;

}while(exp);

  1. if-goto:(慎用:goto实现的是无条件的跳转,且不能跨函数跳转)
  2. 出现死循环:

while(1);

for( ; ;);

杀掉死循环:ctrl + c

  1. 辅助控制:break 和 continue 的区别
while(exp)
{
    ...
    break;//break结束while循环,跳至下面...
}
...
    
    
while(exp)
{
    ...
    continue;//至continue时,退出至while条件,如果满足exp再进行while,不满足则跳至下面...
}
...

函数

常用库函数

IO函数
字符串操作函数
字符操作函数
#include <String.h>

String DecIntToHexStr(long long num) //十进制整数转十六进制字符串
{
  String hexStr = "";
  char hexDigits[] = "0123456789ABCDEF";

  if (num == 0) {
    return "0";
  }

  while (num > 0) {
    int rem = num % 16;
    hexStr = hexDigits[rem] + hexStr;
    num /= 16;
  }

  return hexStr;
}

//示例:
#include <Arduino.h>

// 将十进制整数转换为十六进制字符串
String DecIntToHexStr(long long num) {
  String hexStr = "";
  char hexDigits[] = "0123456789ABCDEF";

  if (num == 0) {
    return "0";
  }

  while (num > 0) {
    int rem = num % 16;
    hexStr = hexDigits[rem] + hexStr;
    num /= 16;
  }

  return hexStr;
}

void setup() {
  Serial.begin(9600);
  long long decimal = 255;
  
  String hexString = DecIntToHexStr(decimal);
  
  Serial.print("Decimal: ");
  Serial.println(decimal);
  Serial.print("Hexadecimal: ");
  Serial.println(hexString);
}
输出结果:
    
Decimal: 255
Hexadecimal: FF
内存操作函数
时间/日期函数
数学函数
其他函数