C语言笔记(郝斌)

二次复习做的笔记,很基础,适合新手入门,复习巩固

C语言

前言

1.为什么学习c语言

优点 :代码量小 速度快 功能强大 可移植性较好

缺点:危险性高 开发周期长

应用领域广:系统软件 应用软件

为学习数据结构 C++打基础

2.怎样学C语言

多思考 多上机多上机多上机

目标:能看懂程序,能调试程序

3.学习的目标

熟练掌握C语言的语法规则

理解面向过程的思想

4.学习重点

流程控制 函数 指针 动态内存分配

1.基本编程知识

1.1 CPU 内存条 硬盘 显卡 主板 显示器之间的关系

简单的描述:硬盘上的内容调入内存条,经过CPU的处理,如果是图像则经过显卡显示在显示器上

通过主板组在一起

1.2 helloWorld程序如何运行起来

编译器读取源代码 编译生成目标文件 再经过链接器链接 生成可执行文件 .exe文件是操作系统执行的

1.3 什么是数据类型

基本类型数据:整数:整型 int(4) 短整型 short int(2) 长整型 long int(8 )

浮点数(实数):单精度浮点数:float(4 ) 双精度浮点数:double(8)

字符:char (1)

复合类型数据:结构体 枚举

1.4 什么是变量

变量的本质就是内存中的一段存储空间

1.5 CPU 内存条 VC 操作系统之间的关系

在 VC 编程环境下,操作系统不仅负责为程序分配包括内存空间在内的各种系统资源,还对 CPU 的运算任 务进行调度管理,内存条作为数据暂存介质为 CPU 提供快速的数据读写支持,而 CPU 作为运算核心负责执 行程序指令并进行各种数据处理操作,它们协同工作以确保程序的正常运行。

1.6 变量为什么必须初始化

变量必须初始化是为了给变量赋予一个确定的初始值,避免其存储单元中存在不确定的垃圾值,从而确保程序运行时变量能按预期参与运算和操作,防止因未初始化而导致程序出现逻辑错误和不可预测的结果。

1.7 如何定义变量

数据类型 变量名 = 要赋的值

1.8 什么是进制

几进制就是逢几进一

表示方法:数字后加字母B就是二进制 O是八进制 H就是十六进制

%d是十进制输出 %o是八进制 %x和%X是十六进制

1.9 常量在C语言中是如何表示的

整数:十进制 八进制前面加数字0 十六进制前面加0x或0X

浮点数:传统写法:float x = 3.2; 科学计数法:float x = 3.2e3; //x的值是3200

字符: 单个字符用单引号引起来 字符串用双引号引起来

1.10 常量以什么样的二进制代码存储在计算机中

整数和字符是以补码的形式转换为二进制diamagnetic存储在计算机中

实数是以IEEE754标准转化为二进制代码存储在计算机中的

1.11 什么是字节

字节就是存储数据的单位,并且是硬件所能访问的最小单位

1.12 什么是ASCII

字符本质上与整数的存储方式相同,ASCII码不是一个值,而是一种规定,规定了不同的字符是使用哪个整数值去表示 'A' 65 'a' 97

1.13 基本的输入输出函数的用法

printf("字符串"); printf("输出控制符",输出参数) ; printf("输出控制符1 输出控制符2",输出参数1,输出参数2); printf("输出控制符 非输出控制符",输出参数) ;

scanf() 含有非输入控制符的情况 一次给多个变量赋值的情况··

%d:有符号十进制整数 %u:无符号十进制整数 %o:无符号八进制整数 %x或%X:无符号十六进制整数,%x以小写字母输出,%X以大写字母输出.

%#x中的#是一个标志字符,它会使输出结果带有0x前缀,这样可以更明确地表示输出的是十六进制数,增强了输出结果的可读性和标识性。

1.14 为什么需要输出控制符

01组成的代码可以表示数据也可以表示指令

1.15 存储单位换算

  • 1 字节(Byte)= 8 位(bit)

  • 1 KB = 1024 Byte

  • 1 MB = 1024 KB

  • 1 GB = 1024 MB

  • 1 TB = 1024 GB

  • 1 PB = 1024 TB

2. 运算符

算术运算符 + - * / %(取余)

关系运算符 > >= < <= != ==

逻辑运算符 ! &&(且) ||(或)

赋值运算符 = += -= *= /=

优先级 算术 > 关系 > 逻辑 > 赋值

除 除数和被除数只要有一个是浮点型,则商是浮点型

自增:k = i++; 尽管是给k赋值,可是如果没有其他变化,最后输出的i的值会比原来加一

取余的运算对象必须是整数,结果是整出后的余数,其余数的符号与本除数相同

三目运算符: A ? B : C 如果A对,运行B,否则C

逗号表达式: (A , B , C , D) 功能:从左到右执行 最终表达式的值是最后一项的值

短路特性: &&左边的表达式为假 右边的表达式肯定不会执行

||左边的表达式为真 右边的表达式肯定不会执行

非零为真 零为假

强制类型转化 (数据类型)(表达式) (int)(4.5+5) 答案是9

3. 流程控制

3.1 什么是流程控制

程序代码执行的顺序

3.2 流程控制的分类

顺序

选择 if switch

if的范围问题:if和else默认的只能控制下面紧挨着的 一个 语句

控制多个需要{}起来

循环 for while do...while

float和double都不能保证可以精确的存储一个小数 循环中更新的变量不能定义为浮点数

while: for和while可以相互转换 do...while不行

do{}while(表达式) 主要用于人机交互

switch case case是程序的入口,进入后会一直执行,所以需要break退出

break用于:终止循环 终止switch 不能直接用于if,除非if属于循环内部的一个字句, 这时会退出if所在的那一层循环,即一个break只会终止一层循环(离他最近的那层循环)

continue用于跳过本次循环余下的语句

进制转换 10进制转化为n进制 : 除n取余,直至商0,余数倒序排列

N进制 转化为十进制:按权展开,逐位相加

N进制 转化为X进制:两步走N进制 → 十进制(按权展开)十进制 → X进制(除X取余,倒序排列)

如何看懂一个程序: 1.流程 2.每个语句的功能 3.试数

对一些小算法的程序:1.尝试自己去编程解决它,解决不了就看答案,2.看懂之后尝试自己去修改程序,并且知道修改之后程序的不同输出结果的含义 3.不看答案,自己独立的把答案敲出来

4.数组

数组存在的意义 数组可以解决大量同类型数据的存储问题 模拟现实世界

数组从a[0]开始而不是a[1]

举例:int a[5] = {1,2,3,4,5};第一个元素存在a[0]而不是a[1]中

一维数组

为n个变量连续分配存储空间

所有的变量数据类型必须相同

所有变量所占的字节大小必须相等

未被初始化的元素默认都为0

只有在定义数组的同时才可以整体赋值,其他情况下整体赋值都是错误的

a[x] 单独拿出来a代表的是数组a第一个元素的地址

二维数组

int a[3] [4]

总共是12个元素,可以当作3行4列看待,a[0] [0]开始 a[2] [3]结束

int a[m] [n] 右下角位置的元素是a[m-1] [n-1]

 //定义的格式1
 int a[3][4] = 
 {
     {1,2,3,4},
     {5,6,7,8},
     {9,10,11,12},
 }
 //格式2
 int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
 //输出数组内容
 int i, j;
 for (i = 0; i < 3;i++)
 {
     for (j = 0;j < 4;j++)
         printf("%d",a[i][j]);
 }

多维数组

不存在,因为内存是线性一维的 n维数组可以当作每个元素是n-1维数组的一维数组

5.函数

逻辑上:能够完成特定功能的独立的代码块

物理上:能够接受数据(也能不接受数据),能对接受的数据进行处理,能够将数据处理的结果返回(也可能不返回任何值)

函数是个工具,他是为了解决重复问题而被设计出来的,他可以被当作一个黑匣子

 int f(void)  //括号中的void表示该函数不能接受任何数据,int表示返回值是int类型的数据
 ​
 void g(void)  //函数名前面的void表示该函数没有返回值

函数的意义

避免了重复性操作

有利于程序的模块化

如何定义函数

 函数的返回值类型 函数的名字(函数的形参列表)
 {
     函数的执行体
 }

return和break的区别

break是终止循环,return是终止被调函数,向主函数返回表达式的值

函数的分类

有参函数和无参函数

有返回值函数和无返回值函数

库函数和用户自定义函数

值传递函数和地址传递函数

普通函数和主函数(主函数可以调用普通函数,普通函数不能调用主函数)

函数的声明

int a(); 写在主函数前面 函数的声明是一个语句,所以;不能丢

函数前置声明的作用:告诉编译器即将出现的若干字母代表的是一个函数;告诉编译器即将出现的若干字母所代表 的函数的形参和返回值的具体情况

对库函数的声明是通过

函数调用和函数声明的顺序:先声明或定义再调用 # include<库函数所在的文件的名字.h>来实现的

函数声明时可以不写形参

实参和形参

实参和形参个数必须相同,位置一一对应,类型应该兼容(最好相同)

如何在软件开发中合理的设计函数来解决实际问题

一个函数的功能尽量独立,单一

多学习,多模仿牛人的代码

常用的系统函数

double sqrt (double x) 求x的平方根

int abs (int x) 求x的绝对值(整数)

double fabs (double x) 求x的绝对值(实数)

变量的作用域和存储方式

按作用域: 全局变量 局部变量

在函数内部,若存在同名的全局变量和局部变量,局部变量会屏蔽全局变量

按存储方式:静态变量 自动变量 寄存器变量

6.指针

指针就是地址

 int i;
    //p是变量的名, int * 表示p变量存放的是int类型变量的地址
 p = &i;   //正确
 p = i;    //错误,类型不一致,p是int *型 i是int型
 p = 55;   //错误,原因同上

int * 类型就是存放int变量地址的类型

p保存了i的地址,因此p指向i

p不是i,i也不是p,或者说,修改两者中的一个不影响另一个

如果一个指针变量指向了某个普通变量,则 *指针变量 就完全等同于 普通变量 也就是说: 在所有出现*p的变量都可以替换成i; 在所有出现i的变量都可以替换成*p

在C语言中,char *p = "helloworld"; 是合法的,且无需额外分配地址。

指针的作用

表示一些复杂的数据结构

快速的传递数据

使函数返回一个以上的值

能直接访问硬件

能够方便的处理字符串

是理解面向对象语言中引用的基础

指针的定义

int *p; p是指针变量,p中存储的值是指针

地址 内存单元的编号 从零开始的非负整数

指针的本质就是一个操作受限的非负整数

指针的分类

指针和数组

一维数组:

一维数组名 是个指针常量 它存放的是一维数组第一个元素的地址

下标和指针的关系

如果p是个指针变量,则 p[i]等价于*(p + 1)

确定一个一维数组需要几个参数 类型 数组开始地址 数组元素个数

p[i] = *(pArr + 1) = pArr[i]

*的含义

乘法

定义指针变量

指针运算符 该运算符放在已经定义好的指针变量的前面,如果p是一个已经定义好的指针变量 则*p表示 以p的内容为地址的变量

如何通过被调函数修改主调函数普通变量的值

1.实参必须是该普通变量的地址

2.形参必须是指针变量

3.在被调函数中通过 *形参名 = 。。。的方式可以修改主调函数相关变量的值

指针变量的运算

指针变量不能相加 相乘 相除

如果两个指针变量指向的是同一块空间中的不同存储单元,这两个指针变量才能相减

一个指针变量占四个字节(32位系统) 64位系统是八个字节

传统数组的缺点:

1.数组的长度必须事先指定,且只能是常整数,不能是变量

2.传统形式定义的数组,该数组的内存程序员无法手动释放(本函数运行完毕时,系统自动释放)

3.数组的长度不能在函数运行的过程中动态的扩充或缩小

4.在A函数定义的数组,A函数运行完毕后,该数组将无法被其他函数使用

上面四个传统数组的缺点,动态数组都能解决

构造动态函数

 int i = 5; //分配了四个字节(int型),静态分配
 int * p = (int *)malloc(4);
 * p = 5;
 free(p);

要使用malloc函数,需要添加malloc.h这个头文件

malloc函数只有一个形参,并且形参是整型

4表示请求系统为本程序分配四个字节

malloc函数只能返回第一个字节的地址

第二行一共分配了8个字节,p占4个字节,p所指向的内存也占4个字节

*p代表的就是一个int变量,只不过*p这个整型变量的内存分配方式和i变量的不一样

p本身所占的内存是静态分配的,p所指向的内存是动态分配的

free(p)表示把p所指向的内存给释放掉(p本身的内存是静态的,不会被释放)

动态构造一维数组

 int a[5]; //如果int占四个字节的话,则本数组总共包含20个字节,每四个字节被当做了一个int变量来使用
 int len;
 int *pArr;
 pArr = (int *)malloc(4 * len);//动态构造,该数组的数组名是pArr,元素类型是int类型                                             类似于 int pArr[len];

多级指针

 int i = 5;
 int *p = &i;
 int **q = &p;
 int ***r = &q;//r是int ***类型,所以只能存放int **类型变量的地址

7. 结构体

为什么需要结构体

  1. 整合数据:把不同类型的相关数据组合成整体。

  2. 方便管理:让数据组织有序,提升代码可读性与维护性。

  3. 高效传参:作为一个单元在函数间传递数据。

  4. 自定义类型:按需求创建新数据类型,增强灵活性。

定义方式

 //第一种定义方式
 struct Student
 {
     int age;
     float score;
     char sex;
 }
 //第二种定义方式
 struct Student
 {
    int age;
     float score;
     char sex;
 } st2

赋值和初始化

 //第一种初始化 定义的同时赋值
 struct Student st = {80, 66.6, 'F'}; 
 //第二种初始化 先定义再分别赋值
 struct Student st2;
 st2.age = 10;
 st2.score = 88;
 st2.sex = 'F';

如何取出结构体变量中的每一个成员

1.结构体变量名.成员名

2.指针变量名->成员名(常用)

 struct Student *pst = &st;   //&st不能改成st
 //第一种方式
 st.age = 10;
 //第二种方式
 pst->age = 88;  //pst->在计算机内部会被转化成(*pst).age 等价于st.age

结构体变量和结构体指针变量作为函数参数传递的问题

推荐使用结构体指针变量作为函数参数来传递

结构体变量的运算

不可以相互加减乘除,但可以相互赋值 st1 = st2;

枚举

 enum weekday {};

把一个事物所有可能的取值一一列举出来

使代码更安全 书写麻烦

进制转换

补码

原码

也叫符号-绝对值码 最高位0表示正,1表示负,其余二进制位是该数组的绝对值的二进制位

简单易懂但加减运算复杂 0的表示不唯一 存在加减乘除四种运算,增加了CPU的复杂度

反码

运算不便,也没有在计算机中应用

移码

表示数值平移n位,n称为移码量,主要用于浮点数的阶码的存储

补码

正数与原码相同

负整数转二进制

先求与该负数相对应的正整数的二进制代码,然后将所有位取反,末尾加1,不够位数时,左边补1

已知二进制求十进制

如果首位时0,是正整数,普通方法求

如果首位是1,是负整数,将所有位取反,末尾加1,所得数字就是该负数的绝对值

如果全是0,则对应的十进制数字就是0

最小负数的二进制代码是多少

最大整数的二进制代码是多少

字符串的处理

链表

首节点:存放第一个有效数据的节点

尾节点:存放最后一个有效数据的节点

头节点:头节点的数据类型和首节点的类型是一模一样的

头节点是首节点前面的那个节点

头节点并不存放有效数据

设置头节点是为了方便对链表的操作

头指针:存放头节点地址的指针变量

算法

算法是依附与存储结构的,不同的存储结构,所执行的算法是不一样的

通俗定义:解题的方法和步骤

狭义定义:对存储数据的操作

对不同的存储结构,要完成某一个功能所执行的操作时不一样的

广义定义:广义的算法也叫泛型,无论数据是如何存储的,对该数据的操作都是一样的

优点:插入删除效率高 不需要一个连续空间

缺点:需要一个连续的很大的空间

位运算符

& 按位与 比如1010 和 0110 经过运算就是0010

| 按位或

~按位取反

<< 按位左移 i<<1;表示把i的所有二进制左移一位 左移n位相当于乘以2的n次方

>>按位右移

二进制全部为0的含义

数值0 字符串结束标记符 空指针NULL

NULL本质也是0,但这个零不代表数字0,而表示的是内存单元的编号零

计算机规定了以0为编号的存储单元的内容不可读,不可写

动态内存分配

文件

在C语言中用一个指针变量指向一个文件,这个指针称为文件指针,通过文件指针就可对他所值得文件进行各种操作.

 #include 
 #include 
 ​
 int main() {
     // 1. 文件指针
     // 用于指向文件,后续的文件操作都通过这个指针来进行
     FILE *fp;
 ​
     // 2. 打开文件
     // fopen函数用于打开文件,第一个参数是文件名,第二个参数是打开模式
     // "w" 表示以写入模式打开文件,如果文件不存在则创建,如果存在则清空内容
     fp = fopen("test.txt", "w");
 ​
     // 检查文件是否成功打开
     if (fp == NULL) {
         // 如果文件打开失败,输出错误信息并退出程序
         perror("无法打开文件");
         return 1;
     }
 ​
     // 3. 写入文件
     // fprintf函数用于向文件中写入格式化数据,用法和printf类似
     fprintf(fp, "这是写入文件的第一行内容。\n");
     // fputs函数用于向文件中写入字符串
     fputs("这是写入文件的第二行内容。\n", fp);
 ​
     // 4. 关闭文件
     // fclose函数用于关闭文件,释放相关资源
     fclose(fp);
 ​
     // 5. 以读取模式打开文件
     // "r" 表示以只读模式打开文件,如果文件不存在则打开失败
     fp = fopen("test.txt", "r");
     if (fp == NULL) {
         perror("无法打开文件");
         return 1;
     }
 ​
     // 6. 读取文件
     // 6.1 使用fgets函数逐行读取文件内容
     char line[100];
     while (fgets(line, sizeof(line), fp) != NULL) {
         // 输出读取到的行
         printf("%s", line);
     }
 ​
     // 重新定位文件指针到文件开头
     // 以便后续再次读取文件内容
     rewind(fp);
 ​
     // 6.2 使用fscanf函数按格式读取文件内容
     char buffer[100];
     while (fscanf(fp, "%s", buffer) != EOF) {
         // 输出读取到的单词
         printf("读取到的单词: %s\n", buffer);
     }
 ​
     // 7. 关闭文件
     fclose(fp);
 ​
     // 8. 以追加模式打开文件
     // "a" 表示以追加模式打开文件,如果文件不存在则创建,如果存在则在文件末尾追加内容
     fp = fopen("test.txt", "a");
     if (fp == NULL) {
         perror("无法打开文件");
         return 1;
     }
 ​
     // 9. 追加内容到文件
     fputs("这是追加到文件的内容。\n", fp);
 ​
     // 10. 关闭文件
     fclose(fp);
 ​
     return 0;
 }

auto 变量类型推演,

register 建议编译器将该变量放入CPU,

static 静态变量,

extern 声明变量,常用于多文件需要使用同一变量时

不能被重载的运算符 1、. (成员访问运算符) 2、.* (成员指针访问运算符) 3、:: (域运算符) 4、sizeof(长度运算符) 5、?: (条件运算符)

typedef

 // 为 int 类型定义别名 Age
 typedef int Age;
 // 为 char 类型定义别名 Character.2
 typedef char Character;
 // 为包含 10 个整数的数组定义别名 IntArray
 typedef int IntArray[10];
 // 为指向 int 类型的指针定义别名 IntPointer
 typedef int* IntPointer;
 // 为结构体类型定义别名 Person
 typedef struct {
     char name[50];
     int age;
 } Person;
 ​
 int main() {
     Person p = {"John", 25};
     printf("Name: %s, Age: %d\n", p.name, p.age);
     return 0;
 }
 ​
 ​
 ​

ASCALL代码

48 0 65 A 97 a

你可能感兴趣的:(c语言,单片机,stm32,开发语言,笔记,linux,改行学it)