C语言学习记录

简介

跟着郝斌老师C语言自学教程,做的视频记录。

c语言的特点:

优点:代码量小,速度快,功能强大

缺点:危险性高,开发周期长,可移植性不强(很多机器不能跑)

c语言应用领域

系统软件开发

  • 操作系统
  • 驱动程序:主板程序、显卡驱动、摄像头驱动
  • 数据库: DB2、Oracle、Sql server

应用软件开发

  • 办公软件:WPS
  • 图形图像多媒体:
  • 嵌入式软件开发:智能手机、掌上电脑
  • 游戏开发:2D、3D

参考资料

1

C语言关键字

autobreakcaseconst
continuedefaultdodouble
elseenumexternfloat
forgotoifint
longregisterreturnshort
signedsizeofstaticstruct
switchtypedefunsignedunion
voidvolatilewhilechar

一个简单程序:

解一元二次方程

#include <stdio.h>
#include <math.h>
int main() {
	int a, b, c;
	float delta;
	float x1, x2;
	
	scanf_s("%d %d %d", &a, &b, &c);

	delta = b * b - 4 * a * c;

	if (delta > 0) {
		x1 = (-b + sqrt(delta)) / (a * 2);
		x2 = (-b - sqrt(delta)) / (a * 2);
		printf("两个解:%f %f\n", x1,x2);
	}
	else if (delta == 0) {
		x1 = (-b) / (2 * a);
		x2 = x1;
		printf("一个解:%f \n", x1);
	}
	else {
		printf("无解\n");
	}

	return 0;
}

基本编程知识

1、cpu、内存条、硬盘、显卡、主板显示器 之间的关系

操作系统通过一些点击事件,先把硬盘中的东西,调用到内存条里面,cpu再去处理内存条里面的数据,处理的数据通过显卡在显示器中输出。

2、hello world 程序如何运行起来的

3、什么是数据类型

基本数据类型

  • 整数
    • 整型 – int (4个字节)
    • 短整型 – short int (2个字节)
    • 长整型 – long int (8个字节)
  • 浮点数(实数)
    • 单精度浮点数 – float (4个字节)

    • 双精度浮点数 – double (8个字节)

    • float和double在计算机中,不能保证可以精确的存储一个小数

  • 字符 – char (1个字节)

复合数据类型

  • 结构体
  • 枚举
  • 共用体(用处不大,用于单片机,节省内存)

4、什么是变量

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

5、cpu、内存条、vc++6.0、操作系统 之间的关系

6、变量为什么必须得初始化

初始化就是赋值的意思,当变量未被初始化时,变量所占用的内存空间,会输出该内存空间原存放的数据(注:操作系统在回收内存空间时,并不会清空该内存空间中遗留下来的数据),会造成用户没发现该变量已经出现未知错误。

现在C语言在vs运行中,未初始化变量会直接报错。

7、如何定义变量

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

等价于:

​ 数据类型 变量名;

​ 变量名 = 要赋的值;

例:

int i = 3;
float j;
j = 4.1;

8、 什么是进制

进制就是逢几进一,是一种计数的形式。

N进制就是逢N进一

汇编中:

​ 数字后加 B 表示二进制数,(1101B)

​ 加 O 表示八进制数,(573O)

​ 加 D 表示十进制数,(12D)

​ 加 H 表示十六进制数。(ab1eH)

int i=20;
printf("%d", i);// 十进制输出
printf("%x %X\n", i, i);// 十六进制输出
printf("%o \n", i); // 八进制输出

9、常量在c语言中是如何表示的

  • 整数:
    • 十进制:传统写法
    • 十六进制:数字前面加 0x0X
    • 八进制:数字前面加 0
  • 浮点数
    • 传统写法:float x = 3.2;
    • 科学计数法:
      • float x = 3.2e3; // x值为3200
      • float x = 123.45e-2; // x值为1.2345
  • 字符
    • 单个字符用单引号:‘a’
    • 字符串用双引号: “a” 代表 ‘a’和’\0’的组合

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

整数以补码的形式转化为二进制代码存储在计算机中的,

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

字符的本质与整数的存储方式相同。

11、程序代码规范化

定义变量=》变量初始化=》输出变量

使他人更清楚的看懂程序,使代码的可读性更强。

12、什么是字节

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

一个字节=8位

1K = 1024个字节

1M= 1024K

1G= 1024M

13、不同类型数据之间相互赋值的问题

与补码有关

14、什么是ASCII

是一种规定,规定了不同的字符是使用哪个整数值去表示

15、字符的存储,字符本质上与整数的存储方式相同


printf的用法:输出控制符与输出参数一一对应

输出控制符有:

%d%x%X
%ld%f%lf
%o%c%s
%#X
(输出十六进制数123时,
显示器显示为:0x123,推荐使用)

数据类型

基本数据类型

  • 整数
    • 整型 – int (4个字节)
    • 短整型 – short int (2个字节)
    • 长整型 – long int (8个字节)
  • 浮点数(实数)
    • 单精度浮点数 – float (4个字节)
    • 双精度浮点数 – double (8个字节)
  • 字符 – char (1个字节)

复合数据类型

  • 结构体
  • 枚举
  • 共用体(用处不大,用于单片机,节省内存)

数据类型之间的强制类型转换

- 格式:(数据类型)(表达式)
  • 功能:把表达式的值强制转化为前面所执行的数据类型

  • 例:

  (int)(4.5+2.2);// 6
  (float)(5); // 5.000000
  • 分类:

  • 隐式强制类型转换:1/2.0;

  • 显式强制类型转换:(int)(4.2);

运算符和表达式

运算符

运算符说明
算术运算符+-*/%
关系运算符>>=<<=!===
逻辑运算符! (非)、&&(并且) 、`
位运算符& 按位与、 `
赋值运算符=+=*=/=-=
三目运算符
(条件运算符)
?:
优先级别算术 > 关系 > 逻辑 > 赋值
逗号表达式a, b, c 从左到右执行
i = i*8; // A
i = i<<3;// B

// AB两个语句,在计算机中B语句执行速度更快。

流程控制*

1、什么是流程控制

是程序代码执行的顺序

2、流程控制的分类

  • 顺序执行

  • 选择执行

    • 定义:某些代码可能执行,也可能不执行,有选择的执行某些代码

    • 分类:

      • if

        • 简单if
        • if…else…
        • if…else if…else…
      • switch

        switch(表达式){
            case 常量表达式1: 语句1;
                break;
            case 常量表达式2: 语句2;
                break;
            ...
            default: 语句n;
                break;
        }
        
  • 循环执行

  • 定义:某些代码会被重复执行

  • 分类:

    • for
    • while
    • do … while
  • breakcontinue

    • break:终止当前循环语句
    • continue:跳过本次循环,继续进行下一次循环
  • 嵌套

数组

  • 一维数组

    • 定义:为n个变量连续分配存储空间。
    • 注:
      • 所有变量数据类型必须相同
      • 所有变量所占字节大小必须相等
    // 完全初始化
    int a[5] = {1,2,3,4,5}; 
    // 不完全初始化
    int a[5] = {1,2,3};
    // 不初始化
    int a[5];
    // 清零
    int a[5] = {0};
    // 错误写法
    int a[5];
    a[5] = {1,2};
    a[5] = 100;// wrong 最后为a[4]
    
  • 二维数组

    int a[2][3]={{1,2,3},{4,5,6}}; // 2行3列
    // or
    int a[2][3]={1,2,3,4,5,6};
    
  • 多维数组

    计算机中不存在多维数组,

    因为**内存是线性一维的**,

    n维数组可以当作每个元素是n-1维数组的一维数组。

  • 用途

    排序、求最大/最小值、倒置、查找、插入、删除…

函数*

为什么需要函数

避免了重复性操作,

有利于程序的模块化

什么叫函数

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

物理上:

  • 能够接收数据
  • 能够对接受的数据进行处理
  • 能够将数据处理的结果返回

总结:

  • 函数是一个工具,是为了解决大量类似问题而设计的工具
  • 函数可以当作是一个黑匣子
如何定义函数
函数返回值 functionName(形参列表){
    函数的执行体
    return 表达式;
}
// void 函数没有return返回值

函数返回值类型和return 返回值类型相同,以函数返回值类型为基准。

函数分类
  • 有参函数和无参函数
  • 有返回值和无返回值
  • 库函数和用户自定义函数
  • 普通函数和主函数(main函数)
    • 一个程序有且仅有一个main函数
    • 普通函数可以相互调用
    • 主函数是程序的入口和出口
    • 主函数可以调用普通函数,但是普通函数不能调用主函数
函数的声明

函数是C语言的基本单位,类是Java、C#、C++的基本单位。

  • 第一种写法:后置声明
#include <stdio.h>
void f(); // 注意分号
int main(){
    f();
    return 0;
}
void f(){
    printf("hello world\n");
}
  • 第二种写法:前置声明
#include <stdio.h>
void f(){
    printf("hello world\n");
}
int main(){
    f();
    return 0;
}
注意的问题

return和break:

  • return终止函数
  • break终止循环,终止switch

函数是从上往下执行的。

#include <stdio.h>
void g(){
    f(); // wrong  先执行g() 函数时,还未定义f()函数。
}
void f(){
    printf("hello world");
}
int main(){
    g();
    return 0;
}
形参和实参

形参和实参必须 一一对应 ,个数相同,数据类型相互兼容。

形参:定义函数时,函数中定义形式参数列表中的参数。

实参:调用函数时,所传递的参数。

常用的系统函数

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

int abs(int x):求x绝对值

double fabs(double x):求x的绝对值

递归
变量的作用域和存储方式

按作用域分:

  • 全局变量
  • 局部变量

按变量的存储方式

  • 静态变量
  • 自动变量
  • 寄存器变量

指针

指针就是地址,地址就是指针。

地址就是内存单元的编号。

指针变量,是存放 地址 的变量。

指针和指针变量是两个不同的概念,

但是,通常我们叙述时会把指针变量简称为指针,实际它们的含义并不一样。

// 基本指针
int* p;// p是变量的名字,int* 表示p变量存放的是int类型变量的__地址__
int i = 3;

p = &i;
// 解释说明
/*
1、p保存了i的地址,因此p指向i
2、p不是i,i也不是p,更准确的说:修改p的值不影响i的值,修改i的值也不会影响p的值
3、如果一个指针变量指向某个普通变量,则
	 *指针变量 就完全等同于 普通变量
	例:
	如果p是指针变量,并且p存放了普通变量i的地址
	则p指向了普通变量 i
	*p 就完全等同于 i
	或者说:在所有出现*p的地方都可以替换为i
			在所有出现i的地方都可以替换为*p
	*p就是以p的内容为地址的变量
*/

指针的重要性

  • 指针用于表示一些复杂的数据结构

  • 快速的传递数据,减少内存的损耗

  • 使函数返回一个以上的值

  • 能直接访问硬件

  • 能够方便的处理字符串

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

  • 指针是C语言的灵魂

指针的定义

地址:

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

指针:

  • 指针就是地址,地址就是指针
  • 指针变量就是 存放内存单元编号 的变量,或者说指针变量就是 存放地址 的变量
  • 指针的本质就是一个操作受限的非负整数

指针的分类

  • 基本类型指针
  • 指针和数组
  • 指针和函数
  • 指针和结构体
  • 多级指针

* 的含义

  • 乘法
  • 定义指针变量
  • 指针运算符:该运算符放在已经定义好的指针变量。

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

  • 实参必须为该普通变量的地址
  • 形参必须为指针变量
  • 在被调函数中通过:*形参名 = ... 的方式,就可以修改主调函数相关变量的值
指针和数组
指针和一维数组
  • 一维数组名

    一维数组名是一个指针常量int a[10];其中数组名 a 是指针 常量

    存放的是一维数组第一个元素的地址,输出 &a[0] 等价于 输出a

    a是int* 类型

  • 下标和指针的关系

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

int arr[5]={1,2,3,4,5};
int* parr = a;
for(int i=0;i<5;i++){
    printf("%d ,", arr[i]);
    printf("%d ,", *(parr + i));
    printf("%d ,", parr[i]);
    printf("%d\n", *(arr + i));
}
/*
结论
arr[i] = parr[i] = *(arr+i) = *(parr+i)
*/
  • 指针变量的运算

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

    如果两个指针变量指向的是同一个连续空间中的不同存储单元,则这两个指针变量可以相减。

int a[5];
int* p;
int* q;
p = &a[1];
q = &a[4];
printf("%d\n", q-p); // 3

注:

int* p1;
char* p2;
double* p3;
printf("%d,%d,%d\n",sizeof(p1),sizeof(p2),sizeof(p3));
// 输出结果相同,所占字节与系统的寻址能力有关。
多级指针
int i = 10;
int *p = &i;
int **q = &p;
int ***r = &q;

// ***r == i; // true

结构体

为什么需要结构体

为了表示一些复杂的事物,而普通的基本类型无法满足实际需求

什么叫结构体

把一些基本类型数据组合在一起形成的一个新的复合数据类型,这个叫做结构体。

如何定义结构体

3种方式

// 方式一,推荐使用
struct Student{
    int age;
    float score;
    char sex;
};
// 方式二
struct Student2{
    int age;
    float score;
    char sex;
} stu2;
// 方式三
struct{
    int age;
    float score;
    char sex;
} stu3;
怎么使用结构体变量
  • 赋值和初始化
struct Student{
    int age;
    float score;
    char sex;
};
int main(void){
    // 定义的同时,进行赋值
    struct Student stu1 = {18,89.5,'F'};
    // 先定义,再赋值
    struct Student stu2;
    stu2.age = 19;
    stu2.score = 95.5;
    stu2.sex = 'M';
    
    return 0;
}
  • 如何获取结构体变量中的每一个成员

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

2、指针变量名->成员名 ,在计算机内部转换为 (*指针变量名).成员名

struct Student *ptr = &stu;
ptr->age = 19; // 在计算机内部会转换成(*ptr).age
ptr->score = 87.0;
ptr->sex = 'M';
// ptr->age 等价于 (*ptr).age、stu.age;
  • 结构体变量和结构体变量指针作为函数参数传递的问题

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

#include <stdio.h>
#include <string.h>
struct Student{
    int age;
    char sex;
    char name[100];
};

void inputStu(struct Student s);
void inputStu2(struct Student* pstu);
int main(void){
    struct Student stu;
    inputStu(stu); // 输入
    inputStu2(&stu); // 发送地址
    outputStu(std); // 输出
    
    return 0;
}
// 本函数无法修改值,函数为 错误 使用。
void inputStu(struct Student s){
    s.age = 10;
    strcpy(s.name,"zhangsan");
    s.sex = 'F';
}
// 使用结构体指针
void inputStu2(struct Student* pstu){
    pstr->age = 10;
    strcpy(pstr->name,"zhangsan");
    pstr->sex = 'F';
}
// 输出
void outputStu(struct Student s){
   printf(s.age,s.name,s.sex);
}
  • 结构体变量的运算

结构体变量不能相加、不能相减、不能相互乘除,

可以相互赋值。

  • 举例:动态构造存放学生信息的结构体数组

枚举

什么是枚举、怎样使用。

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

#include <stdio.h>
// 定义了一个数据类型,并没有定义变量,该数据类型的名字是enum WeekDay
enum WeekDay{
MonDay,TuesDay,WednesDay,ThursDay,FriDay,SaturDay,SunDay
};

int main(void){
    enum WeekDay day = WednesDay;
    printf("%d\n",day); // 2
}
枚举的优缺点

代码啊更安全

书写麻烦

专题

进制转换

计算机采用二进制由莱布尼茨提出。

进制就是逢几进一

在汇编中:

  • 数组后加字母B,二进制
  • 数组后加字母O,八进制
  • 数组后加字母H,十六进制

补码

int类型变量

原码:

​ 也叫符号-绝对值码,

​ 最高位0表示正,1表示负,其余二进制位是该数字的绝对值的二进制位

​ 优缺点:

  • 原码简单易懂
  • 加减运算复杂
  • 存在加减乘除四种运算,增加了CPU的复杂度
  • 零的表示不唯一。

反码:

​ 反码运算不便,没有在计算机中应用

移码:

​ 移码表示数值平移n位,n称为移码量,

​ 移码主要用于,浮点数的阶码的存储。

**补码:**计算机存储的形式

​ 已知十进制求二进制

  • 求正整数的二进制
    • 除2取余,直至商为零,余数倒叙排序
  • 求负整数的二进制
    • 先求与该负数相对应的补码,然后将所有位取反,末尾加一,不够位数时,左边补一
  • 零的二进制,全是零

​ 已知二进制求十进制

  • 首位是0,表明是正整数,按普通方法求
  • 首位是1,则表明是负整数,将所有位取反,末尾加一,所得数字就是该负数的绝对值
  • 全是0,就是0

动态内存分配*

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

    int a[5]; // ok
    int len = 5;
    int b[len]; // error
    
  2. 传统形式定义的数组,该数组的内存程序员无法手动释放。在一个函数运行期间,系统为该函数中数组所分配的空间会一直存在,直到该函数运行完毕时,数组的空间才会被系统释放。

  3. 数组的长度不能在函数运行的过程中动态的扩张或缩小。传统数组的长度一旦定义,其长度就不能再更改。

  4. A函数定义的数组,在A函数运行期间可以被其他函数使用,但A函数运行完毕之后,A函数中的数组将无法再被其他函数使用。

为什么需要动态分配内存

动态数组很好的解决了传统数组的上面四个缺陷。

传统数组也叫静态数组。

动态内存分配举例——动态数组的构造

malloc()函数

#include <malloc.h>// 使用malloc函数需要添加这个头文件

// 语法
(指针变量类型)malloc(分配空间大小);
// 例:
int* p = (int*)malloc(16);
free(p);// 释放p所指向的内存
/*
malloc函数只有一个形参,并且形参是整型
16表示请求系统为本程序分配16个字节
malloc函数只能返回第一个字节的地址
p本身所占的内存是静态分配的,p所指向的内存是动态分配的。
*/

// realloc函数
realloc(p,100);// 扩充
realloc(p,2);//缩小
静态内存和动态内存的比较
  • 静态内存是由系统自动分配,由系统自动释放。

  • 静态内存是在 分配的。

  • 动态内存由程序员手动分配,手动释放。

  • 动态内存是在 分配的。

跨函数使用内存的问题
#include <stdio.h>
void f(int** q){
    int i = 5;
    // *q等价于p
    // *q = i; // error
    *q = &i;
}

int main(void){
    int* p;
    f(&p);
    printf("%d\n", *p); // 逻辑上有问题,f()函数使用完之后,内存被释放,没有i值了。
}
#include <stdio.h>
#include <malloc.h>

void f(int** q){
    *q = (int *)malloc(sizeof(int));
    // 等价于 p = (int*)malloc(sizeof(int))
    // q = 5;// error
    // *q = 5;// p = 5;
    // **q = 5;// *p = 5;
}
int main(void){
    int *p;
    f(&p);
    printf("%d\n", *p); // ok,动态内存分配
    
    return 0;
}

链表的使用

算法:

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

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

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

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

  • 广义定义:广义的算法也叫 泛型

    无论数据是如何存储的,对该数据的操作都是一样的。

我们至少可以通过两种结构来存储数据

  • 数组

    • 优点:存取速度快

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

      插入和删除元素的效率很低

  • 链表

    • 专业术语:

      • 头节点:

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

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

        头节点并不存放有效数据,

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

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

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

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

    • 优点:插入删除元素效率高,不需要一个连续的很大的内存

    • 缺点:查找某个元素的位置效率底

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值