C语言
课程讲解思路
- 基本概念
- 数据类型,运算符和表达式
- 输入输出专题
- 流程控制
- 数组
- 指针
- 函数——cplusplus.com
- 构造类型
- 动态内存管理
- 调试工具和调试技巧(gdb ,make)
- 常用库函数
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
- 格式化输入输出函数:scanf,printf
- 字符输入输出函数:getchar,putchar
- 字符串输入输出函数: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 型 |
数组
-
声明
数据类型 数据名 [数据长度];
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};
-
元素的访问与修改
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;
-
结构体——描述一个变量多方面的属性
如果说数组是一组相同类型的数据的集合,那么结构体是一组不同类型的数据的集合。
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;
初始化结构体
-
在定义时指定初始值
// 定义了一个有三个成员的名为People的结构体,没有声明变量 struct People { int age; char name[100]; double height; }; //此处声明了一个People的结构体变量zhh_baiyu 并赋了初始值 struct People zhh_baiyu = {15,"zhh_baiyu",1.90};
-
也可以在声明变量之后赋初始值
//此处声明了一个People的结构体变量 baiyu struct People baiyu; //对baiyu的结构体成员进行赋值 baiyu.age = 18; baiyu.name = "baiyu"; baiyu.height = 1.85;
-
引用
#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;可以防止即使出现程序错误也能正常流程走
分支
- 单分支结构
if(表达式)
语句;
或者
if(表达式)
{
语句1;
语句2;
}
- 双分支结构——
if(表达式1)
语句1;
else(表达式2)
语句2;
-
多分支——按顺序判断表达式是否满足条件
else符合最近原则,对应最近的if语句
if(表达式1)
语句1;
else if(表达式2)
语句2;
else if(表达式3)
语句3;
else(表达式4)
语句4;
- 逗号表达式——优先级最低的表达式
当没有括号时,第一个表达式为整个表达式的值。
而有括号时,依次执行后,最后一个表达式为整个表达式的值。
而且所有的表达式都依次执行了。
//例一
#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);
}
- 判断等级
#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");
}
- 判断是否是闰年
判断条件
- 公历年份能被4整除但不能被100整除的,为普通闰年(如2004年、2008年、2012年、2016年、2020年);
- 公历年份能被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;
}
- 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
- for格式——初始化条件,变化条件,循环的终止条件都写在括号里面
for:(最少循环0次)
for(初始化表达式;判断表达式;更新表达式)
{
循环体语句;
}
- while:(最少执行次数为0)
while(exp)
loop;
- do-while:(最少执行次数为1次)
do
{
loop;
}while(exp);
- if-goto:(慎用:goto实现的是无条件的跳转,且不能跨函数跳转)
- 出现死循环:
while(1);
for( ; ;);
杀掉死循环:ctrl + c
- 辅助控制: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