c++学习课件(一)

c++学习课件(一)

day01

一、C++ 介绍

1. 语言的产生

C++ 由 Bjarne Stroustrup 于 1979 年在贝尔实验室开始设计开发的,由于C++ 进一步扩充和完善了 C 语言(面向过程),是一种面向对象的程序设计语言 ,所以最初命名为带类的C 。C++ 是 C 的一个超集,事实上,任何合法的 C 程序都是合法的 C++ 程序。C++ 也是目前嵌入式 ( 无人驾驶 ) 的首选语言。 扫地机器人 (激光雷达 、 车轮子 )… 。游戏引擎(底层代码 , c++ )

至少掌握一门编程语言 python , java , c/c++

[外链图片转存失败,

2. C++发展

C++语言发展大概可以分为三个阶段:

  • 第一阶段 从80年代到1995年。这一阶段C++语言基本上是传统类型上的面向对象语言,并且凭借着接近C语言的效率,在工业界使用的开发语言中占据了相当大份额;
  • 第二阶段 从1995年到2000年,这一阶段由于标准模板库(STL)和后来的Boost等程序库的出现,泛型程序设计在C++中占据了越来越多的比重性。当然,同时由于Java、C#等语言的出现和硬件价格的大规模下降,C++受到了一定的冲击;
  • 第三阶段 从2000年至今,由于以Loki、MPL等程序库为代表的产生式编程和模板元编程的出现,C++出现了发展历史上又一个新的高峰,这些新技术的出现以及和原有技术的融合,使C++已经成为当今主流程序设计语言中最复杂的一员。

3. C++优缺点

  • 优点
    1. 实现了面向对象程序设计。在高级语言(底层 , )当中,处理运行速度是最快的,大部分的游戏软件,系统都是由C++来编写的
    2. 功能强大,嵌入式首选。
    3. C++加上严格的codereview (代码检查)可以用来开除猪一样的队友,这是所有其他语言都不具备的功能
  • 缺点
    1. 学习门槛高、难学
    2. 知识点繁杂,有声音说,c++标准委员会仅仅是为炫技并不考虑实际的工业需求

4. 与python比较

程序有两种执行方式,解释执行和编译执行。
python是一种解释语言(弱类型语言 | 动态类型语言),不需要经过编译,是一边解释一边执行,由于底层封装了很多库供程序员使用,所以开发起来方便快捷,且能够很好地跨平台,写一些小工具小程序特别合适。python —> exe —> 发布源码

c++ 则属于编译语言 , (静态类型语言 | 强类型语言),是一种需要编译后运行语言,编译后通过链接,整合其他依赖库,然后生成机器码(可执行文件),以后再运行,无需编译。在指定环境下编译运行,运行效率高 ,c++ —> exe -> 很难查看源码

  • python 代码打印

python xx.py

print(66)

print(66)
print(66)
print(66)


print(66)
print(66)
print(66)
  • c++ 代码打印
#include <iostream>

int main(){
    int x = 5;
    std::cout << x << std::endl;
    return 0 ;
}

二、环境准备

编译环境 + 开发工具

python : python.exe + pycharm

c++ : mingw + clion

1. 安装编译环境

a. GCC 系列

GNU编译器套装(英语:GNU Compiler Collection,缩写为GCC),指一套编程语言编译器,常被认为是跨平台编译器的事实标准。原名是:GNU C语言编译器(GNU C Compiler) 起初只能编译C语言, 之后也变得可处理c++FortranObjective-CAdaGo与其他语言。

另外许多类Unix系统,把gcc当成是标准编译器,比如:Linux

Unix —> linux | mac os

windows : ms-dos

  • MinGW

mingw 名称 全称 W windows.

又称mingw32 , 是将GCC编译器和GNU Binutils移植到Win32平台下的产物。MinGW占用内存、硬盘空间都比较少,能够链接到任意软件,但它对 POSIX 规范的实现没有 Cygwin 库完备。

  • Cygwin

Cygwin的主要目的是通过重新编译,将POSIX系统(例如Linux、BSD,以及其他Unix系统)上的软件移植到Windows上 。 Cygwin包括了一套库,该库在Win32系统下实现了POSIX系统调用的API;还有一套GNU开发工具集(比如GCC、GDB)

b. MSVC系列

与Visual Studio集成发布,微软自己的编译器,VS是一个基本完整的开发工具集,它包括了整个软件生命周期中所需要的大部分工具,如UML工具、代码管控工具、集成开发环境(IDE)等等。

2. 安装开发工具

开发工具种类繁多,有VS | Dev-C++ | code lite | clion| Qt Creator

三、HelloWorld

1. 入门示例

  • 示例代码如下:
#include<iostream>

int main (){
    std::cout << "hello world !" << std::endl;
    return 0 ;
}

理解:

#include : 引入输入输出库

int main(){} : 主函数入口

std:: … 输出打印

return 0 : 函数返回值,一般0 表示正常执行返回。

2. main函数解释

main函数是C++程序的__入口函数__,C++标准要求main函数的返回值类型为int0 表示正常退出

int main() {
    return 0;
}
  • 返回值释疑

main函数的返回值为int,而函数内并没有出现return语句时,同样可以通过编译并正常运行。

这是因为编译器在main函数的末尾__自动添加了return 0;的语句__。

所以,main函数是C++程序经过__特殊处理的函数__。

其他的返回值类型不是void的函数,如果没有使用return语句,编译器将报错。

int main(){
    
}
  • 参数释疑

main函数有两种写法: 一种是不含参数,一种是携带参数。当考虑程序在运行的时候,立即获取到外部传递进来的数据,那么可以使用含有参数的main函数。 参数 argc 表示传递进来的参数总数, args 表示传递进来的参数。 char ** 是二级指针的写法,当然此时我们是不明白二级指针的意思。那么把它看成是二维数组就好了。 值得注意的是: main函数的第一个参数会隐式传递进来当前程序的全路径地址。

int main(int argc , char ** args) {
    
    std::cout << "参数个数:"<< argc << std::endl;
    
    std::cout << "第一个参数:"<< args[0] << std::endl;
    std::cout << "第一个参数:"<< args[1] << std::endl;
    
    return 0;
}

3. 命令函编译

实际上除了使用IDE工具编写C++代码之外,也可以使用记事本编写,然后使用命令行的方式编译执行写好的C++代码。

#include <iostream>

int main() {
    
    std::cout << "Hello world!" << std::endl;

    return 0;
}
  • 打开命令行,输入以下命令即可
g++ -Wall -std=c++11 main.cpp
    
g++ -Wall -std=c++14 main.cpp -o main.exe      -W  all

g++: 是编译工具.

-Wall: 编译过程中显示所有的警告.

-std=c++11: 使用c++11版本进行编译.

main.cpp: 要编译的cpp文件

编译执行完成后,Windows下默认生成 a.exe 文件,linux下默认生成 a.o 文件,macos下默认生成 a.out 文件

-o: 指定输出文件名称

4. 注释

每一种语言都有注释,以便程序员对其编写的代码进行解释说明。c++采用 ///* */ 实现单行和多行注释。

#include <iostream>

int main (){
    
    // 我是c++的单行注释
	std::cout << "hello world" << std::endl;
    
    /*
   	 	我是c++的多行注释
		我可以写多行
    */
    std::cout << "hello world" << std::endl;
    
    return 0 ;
}


练习

​ main函数练习

四、 数据类型

与其他静态类型语言一样,C++也具备大多数语言的数据类型。除下表给出的数据类型之外,其实还有 short | long | long long 等 。 关于数据类型占用的字节长度其实不用刻意的去背,c++提供sizeof()函数来获取某种数据类型占用的空间大小。

数据类型声明
整数类型int10
单精度浮点数float10.5 空间更小
双精度浮点数double10.5 能显示更多的小数位 ,空间更大
字符char‘a’
字符串string“aabbcc”
布尔booltrue | false

五、变量

1. 声明和初始化

c++ 是强类型语言,任何变量在使用前必须先声明,并且在声明的时候必须要指定该变量的数据类型,即该变量未来能够存储的数据类型。

int main(){
    
    //先声明
    int age ;
    double price;
 
    //后初始化
    age = 10 ;
    price = 20.3;

    //声明及初始化
    int age = 20 ;
    
    return 0 ;
}

  • 变量声明以及初始化常用手法:
int age ; //未初始化

int age = 21 ; // C 方式初始化

int age (21); //构造方法初始化

int age {21} ; //c++ 11标准开始的方式

2. 变量命名规则

  1. 可以包含字母 、 数字 和下划线
  2. 必须以字母或者下划线开始

注意:在C++中,大写字母和小写字母被认为是两个不同的字符。因此,sum和SUM是两个不同的变量名。一般地,变量名用小写字母表示,与人们日常习惯一致,以增加可读性。应注意变量名不能与C++的关键字、系统函数名和类名相同

有效命名无效命名
Age2020_Age
ageAge+1
_age$age
My_AgeMy Age
Intreturn

3. 常量

常量其实和变量没有多大区别, 有名字, 占据存储空间,可以是任何的基本类型,但只有一点不同,常量的值不允许变更。C++中的常量的声明需要使用 const 关键字,而python中的常量默认约定是全大写表示。

int main(){
    
    const double pi {3.1415926};
    const int months_in_year{12};

    pi = 2.5 ; //将会出现编译错误
    
    return 0 ;
}

4. 变量长度

sizeof 除了可以作用于类型身上之外,也可以作用于变量身上。

#include<iostream>

int main(){
   
    double a = 20.0;
    sizeof(a); //8

    sizeof(int); //4
    
    return 0 ;
}

六、常见编码错误

  • 忘记分号结尾
int main (){
    int x  //此处编译错误
    x = 5;
    return 0;
}

  • 声明和定义错误
#include <iostream>
int main (){
    x = 5;  //没有声明
    std::cout << x;
    return 0;
}

  • 命名空间错误
#include <iostream>
int main (){
    int x;
    x = 5;
    cout << x;  //缺少命名空间
    return 0;
}

  • 库包含错误
int main (){
    int x;
    x = 5;
    std::cout << x;  //没有引入 io流 库
    return 0;
}

注意: 除了变量 | 对象的声明初始化之外,其他的逻辑代码(需要经过调用 、 运算)都需要在函数内部编写。

七、输入和输出

任何编程语言都要输入和输出,python的输入输出是inputprint , C语言的输入输出是 scanfprintf , 而C++的相对要复杂些,它使用 std::cinstd::cout 来操作输入输出 。 C++的输入输出,需要导入 iostream 库 。

1. 输出

一般在输出的后面跟上 std::endl 来表示输出结束,它除了含有换行的功能之外,还具备了刷新数据打印缓冲区的功能。

#include <iostream>

int main (){
    
    //由于没有换行,两个单词会出于同一行。
     std::cout << "Hello";
     std::cout << " world";
    
    
    //兼备换行的输出
    std::cout << "Hello" << std::endl;
    std::cout << " world" << std::endl;
    
    
    //可以连续输出打印,这其实是一种叫做:链式调用的手法
     std::cout << "Hello " << " World" << " , I love C++!"<< std::endl;
 
    return 0 ;
}

2. 输入

输入旨在获取键盘的数据输入。 不过获取输入前的提示语句得使用 std:: cout来输出提示。

#include <iostream>

int main (){
    std::cout << "请输入您的年龄:"<<std::endl;
    
    int age ;
    std::cin >> age;
    
    std::cout << "您的年龄是:" << age <<std::endl;
    
    return 0 ;
}

3. 练习

输入三条的边长,判断能否组成三角形。

三条边长能否组成三角形的 依据 ??

八、条件与循环

1. 条件判断

1. if语句

条件判断即是生活中的对某个事物进行判断,比如:父子间对此次考试成绩进行约定,如果超过90分,则可以自由活动一天,如果超过80分,可以休息半天,如果低于80分,则要乖乖去 写作业。

#include <iostream>

int main (){
    
    std::cout << "请输入此次考试的成绩:" << std::endl;
    
    int score ;
    std::cin >> score;
    
    if( score  >  90){
         std::cout << "可以休息一天" << std::endl;
    }else if(score > 80){
         std::cout << "可以休息半天" << std::endl;
    }else{
         std::cout << "乖乖去写作业" << std::endl;
    }
    
    return 0 ;
}

2. switch语句

许多语言中都包含switch,比如:java 、javascript 、php 等,而python是个例外,python没有switch。,实际上switchif 语句块很相似, 它是完全的等价条件判断,但是一旦满足某个case的条件,那么其他case 就不会再进行判断。

#include <iostream>

int main (){
    std::cout << "请输入此次考试的成绩评级" << std::endl;
	char level ;
    std::cin >> level;
    switch (level){
        case 'A':
            std::cout << "优秀" << std::endl;
            break;
        case 'B':
            std::cout << "良好" << std::endl;
            break;
        case 'C':
            std::cout << "及格" << std::endl;
            break;
        case 'D':
            std::cout << "仍需继续努力" << std::endl;
            break;
        default:
            std::cout << "输入错误。" << std::endl;
            break;
    }
	return 0 ;
}

2. 运算符操作

1. 关系运算符

表示大小、相等操作的运算符,python和c++表示方法是一样的。

OperatorPythonC++
equal====
not equal!=!=
greater than>>
less than<<
greater than or equal>=>=
less than or equal<=<=
2. 逻辑运算符

有时候需要对多个条件进行一起判断,比如:前面的考试约定,不仅要考察语文的成绩还要考察数学的成绩,不仅仅是单科成绩了。

OperatorPythonC++
并且and&&
或者or||
非(取反)not!
#include <iostream>

int main (){
    int chinese_score ;
    int math_score ;
    
    std::cout << "请输入此次语文考试的成绩:" << std::endl;
    std::cin >> chinese_socre;
       
    std::cout << "请输入此次数学考试的成绩:" << std::endl;
    std::cin >> math_score;
    
    if( chinese_socre > 90 && math_score > 90){
         std::cout << "可以休息一天" << std::endl;
    }else if(score > 80 && math_score > 80){
         std::cout << "可以休息半天" << std::endl;
    }else{
         std::cout << "乖乖去写作业" << std::endl;
    }
    
    return 0 ;
}

3. 三元运算符

一般来说,非真即假的情况下,如果表达式比较简单,通常会采用三元运算符来实现。在相比之下,c++的三元运算符比python的要简单些。比如下面的例子:如果考试成绩大于90,评为A ,否则评为B,此时针对一个条件的判断,只有两种结果,那么使用三元表达式在简洁程度上要胜过 if 语句。

#include <iostream>

int main (){

	int score;
    std::cout << "请输入此次语文考试的成绩:" << std::endl;
    
    std::cin >> score;

    /*
        //python的三元表达式:

        result = 'A' if 100 > 90  else 'B'
        print(result)
    */

    //c++
    char result = score > 90 ? 'A' : 'B';
    std::cout << "您的语文成绩评级为:" << result <<std::endl;
    
    return 0 ;
}

3. 循环控制

如果需要让某件事重复执行多次,那么循环操作再合适不过了。在c++里面,循环操作有:while | do-while | for 三种实现方式 。 比如模拟路口信号灯闪烁场景

1. while
#include <iostream>
int main (){
	
    int count = 0 ;
 	while(count < 10){
        std::cout << "红灯还在亮着..." << std::endl;

        //单位是毫秒
        Sleep(1000);
        cout++;
    }
	return 0 ;
}

2. continue 和 break

有时候我们需要对循环的每一次操作,都做一次过滤检查,满足条件的让循环跳过当前,进行下一次循环,或者直接退出循环。在大部分编程语言里面, continue和 break正扮演着这样的角色。比如下面打印1 到 20 的偶数。 如果打印到了16,则直接退出整个循环。

#include <iostream>
int main (){
	
    int number = 1 ;
 	while(number <= 20){
       
        if(number == 16){
            break;
        }
        
        //满足条件,表示当前的number是奇数。
        if(number % 2 != 0  ){
            continue;
        }
        
        std::cout << "当前打印的数字是: " << number << std::endl;
        
        number++;
    }
    
     std::cout << "循环已结束! "<< std::endl;
	return 0 ;
}

3. do-while

do-while结构与while差不多,区别只在于前者是上来先执行操作后判断,后者是先判断再执行循环操作

#include <iostream>
int main (){
	
    int count = 0 ;  
    do{
        std::cout << "红灯还在亮着..." << std::endl;
         //单位是毫秒
        Sleep(1000);
        cout++;
    }while(cout < 10);
    
	return 0 ;
}

4. for

在众多循环语句中,for 循环是使用频率最高的一种。

#include <iostream>
int main (){	
    for(int cout  = 0 ; cout < 10 ; cout ++){
         std::cout << "红灯还在亮着..." << std::endl;
         //单位是毫秒
        Sleep(1000);
    }    
	return 0 ;
}

4. 练习

编程计算图形的面积。程序可计算圆形、矩形的面积,根据用户选择的图形类型来计算。,然后根据不同图形输入不同参数的值,计算出面积的值后输出显示。

如:
    
请输入您要计算何种类型图形(1: 圆形,2:矩形)
1 
请输入圆的半径
2
您要计算的圆的面积为: 12.56
    
    
请输入您要计算何种类型图形(1: 圆形,2:矩形)
    2
请输入矩形的长度
    10
请输入举行的宽度
    10
您要计算的矩形的面积是: xxxx
    
 要求使用 : switch   (简单版本)
     	
     高级版本:
     	switch 和  循环(高级版本)
            
            
循环提问用户,要计算什么类型的图形面积。
 while(){
     
     请输入您要计算何种类型图形(1: 圆形,2:矩形 , 0:退出)
     
     
 }


九、命名空间

假设这样一种情况,当一个班上有两个名叫 张三的学生时,为了明确区分它们,我们在使用名字之外,不得不使用一些额外的信息,比如他们的家庭住址,或者某些特征等等。

同样的情况也出现在 C++ 应用程序中。例如,您可能会写一个名为 a 的变量,在另一个可用的库中也存在一个相同的变量 a。这样,编译器就无法判断您所使用的是哪一个。

因此,引入了命名空间这个概念,专门用于解决上面的问题,它可作为附加信息来区分不同库中相同名称的函数、类、变量等。使用了命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。

1. 自定义命名空间

使用命名空间范围内的成员(变量、函数、类),需要使用 域操作符::

#include <iostream>

//深圳的张三
namespace shenzhen{
    string name = "张三";
}

//武汉的张三
namespace wuhan{
    string name = "张三";
}

int main() {
    std::cout << shenzhen::name << std::endl;
    return 0;
}

2. 使用using指令

可以使用 using namespace 指令,这样在使用命名空间时就可以不用在前面加上命名空间的名称。这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。

#include <iostream>
using namespace std;

int main(){
    count<<"hi c++" << endl;
    return 0 ;
}

练习

上海和香港都有迪士尼,使用命名空间的方式描述香港和上海两地迪士尼的特征(地址、价格、开营时间…) 。请通过获取用户输入的方式,把对应迪士尼的的信息打印出来。

十、数组

c++的数组实际上和pythonlist 差不多,都是具有下标(索引) , 稍有不同的是,pythonlist不区分类型,而c++的数组必须是同一类型。

  • python的数组
scores = [100,98,88,'zhangsan',True]
for s in scores:
	print(s)

  • c++的数组
#include <iostream>

int main() {
int scores[]{100,95,90,88}
    for(int s : scores){
        std::cout << s << std::endl;
    }
    return 0 ;
}

1. 声明和初始化

数组是一系列相同类型的元素,放置在连续的内存位置,数组中的元素都可以通过索引来单独操作它们。 若查看某个变量存储地址可以使用 取地址符 &

在这里插入图片描述

  • 声明

仅仅声明,而没有初始化的数组,内部的元素无法得到保证,系统会随机进行赋值。

#include <iostream>

int main (){
    
    int scores[5];
    
    //这里遍历打印出来,数组的元素是随机的。
    for(int s : scores){
        std::cout << "s=" <<s << std::endl;
    }
	return 0 ;
}

  • 初始化
int main(){
    //数组类型 数组名称  [元素个数]{初始化列表}

    //1. 声明后再初始化
    int scores [5];
    scores[0] = 11;
    scores[1] = 22;
    scores[2] = 33;
    scores[3] = 44;
    scores[4] = 55;


    //2. 声明并初始化
    int scores [5]{100,89,95,70,80};

    int socres [10]{88,75}; //剩下的都会以0占位,只初始化了前两位

    int socres [10]{0}; // 表示10个长度的数组,每个元素都是0

    //3. 自动推算数组大小
    int socres[]{22,33,44,55,66}; //数组长度没有指定,根据后面初始化长度来推断。
	return 0 ;
}

2. 访问数组

  • 获取数组中的某个元素

数组是具有下标(索引)的容器,可以使用下标 (索引)来获取 , 下标(索引)从 0 开始。型如: 数组名称[元素索引]

#include <iostream>

int main(){
   
    //声明并初始化数组
    int scores [5]{100,89,95,70,80};

    std::cout<<"数组的第一个元素是: "<< scores[0]<<std::endl;
    std::cout<<"数组的第二个元素是: "<< scores[1]<<std::endl;
    std::cout<<"数组的第三个元素是: "<< scores[2]<<std::endl;
    std::cout<<"数组的第四个元素是: "<< scores[3]<<std::endl;
    std::cout<<"数组的第五个元素是: "<< scores[4]<<std::endl;


    //越界,不会报错,但是输出内容不是我们想看到的
    std::cout<<"数组的第一个元素是: "<< scores[5]<<std::endl; 

    //修改指定位置的元素
    scores[0] = 66;
    
    return 0 ;
}

  • 遍历数组

c++的数组,并没有提供获取长度的方法,所以不能直接遍历。一种是直接指定遍历的长度,一种是通过代码计算出数组的长度 ,一种是使用c++11提供的基于范围的for循环

#include <iostream>

int main(){
    //定义数组
    int  scores[]{100,95,97,88,85,80,75};

    //直接指定数组
    for(int i = 0; i < 7; i++){
        std::cout << scores[i] << std::endl;
    }
    
    //手动计算数组长度
    int length = sizeof(scores) / sizeof(int);
    for(int i = 0 ; i < length; i++){
         std::cout << scores[i] << std::endl;
    }

    
    //+++++++++++++++++++++++++++++++++++++++++++++++++++
    
    //c++11 提供的for循环 
    for(int score : scores){
          std::cout <<cores[i] << std::endl;
    }
    return 0 ;
}

3. 多维数组

数组里面的元素存储的还是数组,即可称之为多维数组。二维数组是常见的多维数组,再多维度的数组阅读起来就稍微有点复杂了。二维数组的操作实际上实际上和一维数组并没有多大区别。

  • 声明

数组类型 数组名称[x] [y]; x可以认为是有多少行 , y可以认为是有多少列。多维数组无法进行长度推断,所以具体到行列的长度

在这里插入图片描述
在这里插入图片描述

4. 练习

使用数组保存张三 , 李四, 王五6个学科成绩,并且计算每个人的总成绩、平均分。成绩从键盘录入。

要求使用二维数组。

十一、打卡作业

	1. 使用命名空间定义三个学生,包含变量:姓名、学号、表示6个学科成绩的数组
	
	如果习惯用数组,那么6个学科的成绩就用数组类装,如果不习惯用数组,那么就声明6个变量来接收这个学生的6个学科的成绩。
	
	2. 循环获取三个学生的以上信息。(注意:每个学生有6科成绩要记录)
	
	3. 输入完成后,展示每个学生的总分、平均分。

day02/03

一、 字符串

字符串是最常用的一种数据类型了,在python中声明字符串和声明其他类型的数据一样,都非常的简单。但是在c++中,对于字符串的操作,相对来说要稍微复杂一些。

C++ 提供了以下两种类型的字符串表示形式:

  • C 风格字符串
  • C++ 引入的 string 类类型

1. C 风格字符串

C 风格的字符串起源于 C 语言,并在 C++ 中继续得到支持。字符串实际上是使用 null 字符 \0 终止的一维字符数组。如下面的声明和初始化创建了一个 “Hello” 字符串。由于在数组的末尾存储了空字符,所以字符数组的大小比单词 “Hello” 的字符数多一个。

int main(){
    
    char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

    //可以简写成:
    char greeting2[] = "Hello";
    
    return 0 ;
}

(C:\Users\86176\Desktop\c++\资料\img\string.png)]

2. C风格字符串操作

1. 遍历字符串

字符串实际上背后还是一个数组,所以可以使用数组遍历的手法来获取每一个字符 。

#include<iostream>
int main(){
    
	//最后总会跟着一个\0的空字符,此时括号中如果写长度,必须大于等于6
	char  name[] = "hello";

    for (int i = 0; i < sizeof(name ) / sizeof(char); ++i) {
        std::cout << name[i] << std::endl;
    }
}

2. 字符串其他操作

C语言中提供了针对字符串操作的大量函数,不过在使用之前,需要先引入 #include<cstring>

以下函数的使用,需要引入 #include<ctype.h> 头文件

在这里插入图片描述
在这里插入图片描述

#include <iostream>

int main(){
    
    //拷贝字符串
    char  name[] = "hello";
    char name2[6];

    //参数一: 目标字符串, 参数二:源字符串
    strcpy(name2 , name);
    std::cout << name2 << std::endl;
    
    //拼接字符串
    strcat(name2 , " , 张三");
    std::cout << name2 << std::endl;

    // 返回字符串长度
    int len = strlen(name2);
    std::cout << "name2的长度:" << len << std::endl;
    
    return 0 ;
}

2. C++ 风格字符串

C++ 标准库提供了 string 类类型,支持上述所有的操作,另外还增加了其他更多的功能。需要引入 #include<string> ,由于string类声明在命名空间 std ,所以在使用的首要注意 命名空间的联合使用 。

//引入string库
#include <string>

using namespace std;

int mian(){
    
    string s1;
    string s2 {"北京"};
    string s3{s2};
    
    string s4 = "你好";
    
    s1 = s3;
    return 0 ;
}

4. C++风格字符串操作

  • 拼接字符串

c++的字符串拼接,使用起来比较方便,直接使用+ 操作即可。

#include<string>
using namespace std;

int main(){
    string part1 {"c++"};
    string part2 {" is a powerful"};

    string sentence ; 
    sentence = part1 + part2 ;
    return 0 ;
}

  • 获取指定位置的字符

可以使用[]和 at()操作字符串

#include<string>
using namespace std;

int main(){
    string s1 {"i love c++"};

    cout << s1[3]<<endl;
    cout << s1.at(0) << endl;
   return 0 ;
}

  • 遍历字符串
#include<string>
using namespace std;

int main(){
    string s1 {"abcdef"};

    for(char s : s1){
        cout << s << endl;
    }

    for(int s : s1){
        cout << s <<endl;
    }
	return 0 ;
}

  • 字符串比较

字符串也是可以比较大小的。

#include<string>
using namespace std;

int main(){

    string s1{"Apple"};
    string s2{"Banana"};
    string s3 {"kiwi"};
    string s3 {"apple"};
    string s3 {s1};

    s1 == s5 // true
    s1 == s2 // false
    s1 != s2 // true
    s1 < s2 // True
    s1 > s2 // false
    s1 == "Apple" // false
        
	return 0 ;
}

  • 截取字符串
#include<string>
using namespace std;

int main(){

    substr(开始索引, 截取长度);

    string s1 {"This is a test"};
    cout << s1.substr(0 , 4) ; // This
    
	return 0 ;
}


  • 获取字符(字符串)在字符串中的索引
#include<string>
using namespace std;
int main(){
    find(搜索的字符)

    string s1 {"This is a test"};
    cout << s1.find("This") ; // 0 
    cout << s1.find("is") ; // 2
    cout << s1.find("test") ; // 10 
    
   return 0 ;
}


  • 获取字符串长度

length() : 返回字符串长度

5. 练习

给定一串字符串,判定里面有多少个数字,多少个字母。并且把最后那个字母a修改为大写字母A

修改用的函数是:replace(修改起始索引位置,修改的横跨长度,修改成什么字符);

判断是否是字母和数字使用的是 isalpha ()isdigit()  ,需要引入#include<ctype.h>


二、 Vector

Vector其实很大程度上和数组一样,只是数组是固定长度,而vector是不定长度(动态增长)。 假设我们需要记录明年的测试成绩,但是我们并不知道明年会有多少个学生。那么可以有两种选择,定义一个固定长度的数组,这个长度超过假设的长度, 另一种办法就是使用动态数组,比如是: vector

vector 在C++STL(标准模板库)中的一个容器,可以看成是对容器的一种扩展。在运行时可以改变长度 , 与数组具有相似的语法 , 相比数组更高效 , 提供越界检查

1. 声明和初始化

  • 声明

使用vector除了要导入#include之外,由于它声明于std命名空间里面,所以要配合std命名空间使用

#include <vecotr>
using namespace std;

int main(){
    
    vector <char> vowels; 
    vector <int> test_score;

    // =========================
    vector <char> vowels(5);  //声明一个初始大小为5的char类型vector
    vector <int> test_score(10);
   	return 0;
}


  • 初始化
#include <vecotr>
using namespace std;

int mian(){
    //数组定义
    int test_score []{100,99,18,81}

    //vector定义
    vector <char> vowels {'a' , 'e' , 'i' , 'o' ,'u'}; 
    vector <int> test_score{ 100 ,98,95,90,80};
    vector <double> temperatures{26,20.7};
    
    return 0;
}


2. 访问vector

访问 vector中的元素有两种方式,一是仍以数组的方式,另一种是使用vector提供的at函数

  • 数组的语法
#include <iostream>
#include <vector>
using namespace std;

int main(){
    vector<int> test_score {100,90,85};

    cout << "第一个成绩是: " <<test_score[0] << endl;
    cout << "第二个成绩是: " <<test_score[1] << endl;
    cout << "第三个成绩是: " <<test_score[2] << endl;

    cout << "第三个成绩是: " <<test_score[3] << endl;  //不会检查越界
 	return 0 ;   
}


  • vector的语法
#include <iostream>
#include <vector>
using namespace std;

int main(){
    
    vector<int> test_score {100,90,85};

    cout << "第一个成绩是: " <<test_score.at(0) << endl;
    cout << "第二个成绩是: " <<test_score.at(1) << endl;
    cout << "第三个成绩是: " <<test_score.at(2) << endl;
    
    cout << "第三个成绩是: " <<test_score.at(3) << endl; //抛出越界异常
    return 0 ;
}



3. 操作vector

  • 修改vector中的元素
#include <vector>
using namespace std;

int main(){

    vector<int> test_score {100,90,85};
    test_score.at(0) = 73;
    
	return 0 ;
}


  • 往vector中追加元素
#include <vector>
using namespace std;

int main(){
    vector<int> test_score {100,90,85};

    test_score.push_back(80); // 100 , 90 , 85 , 80
    test_score.push_back(95); // 100 , 90 , 85 , 80 , 95

	return 0 ;
}


  • 越界检查

只要当我们使用了vector的语法去获取超出索引的元素时,就会抛出异常。而使用数组的语法去获取元素,则不会进行越界检查

在这里插入图片描述

  • 遍历vector
#include <iostream>
#include <vector>
using namespace std;

int main(){
    
    //使用下标遍历
    vector<int> scores{ 100 ,95 ,88 ,80 ,75};
    for (int i = 0; i < scores.size(); ++i) {
        cout << scores[i] << endl;
    }

    //基于范围for遍历
    vector<int> scores{ 100 ,95 ,88 ,80 ,75};
    for(int score : scores){
        cout << score << endl;
    }
    return 0 ;
}


4. 二维vector

二维vector和二维数组实际上差不太多,二维数组是数组里面装的是数组,二维vector指的是vector里面装的还是vector,在未来碰到矩阵相关的存储操作,多半使用vector来作为媒介。 比如下面的例子,演示了使用vector来存储3个班的考试成绩。每个班的成绩单独使用一个vector来存储。

#include <iostream>
#include <vecotr>
using namespace std;

int main(){

    //声明并初始化vector
    vector<vector<int>> scores {
            {95,77,80,85},
            {58,89,93,100},
            {69,73,81,97}
    };

    for (int i = 0; i < scores.size(); ++i) {
        for (int j = 0; j < scores[i].size(); ++j) {
           cout << scores[i][j] <<"\t" ;
        }
        cout << endl;
    }
	return 0 ;
}


5 . 练习

使用二维vector记录三个学生的6门学科成绩,并且计算每个学生的总分,平均分。


三、 函数

1. 函数介绍

在大多数地方,c++ 和 python的函数是一样的,都是用来包裹定义好的语句,避免重复拷贝粘贴。不过还是有些许不一样的地方。

  1. python的函数是以回车换行结尾,c++的函数是以 大括号结尾
  2. python的函数通常使用缩进方式来表示函数体, ,c++使用大括号区域来表示
  3. python是动态类型语言,而c++是静态类型语言,所以有时候需要像声明变量一样,声明函数。

在这里插入图片描述

2. 定义函数

函数的定义一般可以包含以下几个部分: 方法名称方法参数返回值方法体 , 根据可有可无的设置,函数一般会有以下4种方式体现。

  • 声明并调用函数
#include <iostream>
using namespace std;

void say_hello(){ 
    count << "hello" << endl;
}

int main(){
    
    say_hello();
    return 0 ;
}


1. 无返回值无参数
void say_hello(){ 
    count << "你好 " << endl;
}

int main(){
    say_hello();
    return 0 ;
}


2. 无返回值有参数
#include<iostream>

using namespace std;

void say_hello(string name){ 
    count << "你好 "<< name << endl;
}

int main(){
    say_hello("张三");
    return 0 ;
}


3. 有返回值无参数
#include<iostream>

using namespace std;

string say_hello(){ 
   return "你好 张三";
}

int main(){
    cout << say_hello() << endl;
    return 0 ;
}


4. 有返回值有参数
#include<iostream>

using namespace std;

string say_hello(string name){ 
   return "你好 "+ name;
}

int main(){
    cout << say_hello("张三") << endl;
    return 0 ;
}


3. 函数原型

般来说,c++的函数一般包含声明和定义两个部分。因为c++是静态类型语言,程序属于自上而下编译,所以在使用函数前,必须先表示函数的存在,告诉编译器函数所需要的参数以及函数的返回值是什么。

1. 函数定义在前

在调用函数之前,事先先定义好函数。

#include <iostream>
using namespace std;

//函数定义 ,函数的真正实现。
int add(int a , int b){
    return a + b ; 
}

int main(){
    cout << add(1 ,2)<< endl;
    return 0 ;
}


2. 使用函数原型

把函数分成声明和定义两部分,函数的原型定义在调用的前面,具体实现可以放在后面。

#include <iostream>
using namespace std;

//函数声明 ,也叫函数原型 并不知道这个函数具体是如何实现的。只是有一些基本架子而已。
int add (int a , int b);

int main(){
    cout << add(1 ,2)<< endl;
    return 0 ;
}

//函数定义 ,函数的真正实现。
int add(int a , int b){
    return a + b ; 
}


4. 分离式编译

一般说来,函数的声明 ( 函数原型 )通常都会放到头文件中,之所以称之为头文件是因为它总是在main函数的前面就引入进来。头文件一般以 .h 或者 .hpp 结尾,通常用于 写类的声明(包括类里面的成员和方法的声明)、函数原型、#define常数等,但一般来说不写出具体的实现

  • math.h

为了能够让声明和定义能够快速的被关联上,通常它们的名称会被定义成一样的,这已经成为了一种默认的规定

//函数声明
int add (int a , int b);


  • math.cpp
#include "math.h"

//函数定义 ,函数的真正实现。
int add(int a , int b){
    return a + b ; 
}


  • main.cpp
#include <iostream>  
#include "math.h" //这里使用"" 表示从当前目录查找

int main(){
    add(1 ,2);
    return 0 ;
}


5. 函数重载

在许多语言中,经常会见到两个或者两个以上的函数名称是一样的,当然他们的 参数个数 或者 参数类型 或者是 参数的顺序 是不一样的。这种现象有一个学名叫做 重载 overload, 由于python属于动态类型语言,不区分数据类型,参数可以是任意类型,所以它没有重载。

下面的示例代码即是对加法运行进行了重载,以便能够针对不同的数据类型,不同的参数个数做出匹配

int add(int a , int b){
    return a + b ;
}

int add(int a , int b , int c){
    return a + b + c;
}


int add(double a , double b){
    return a + b ;
}

int main(){
    add(3, 3);
    add(3, 3, 3);
    add(2.5 , 2.5);
        
    return 0 ;
}


**思考: 为什么python没有函数重载? **

6. 函数参数

python的函数,在传递参数的时候,有可变对象和不可变对象的现象,那么在C++里面也有类似的说法。只不过是另一种说辞罢了。

python中传递不可变对象,在C++中,对应的是值的拷贝,也就是传递的只是数据的一份拷贝而已。在函数内部修改数据,并不会改变外部数据

python中传递可变对象,在c++中,对应的是引用传递,也就是传递的是对象的引用,而不是拷贝。在函数内部修改数据,会导致外部数据也发生改变。

1. 值传递

C++默认情况下,处理函数参数传递时,多数使用的是值的拷贝,少数部分除外。

#include<iostream>
using namespace std;

void  scale_number(int num);

int main(){
    int number{1000};
    scale_number(number);
    
    //打印number 1000
    cout << number <endl;
    return 0 ;
}

void scale_number(int num){
    if(num > 100)
        num = 100;
}


2. 传递数组

函数的参数除了能传递普通简单的数据之外,数组也是可以传递的。但是数组稍微有点特殊,这里多做讲解。

  1. 前面提过,形参实际上就是实参的一份拷贝,就是一个局部变量。
  2. 数组的数据太大,如果都进行拷贝,那么比较麻烦,也造成了浪费
  3. 所以实际上传递数组的时候,并不会进行整个数组的拷贝,而只是传递数组的第一个元素内存地址 (指针 ) 进来。
  4. 数组的数据还是在内存中,只是把第一个元素(也就是数组的起始)内存地址传进来而已。
  5. 这就造成了函数的内部根本无法知道这个数组的元素有多少个。
#include<iostream>
using namespace std;

using namespace std;
//传递数组长度
void 	print_array(int number[] , 5 );

int main(){
    //声明数组
    int array []{1,2,3,4,5};
    
    //打印数组
    print_array(array , 5);
    
    return 0 ;
    
}

//传递数组,打印数组
void print_array(int array[] , int size){
    for (int i {0} ; i < size ; i++){
        count << array[i] << endl;
    }
}


3. 传递引用

目前为止,我们所有函数的参数传递,都是对数据进行了一份拷贝(数组除外)。那么在函数的内部是不能修改值的,因为这仅仅是一份值得拷贝而已(函数外部的值并不会受到影响)。如果真的想在函数内部修改值,那么除了数组之外,还有一种方式就是传递引用

引用实际上只是原有数据的一种别名称呼而已,使用 & 定义

#include<iostream>
using namespace std;


void  scale_number(int &num);

int main(){
    int number{1000};
    scale_number(number);
    
    //打印number100
    count << number <endl;
    return 0 ;
}

void scale_number(int &num){
    if(num > 100)
        num = 100;
}


4. 练习

既然掌握了使用传递引用,以便修改外部数据,那么来修改下传递vector吧。vector和普通的数据一样,传递的时候,做的是值得拷贝。数据有点浪费,只想传递引用,怎么办呢?

有一个装有6个学科分数的vector,请把这个vector传给另一个函数change_score()函数,在该函数内部 请使用基于范围的for循环对vector进行遍历,把vector里面所有低于60分的分数,修改为:100分。


7 . 函数是如何被调用工作的

  1. 函数是使用函数调用栈来管理函数调用工作的。
    1. 类似盒子的栈
    2. 遵循后进先出
    3. 可以往里面执行压栈和出栈动作(push 和 pop)
  2. 栈的结构和激活记录
    1. 函数必须把它的返回值返回给调用它的函数(A —> B)
    2. 每次函数的调用都需要创建一次激活记录,然后把它压入栈中(push)
    3. 当一个函数被调用完毕的时候,就需要从栈中弹出(pop)
    4. 函数的参数以及内部的局部变量都是存储在栈中。
  3. 函数栈有可能抛出栈溢出异常(Stack Overflow)
    1. 一旦函数调用栈中被push进来的函数记录过多,就有可能出现。(例如:无限循环调用 | 递归 )
void func2(int &x , int y , int z){
    x +=y+z;
}

int func1(int a , int b){
    int result{};
    result += a + b;
    func2(result , a , b );
    return result ; 
}

int main (){
    int x{10};
    int y{20};
    int z{};
    
    z = func1(x , y );
    
    cout << z << endl;
    return 0 ;
}


8 . 内联函数

函数可以使我们复用代码,但是一个函数的执行,需要开辟空间、形参和实参进行值得拷贝,还要指明函数返回、以及最后回收释放资源的动作,这个过程是要消耗时间的。

  • 作为特别注重程序执行效率,适合编写底层系统软件的高级程序设计语言,如果函数中只有简单的几行代码,那么可以使用inline 关键字来解决了函数调用开销的问题
#include<iostream>

inline int calc_Max (int a, int b)
{
    if(a >b)
        return a;
    return b;
}

int main(){
    
    int max = calc_Max(3, 8);
    std::cout << "max = " << max << std::endl;
    
    return 0 ;
}


增加了 inline 关键字的函数称为“内联函数”。内联函数和普通函数的区别在于:当编译器处理调用内联函数的语句时,不会将该语句编译成函数调用的指令,而是直接将整个函数体的代码插人调用语句处,就像整个函数体在调用处被重写了一遍一样。

有了内联函数,就能像调用一个函数那样方便地重复使用一段代码,而不需要付出执行函数调用的额外开销。很显然,使用内联函数会使最终可执行程序的体积增加。以时间换取空间,或增加空间消耗来节省时间,这是计算机学科中常用的方法。

9. 范围规则

在学习过程,我们会定义很多变量或者引用、这些变量由于定义的位置不同,所以它们的作用域范围也不同。一般会划分成几种类型: 代码块 | 局部变量 | 静态变量 | 全局变量

  • 单独代码块
#include<iostream>
using namespace std;

int main(){
    
    int  num{10};
    int  num1{20};
    
    cout << num << num1 << endl;
    
    {
        int num{100};
        cout << "num = "<< num << endl;
        cout << "num1 = "<< num1 << endl;
    }
    
}


  • 函数中局部变量
#include<iostream>
using namespace std;

int num{300};
void local_example(int x){
    
    
    int num{1000};
    cout << "num =" << num << endl;
    
    num = x ;
    cout << "num =" << num << endl;
 
    
}


  • 静态本地变量
  1. 只会初始化一次
  2. 重复调用函数也只会初始化一次。
#include<iostream>
using namespace std;

void static_local_example(){
    
    static int num{100};
    cout << "num ="<< num << endl;
    num+=100;
    cout << "num ="<< num << endl;
}

int main(){
    static_local_example();
    static_local_example();
    return 0 ;
}


  • 全局变量

通常声明在所有函数和类的外部 ,若存在局部变量和全局变量同名情况下,可以使用 域操作符 :: 来访问全局变量

#include<iostream>
using namespace std;

int age = 99;
int main(){
    
    int age =18 ;
    cout << ::age << endl;
    return 0 ;
}


10. 打卡作业

  1. 有两个字符串 A , B , A 为源字符串,B 为要删除的字符串,判断A是否包含B ,如果包含,请把A 里面包含B的字符删除后,输出全新的字符串A,否则直接输出源字符串A。
string a = "ab123def78cc09"; //源字符串
string b = "cc";//要删除字符串
字符串的删除函数: a.delete()  | a.remove() | a.erase() 这三个函数,必有一个!


  1. 定义一个计算器,提供加减乘除功能

  2. 定义一个calc.h 作为加减乘除 四个函数的声明

  3. 定义一个calc.cpp 作为计算器的具体实现

  4. 定义一个main.cpp 作为程序的入口

  5. 使用二维vector 用于保存张三、李四、王五,三人的6个学科的成绩,

  6. 定义一个二维vector,名叫:score_vector,用来存储三个学生的6个学科成绩

  7. 定义一个initScore(vector<vector>)函数负责从键盘录入成绩 获取3个人,6个学科成绩

  8. 定义一个函数updateScore(vector<vector>) ,用于更新分数,把每个人的不及格的成绩全部修改成99分,

  9. 定义一个函数 printScore (vector<vector>),遍历打印三个人的每个学科成绩。

  10. 要求使用函数原型、分离式写法(头文件和源文件) 、函数传递引用,基于范围for循环。

  11. 禁止定义全局静态vector ,要求2 ,3 ,4 步骤的函数要携带参数,把二维vector传递进去。

  12. 超纲: 应该要考虑引用,否则更新的操作无法实现。具体使用可看上面的引用。

  13. 应该有3个文件 stu.h 、stu.cpp , main.cpp

    1. stu.h :用于声明三个函数
    2. stu.cpp用于实现三个函数
    3. main.cpp 用于程序的执行入口
vector<vector<int>> score_vector;

int main (){
    
    initScore( score_vector ); //往这个二维vector里面装成绩
    
    updateScore( score_vector ); // 遍历二维vector,更新成绩  【必须要用引用 | 指针】
    
    printScore( score_vector ); //打印成绩。
    
    //超纲: 引用。
    return 0 ;
}


day04/05

一、 指针

1. 什么是指针

int a = 3;

int 指针 = 内存地址 (十六进制的数据);

指针其实就是一个变量,不过它的值是一个内存地址 , 这个地址可以是变量或者一个函数的地址

当你声明明一个变量的时候,计算机会将指定的一块内存空间和变量名进行绑定;这个定义很简单,但其实很抽象,例如:int x = 5; 这是一句最简单的变量赋值语句了, 我们常说“x等于5”,其实这种说法是错误的,x仅仅是变量的一个名字而已,它本身不等于任何值的。这条statement的正确翻译应该是:“将5赋值于名字叫做x的内存空间”,其本质是将值5赋值到一块内存空间,而这个内存空间名叫做x。切记:x只是简单的一个别名而已,x不等于任何值。

在这里插入图片描述

  • 为什么需要指针?

不就是使用变量或者调用函数吗?难道不能直接调用吗?那么需要指针做什么呢?

实际上是可以的,但是并不是所有的情况都可以。比如:
1. 在内部函数中,可以使用指针访问外部函数中定义的某个变量x, 因为它并不是声明在自己的函数范围内。
2. 指针在处理函数传递数组的时候非常高效
3. 我们还可以在堆内存中申请一块动态内存,这块内存甚至没有一个变量名称,唯一的访问方式是通过指针。
4. 可以使用你指针访问指定的内存地址(游戏修改器) 内存修改器 | 金山游戏 | CE 。。。

2. 指针使用

1. 声明指针

1.声明指针的时候要记得初始化,如果没有初始化,指针存放的将会是垃圾数据(因为你根本不知道它指向何方)

  1. 可以使用nullptr(c++11)进行指针初始化,初始化存放的值是 0
变量类型  *指针名称;

int * int_ptr;
double * double_ptr;
char * char_ptr;
string * strng_ptr;

2. 初始化指针

指针的指向是一个块内存地址,如果仅仅是声明而未初始化,那么指针的指向无法得到保证,如果没有明确、指针的指向,那么最好把它初始化成一个空指针,也就是表示目前没有指向,空指针的值是0

//初始化指针
int a = 3 ;
int *p0 = &a; //p0是一个指针,指向的是一个int类型的数据,这个数据是3.

//空指针
int *p1 = nullptr;
int *p2 = NULL;
int *p3 = 0 ;

3. 指针地址和大小

指针实际上也是一个变量,也会有自己的内存空间,也会有自己的长度大小。获取指针的内存地址,依然使用取地址符 & , 长度大小依然使用sizeof 来获取

int age = 88;
int *p = &age;

cout << "指针的地址是: " << &p <<endl;
cout << "指针存储的是: " << p <<endl;
cout << "指针大小是: " << sizeof p <<endl;
cout << "age的大小是: " << sizeof age <<endl;

3. 指针dereference( 解引用)

所谓的指针dereference就是,指针就是一个变量,存放的是一个地址。这个地址有可能是变量 a 或者是变量b的地址。有了这个地址,我们可以通过dereference操作符 * 去获取到a对应的值或者b对应的值。

#include<iostream>
using namespace std;

int main(){
    
    //定义一个变量score,赋值100
    int score {100}

    //定义一个指针score_ptr 指向score的地址。
    int *score_ptr{&score};

    //通过指针,获取到指向位置的数据 打印100
    cout << *score_ptr << endl; 

    //使用指针修改原来的score
    *score_ptr = 200 ;
    
    //使用指针和变量的方式打印score,结果都输出200
    cout << *score_ptr << endl;  
    cout << score << endl; 
	return 0 ;    
}

练习

定义一个函数,用于交换两个变量的值。请使用指针来完成

void changeValue( int a,int  b){ //变量a ? 还是指针? 不要写引用。
    
    //在这里面更改你的值。
}

int main(){
    int a = 3 ;
    int b = 4;
    
    changeValue(a , b);
    
    
    cout <<"a =" << a << endl; // 4
    cout << "b = " << b <<endl; //3
    return 0 ;
}

4. 动态内存分配

在进行编码的时候,我们根本不知道需要多少内存空间。举个例子,比如我们需要存储学生的数据,这时候可以使用数组来存储,那么就必须知道学生的具体人数。如果不知道,就无法使用数组了。实际上之前学过的vector就是使用动态内存。但是有时候,我们如果需要存放的是一单个对象数据,并不是一堆数据。用vector就有点浪费了。

为了解决上述问题,C++ 提供了一种“动态内存分配”机制,使得程序可以在运行期间,根据实际需要,要求操作系统临时分配一片内存空间用于存放数据。此种内存分配是在程序运行中进行的,而不是在编译时就确定的,因此称为“动态内存分配”。申请动态内存

1. 申请内存

可以使用new 关键字来申请动态内存 , new 开辟出来的空间都位于堆内存中。

#include<iostream>
using namespace std;

int main(){
    //定义一个int类型的指针,并没有指向任何地方。
    int * int_ptr{nullputr};
	
    //在堆中申请内存,使用指针指向
    int_ptr = new int ; 
    
    //由于未赋值,所以输出的可能是未知的值
    cout << *int_ptr << endl; 
    
    //修改开辟空间的数据值为100
    *int_ptr = 100;
    
    //解引用,输出100
    cout << *int_ptr << endl;
	return 0 ;
}

2. 释放内存

new 常和 delete 成对出现,使用 new 开辟空间, 使用 delete 释放申请的内存,避免造成内存泄漏

#include<iostream>
using namespace std;

int main(){

    int *int_ptr{nullptr};
    
    int_ptr = new int ; //申请内存
    ...
    delete int_ptr ;     //释放内存

 	return 0 ;   
}

3. 数组操作

使用new int[] 来给数组申请动态内存 , 然后使用 delete[] 释放申请的内存

#include<iostream>
using namespace std;

int main(){

    int *array_ptr{nullptr};
    int size{};
    cout << “你期望的数组大小是:” << endl;
    cin >> size ;

    array_ptr = new int[size];
    
     //释放申请的空间
    delete [] arrray_ptr;
    
 	return 0 ;   
}


4. 关于动态内存的思考

通常情况下,定义的变量存储的位置位于栈内存中,栈内存的数据,当函数执行结束后即会被释放,这是栈内存的机制自己决定的,并且栈内存中由于内存并不是太大,所以不建议大量的数据存放在栈内存。

而堆内存中的容量相比栈内存要大多了,但是堆内存并不提供回收释放的工作,允许程序申请内存空间,但是同时也要自己负责内存空间的释放工作。

不能一概而论哪一种是最优的决定,要根据开发场景来决定。

如果你想让这份数据存留的时间更长,不以函数的执行结束而销毁,那么就需要使用堆内存来存放。int *a = new int;如果这份数据是与函数共存亡,其他地方也用不上,那么就直接定义到栈内存里面去就行。 int a = 3;

5. 数组和指针的关系

数组其实和指针是存在一些内在联系的,如下:

  1. 根据数组名字取到的内存地址,是数组的第一个元素地址
  2. 指针其实是一个变量,这个变量存放的值是内存地址
  3. 如果一个指针和数组是同样的类型,并且指针存放的地址正好是数组的某个元素地址,那么可以通过该指针操作数组
1. 数组与指针
#include<iostream>
using namespace std;

int main(){
    
    //定义3个长度的int类型数组
    int scores []{100, 95 , 98};

    //直接打印数组,实际上是打印数组第一个元素的地址   0x61fec8
    cout << scores  << endl;  
    
    //使用*操作符是根据地址获取数据,所以取到的是第一个元素 : 100
    cout << *scores  << endl; 

     //声明指针,存放的是数组第一个元素的地址
    int *score_ptr{scores};  
    
    //打印指针,其实输出它保存的地址,即数组首元素地址 0x61fec8
    cout << score_ptr  << endl;  
    
    //解引用,输出的是数组的首元素 100
    cout << *score_ptr  << endl; 
    
    return 0 ;
}


2. 指针运算

如果指针指向的是数组的第一个元素地址,那么同样可以通过对指针进行加减运算,来获取其他的元素 。值得注意的是,指针的相加并不是单纯数字上的相加,而是指针对应类型占用字节的相加

#include<iostream>
using namespace std;

int main(){

    //定义3个长度的int类型数组
    int scores []{100, 95 , 98};
    
    //定义一个int类型指针,指向的是数组的首元素
    int *score_ptr{scores};

    //使用数组的手法打印数组
    cout << score_ptr[0]  << endl; //100
    cout << score_ptr[1] << endl; //95
    cout << score_ptr[2] << endl; //98

  
	//对指针进行加法运算。由于score_ptr 是int类型,
    //而int类型占用4个字节,所以每次相加打印出来的地址都会变长4个字节
    cout <<score_ptr  << endl; // 0x7fffacde9420
    cout <<(score_ptr+1)  << endl; // 0x7fffacde9424
    cout <<(score_ptr +2) << endl; // 0x7fffacde9428
    
    //指针解引用取值
    cout <<*score_ptr  << endl; // 100
    cout <<*(score_ptr+1)  << endl; // 95
    cout <<*(score_ptr +2) << endl; // 98

    return 0 ;
}


6. 指针算数

指针除了表示存储的是内存地址之外,它也可以做算术运算 和 比较大小。

指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。

1. 指针递增

指针递增 , 如果是数组操作,可以指向后一个元素

int score[]{90,85,100};
int * score_ptr{score}

for(int i{0} ; i<3 ; i++){
    
    cout << ptr << endl;
    cout << *ptr << endl;
    
    ptr++; // 指针移动指向下一个元素
}


2. 指针递减

指针递减 , 如果是数组操作,可以指向前一个元素

int score[]{90,85,100};
int * score_ptr;

score_ptr = score[2]; //指针指向的是数组最后一个元素内存地址

for(int i{3} ; i>0 ; i--){
    
    cout << ptr << endl;
    cout << *ptr << endl;
    
    ptr--; // 指针移动指向下一个元素
}


3. 等价判断

两个指针的等价判断,实际上是他们指向的地址比较

#include<iostream>
using namespace std;

int main(){
    string s1{"张三"};
    string s2{"张三"};

    string *p1 {&s1};
    string *p2 {&s2};
    string *p3 {&s1};


    cout << (p1 == p2) <<endl;  //false 
    cout << (p1 == p3) <<endl;  //true

    cout << (*p1 == *p2) <<endl;  //true 
    cout << (*p1 == *p3) <<endl;  //true
    
    return 0 ;
    
}


4. 练习

有一个int类型数组,存储着张三的6个学科成绩,其中有的学科成绩不及格,现在想使用updateScore() 函数来更新不及格的分数为100分。updateScore (int count , int [] scores) count : 为数组的长度, scores 用于接收数组。 在函数内部使用指针运算,修改分数。

7. 指针与常量

1. 指针常量

const int *p 表示指针指向常量 , 不允许修改对应的值,但是可以指向别的地方, 这和常量修改值一样。

int main(){
    
	//定义高分和低分的变量
    int high_score{100};
    int low_score{75};

    //使用指针常量指向高分。这里的const修饰的是指向的数据
    const int *score_ptr {&high_socre};

    //不允许修改值,因为此时编译器会认为high_score 是一个常量
    *score_ptr = 86 ; // 错误
    
    //可以指向其他位置。
    score_ptr = &low_score ; // 正确
    
    //当然也可以通过变量方式来修改值。
    high_score = 88;

	return 0 ;
}


2. 常量指针

int* const p 表示这个指针是常量指针,不能再指向别的地方了,但是可以修改目前指向地方的值

int main(){
	
    int high_score{100};
    int low_score{75};
	
    //表示这个指针是一个常量,这里的const修饰的是指针
    int *const score_ptr{&high_score};

    //允许修改值,但是不允许再做其他指向
    *score_ptr = 86 ; // 正确
    score_ptr = &low_score ; // 错误
    return 0 ;
}


3. 常量指针指向常量

const int* const p 表示这个指针是常量指针,并且它指向的位置的值也是常量

int main(){

    int high_score{100};
    int low_score{75}l

    //第一个const是修饰指向的数据,第二个const是修饰指针。
    //表示不管是指针还是指向的数据,都是常量 
    const int *const score_ptr{&high_score};

    //既不允许修改指向,也不允许修改指向的值
    *score_ptr = 86 ; // 错误
    score_ptr = &low_score ; // 错误 
 
    return 0 ;
}


8 打卡作业

  1. 定义结构体stu, 包含学生的姓名(字符串)、学号(字符串)、成绩(vector)6个学科

  2. 定义一个vector来保存张三、李四、王五三个学生的信息 ,请使用学生的指针类型。。

    1. vector< stu* > stu_vector;
  3. 三个学生采用new的方式创建出来,保存在堆内存中。

  4. 定义initScore函数负责录入学生的信息。 姓名 , 学号 , 6个学科成绩

  5. 定义updateScore函数负责更新学生的沉寂。成绩小于60的,更新为60

  6. 定义printScore函数打印学生的信息: 姓名、学号、总分、平均分

  7. 使用分离式写法: stu.h , stu.cpp , main.cpp

    1. stu.h 包含 结构体stu的声明, 三个函数的声明
    2. stu.cpp 包含三个函数的具体实心。
    3. main.cpp 作为程序的入口
  8. 三个函数传递vector的时候,请使用指针类型传递。

    initScore(vector & score_vector)

    initScore(vector <stu*> *score_vector)

二、指针与函数

1. 参数传递指针

函数的参数,除了传递普通的变量,引用之外,还可以把指针当成参数来传递

#include <iostream>
using namespace std;

//函数原型
void double_data(int *int_ptr);

int main(){
    int value{10};
    
    //修改前,输出为10
    cout << value << endl;
    
    //在函数内部修改value的值
    double_data(value);
    
    //修改后,输出为20
    cout << value << endl;
    
}

void double_data(int *int_ptr){
    *int_ptr *=2;
}


在某些情况下,传递指针比其他方式的传递要合适得多,比如下面有一个函数负责交换传递进来的两个参数的值, 此时如果不使用指针或者引用,则无法实现该功能

#include <iostream>
using namespace std;

void swap(int *a , int *b);

int main (){
    
    int x{100},y{200};
    
    //交换前打印 x : 100 , y : 200
    cout << x <<" = " << y <<endl;
    
    swap(&x , &y)
    
    //交换前打印 x : 200 , y : 100
    cout << x <<" = " << y <<endl;
    
}

void swap(int *a , int *b){
    int temp = *a ;
    *a = *b ;
    *b = temp;
}


2. 函数返回指针

函数平常除了返回标准的数值之外,其实也可以返回指针。

#include <iostream>
using namespace std;

//返回两个参数中的最大者
int *calc_largest(int *ptr1 , int *ptr2)int main(){
    int a = 100int b = 200//使用指针指向最大数值
    int *largest_ptr = calc_largest(&a , &b);
    
    //输出:200
    cout << *largest_ptr << endl ;
    return 0 ;
}

int *calc_largest(int *ptr1 , int *ptr2){
	//解引用获取到数据后比较 : 
    if(*ptr1 > *ptr2){  
        return ptr1;
    }else{
        return ptr2;
    }
}


注意: 不要返回一个函数内部的一个局部变量指针 , 因为本地变量的声明周期应该只位于函数内部。一旦函数执行完毕则被释放。

#include <iostream>
using namespace std;

int *do_this(){
    int size = 10;
    return &size;
}

int main(){
    
    int *result = do_this();
    
    std::cout <<"result = " <<result << std::endl;
    
    return 0 ;
}


3. 二级指针

指向指针的指针,即可称之为二级指针。有点类似二维数组,数组里面装的是数组,即可称之为二维数组。

1. 二级指针介绍

在这里插入图片描述

2. 二级指针的应用

二级指针不如一级指针使用的那么频繁,通常出现的地方是作为函数参数传递。如果在函数的内部想要修改外部一级指针指向的数据值,那么则需要二级指针了。

如下示例所示, 如果传递的是一个一级指针,那么在外部的p依然没有被分配空间,传递进去的依然是一份值的拷贝而已 , 导致后面的指针赋值 为 42 也无法执行。

//函数原型
void createPointer(int** p);

int main(){
    
    //空指针
	int* p = nullptr;
    
    //在函数内部对该指针初始化
    createPointer(&p);
    
    //初始化完毕后,修改指向空间的数据
    *p = 42;
    
    //释放指针
    delete p ;
    
    return 0 ;
}

void createPointer(int** p){
   // 解引用,得到的是一级指针,其实就是得到了外面的 p 那么整段话连起来就是 int *P  = new int();
  *p = new int(); 
}


3. 练习

有一个vector指针, 用于存储3个学生张三、李四、王五, 该vector的指针创建工作交由createContainer 函数来创建,并且该函数没有返回值。通过函数参数的方式,来创建外部vector指针。

4. 函数指针

1. 基本使用

函数指针的意思是指向函数的指针 。 通常来说,指针是变量,有自己的类型,那么函数指针也有类型。只不过它的类型稍微不一样而已。函数指针的类型由函数的返回值、函数的参数列表决定。 要想声明一个函数指针,只需要使用指针来替换函数名即可。

  • 普通方式调用函数
#include <iostream>

//函数原型
int add(int a , int b); 

int main(){
    
    int result = add(3,4);
    std::cout << "result = " << result << std::endl;
    return 0 ;
}

int add(int a , int b){
    return a + b;
}


  • 使用函数指针方式调用函数
#include <iostream>
using namespace std;

//函数原型
int add(int a , int b); 

int main(){
    
    //前半段表示声明一个函数指针add_ptr  该函数指针指向的函数返回值是int,并且有两个int类型的参数。
    //指针的小括号不能省略。
    int (*add_ptr) (int,int) =add;
    
    //普通方式调用函数
    int result =add(3,4);
    cout << "result = " << result << endl;
    
    //使用函数指针方式调用add函数
    int result2 = add_ptr(3,4) ;
    cout << "result2 = " << result2 << endl;
    
    return 0 ;
}

int add(int a , int b){
    return a + b;
}


2. 函数指针作为参数

有时候,也可以把某个函数A通过参数的方式传递给另一个函数B,随后在函数B里面执行传递进来的函数A。函数虽然不能直接作为参数来进行传递,但是函数指针可以。实际上在传递的时候,传递的是指针而已。

比如下面的示例: 有一个计算的函数calc , 允许在第三个参数传递进来具体的计算函数。

#include<iostream>
using namespace std;

int add (int a , int b)int calc(int a , int b ,int (*fun)(int, int))int main() {
    //函数指针p,指向add函数
    //int(*p)(int ,int) = add;
    //cout << calc(3,5 , p) << endl;
    
    //函数名称可以直接使用,它实际上就是一个函数指针。
    cout << calc(3,5 , add) << endl;
    return 0 ;
}
int add (int a , int b){
    return a + b;
}

//计算的函数,最后的参数要求的是一个函数指针。
int calc(int a , int b ,int (*fun)(int, int)){
    return fun(a,b);
}


3. 函数指针的作用

如果一个通用的函数,需要使用到 另一个函数,但是这个函数并没有确定名称,是由其他组织或者个人开发的,那么这时候可以预留一个位置,做成函数指针 虚位以待。比如:现在有一个vector或者数组,需要交给其他个人或组织来遍历,但是这些组织或者个人的遍历手法不一样,那么这时候可以使用函数指针占位。

//函数原型
void listScore(vector<int> scores , void (*ps)(vector<int>));
void printScore1(vector<int> scores);
void printScore2(vector<int> scores);

int main() {
    vector<int> scores {50,60,70,80,90};
    listScore(scores , printScore1);
    listScore(scores , printScore2);
    return 0 ;
}

//接收函数指针的函数
void listScore(vector<int> scores , void (*ps)(vector<int>)){
    ps(scores);
}

//打印函数1
void printScore1(vector<int> scores){
    cout << "****采用基于范围for循环遍历******" << endl;
    for(int i : scores){
        cout << i << endl;
    }
}

//打印函数2
void printScore2(vector<int> scores){
    cout << "****采用for i循环遍历******" << endl;
    for (int i = 0; i < scores.size(); ++i) {
        cout <<scores[i] << endl;
    }
}



4. typedef使用

typedef 的作用是自己习惯的名字,来替已有的类型名称。 语法: typedef 已知类型名称 自定义名称

#include<vector>

using namespace std;

//使用myint 来替代 int。
typedef int myint ;

//使用vi 来替代 vector<int>
typedef vector<int> vi; 

int main(){
   
    int a = 3 ;
    
    //此时的myint  等价于 int
    myint b = 3 ;

    vector<int> scores1{60,70,80,90}
    
    //此时的vi 等价于 vector<int>
    vi scores2{60,70,80,90};

    return 0 ;
}



1. 未使用 typedef 简化

下面示例代码,没有使用typedef 简化函数指针

#include<vector>
using namespace std;

void listScores(vector<int> s , void(*pfs)(vector<int>)){
    pfs(s);
}

void pirntScores(vector<int> scores){
    //遍历打印
    for(int s: scores){
        cout << s<< endl;
    }
}

int main(){
    vector<int> scores {60,70,80,90};
	
    //把printScores函数传递给listScores , 函数名称单独使用,它实际上是一个函数指针
    listScores( scores ,pirntScores);

    return 0 ;
}



2. 使用 typedef 简化

下面示例代码,使用typedef 简化函数指针 , 实现的功能都是一样的,只是代码简化了些

#include<vector>
using namespace std;

// 声明一种类型,这种类型的名称叫做 pfs ,它是一种函数指针,该函数没有返回值,有两个分别是int的参数
typedef void(*pfs)(vector<int>);

//此处使用简化好的函数指针类型
void listScores(vector<int> s , pfs p){
    p(s);
}

void pirntScores(vector<int> scores){
    //遍历打印
    for(int s: scores){
        cout << s<< endl;
    }
}

int main(){
    vector<int> scores {60,70,80,90};

    //也可以选择使用变量来接收printScores
    pfs pfs1 = pirntScores;
    
     //把pfs1 传递给listScores ,实际上传递的是printScores
    listScores( scores ,pfs1);
    
    return 0 ;
}



5. 练习

设计一个计算器,具有增删改查函数,还有一个函数operator。该函数接收三个参数,分别是: a, b , 和最终执行的具体操作函数的指针。最终通过operator来调用对应的算术操作,举例如下:

operator( 3, 4, , add) ; // 执行加法运算

operator(3,4 , sub) ; //执行减法运算

operator(3,4 , mul); //执行乘法运算

operator(3,4,div) ; //除法运算。

其中第三个参数,是函数指针。

三、引用

1. 什么是引用

引用,顾名思义是某一个变量或对象的别名,对引用的操作与对其所绑定的变量或对象的操作完全等价。引用在使用时,有几个要注意的地方:

  1. &不是求地址运算符,而是起到标志作用
  2. 引用的类型和绑定的变量类型必须相同 (指针也一样)
  3. 声明引用的同时,必须对其进行初始化,否则报错。
  4. 引用相当于变量或者某个对象的别名,但是不能再将已有的引用名作为其他变量或者对象的名字或别名 (和指针不一样。)
  5. 引用不是定义一个新的变量或对象,因此内存不会为引用开辟新的空间存储这个引用
  6. 不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。
#include<iostream>
using namespace std;

int main(){
    //语法:类型 &引用名=目标变量名;
    int a = 3 ; 
    int &b = a ;
    
    //此时a也会变成33
    b = 33;
    
    
    
    vector<string> names {"张三""李四" ,"王五"};

    // 这仅仅是改变了str的值,并不会改变vector里面的元素
    for(auto str:names){
        str = "赵六"; 
    }

    //此处不会产生新的变量,所以修改的都是vector里面的元素
    for(auto &str : names){
        str = "赵六" ;
    }
	return 0 ;    
}


2. 左值和右值

C++的表达式要么是左值 lvalue,要么是右值 rvalue 这两个名词是从C语言继承过来的。左值可以出现在赋值语句的左侧和右侧,右值只能出现在右侧。最长见到左值和右值的地方,是在函数的参数以及报错的日志信息里面。

不能简单的以等号的左右来判断是否是左值还是右值

判断是否是左值,有一个简单的办法,就是看看能否取它的地址,能取地址的就是左值 。使用排除法,其他的即为右值。

左值可看作是 对象,右值可看作是 而有时候对象也可以是左值也可以右值,一句话概括:当一个对象成为右值时,使用的是它的值(内容) , 而成为左值时,使用的是它的身份(在内存中的位置)。

//只能把左值赋值给引用,不能把右值赋给引用
int square(int &n){
    return n * n ; 
}

int main(){
    int x = 1000;  // x是一个左值, 而 1000 是一个右值。
    x = 1000 + 20 ;  //x 是左值, 1000 + 20 是右值,

    int b = 10; // b 是一个左值, 10 是一个右值
    b = x ;   //b是一个左值, 而 x依然是一个左值。


    int num = 10 ;
    square(num) ; //正确

    square(5) //错误。因为5是右值 ,不能赋值引用。
	return 0;    
}


3. 左值引用

平常所说的引用,实际上指的就是左值引用 lvalue reference , 常用单个 & 来表示。 左值引用只能接收左值,不能接收右值。const 关键字会让左值引用变得不同,它可以接收右值

#include <iostream>
using namespace std;

//函数原型
int add(int &num1);
void print(vector<int> &scores);

int main(){
    
    int a = 3 ;
    int &b = a ; //ar是一个左引用,实际上可以看成是a的一个别名。

    // 这是不允许的。
    // 1. 从引用层面理解的话是: 引用接收的一个变量,给某个变量起别名
    // 2. 从左右值的层面理解是,这是一个左值引用,只能接收左值。 3 属于右值。
    int &c = 3 ;  //错误!
    
    
    int a = 3 ;
    add(a) ; //正确
    add(3) ; //错误! 参数要求的是一个左值引用,只能赋值左值 ,3 属于右值
    
    
    vector<int> scores{60,70,80,90};
    print(scores); //正确
    print({60,70,80,90}); //错误!
}


4. 右值引用

为了支持移动操作,在c++11版本,增加了右值引用。右值引用一般用于绑定到一个即将销毁的对象,所以右值引用又通常出现在移动构造函数中。

看完下面的例子,左值和右值基本就清楚了,左值具有持久的状态,有独立的内存空间,右值要么是字面常量,要么就是表达式求值过程中创建的临时对象

int main(){
   
    int i = 66;
    int &r = i ; //r 是一个左引用,绑定左值 i

    int &&rr = i ; //rr是一个右引用,绑定到左值i , 错误! 
    int &r2 = i*42 ; //  r2 是一个左引用, 而i*42是一个表达式,计算出来的结果是一个右值。 错误!

    const int &r3 = i*42; // 可以将const的引用,绑定到右值 正确 
    int &&rr2 = i*42 ; // 右引用,绑定右值 正确
    
	return 0 ;    
}


  • 示例
#include<vector>
using namespace std;

//函数原型
int add(int &&num1);
void print(vector<int> &&scores);

int main(){
    int a = 3 ;
    add(a) ; //错误!参数要求的是一个右值引用,只能赋值右值
    add(3) ; //正确! 
    
    
    vector<int> scores{60,70,80,90};
    
    //print接收的是一个右值,此处的scores是一个左值。
    print(scores); //错误!
    
    //{60,70,80,90} 属于右值。
    print({60,70,80,90}); //正确
    
    return 0 ;
}


下一篇C++学习课件(二)————面向对象

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 好的,首先,你需要明确你的学习目标。你需要了解C 语言的基本概念,如变量、控制结构、数据类型等,然后着手实践,用C 语言编写一些简单的程序,以加深你对基本概念的理解。你还可以阅读一些有关C 语言的书籍,以掌握更多的知识。最后,可以尝试解决一些C 语言编程问题,以检验你的能力。 ### 回答2: 制定一份学习计划可以帮助你更好地安排C语言学习的时间和内容,让学习更高效和有条理。以下是一个示例的C语言学习计划。 1. 初步了解C语言:首先要了解C语言的基本概念、语法规则和基本数据类型等,在这个阶段可以通过阅读相关教科书、参考资料和在线教程等进行学习。 2. 练习基本语法:掌握C语言的基本语法结构,例如变量定义、控制语句、循环语句和函数等。可以通过编写简单的程序来加强对这些语法知识的理解,并进行反复的练习和代码实现。 3. 深入学习C语言:学习C语言的高级特性,例如指针、结构体、位运算和文件操作等。这些内容对于提升编程能力和解决实际问题非常重要,可以通过阅读专业的C语言书籍和参加相关课程进行学习。 4. 做一些小项目:从简单到复杂,逐渐进行一些小型的C语言项目实践。这有助于巩固之前学到的知识,培养编程思维和解决问题的能力。 5. 参与开源项目或实习:通过参与开源项目或找一份与C语言相关的实习工作来提升自己的技能和实践经验。与其他开发者互动和参与实际项目能够让你更好地理解和应用C语言。 6. 持续学习和提升:C语言是一个广泛应用的编程语言,学习永无止境。可以通过参加培训班、参考专业书籍和持续学习新的C语言特性和技术,保持自己的学习动力和对C语言的深入理解。 以上是一个基本的C语言学习计划示例,你可以根据自己的实际情况和需求进行调整。记得要坚持学习,努力实践,相信你一定能够掌握好C语言这门编程语言。 ### 回答3: 当然,我很乐意帮助你制定一份C语言学习计划。以下是一个简单的参考计划,你可以根据自身情况进行调整和适应。 第一周: 1. 学习C语言的基本概念和历史背景,了解为什么选择学习C语言。 2. 阅读C语言的语法和基本语法规则。 3. 编写简单的C程序,包括Hello World程序和一些基本的输入输出操作。 第二周: 1. 系统学习C语言的数据类型,包括整数、浮点数、字符和字符串等。 2. 学习C语言的运算符和表达式,包括算术运算符、关系运算符和逻辑运算符等。 3. 编写一些涉及数据类型和运算符的小程序,加深对其理解。 第三周: 1. 掌握C语言的控制语句,包括条件语句和循环语句。 2. 学习函数的定义和调用,了解函数的参数传递和返回值。 3. 编写一些结合条件语句、循环语句和函数的程序,加强对这些知识的掌握。 第四周: 1. 学习C语言的数组和指针,了解它们的概念和使用方法。 2. 掌握C语言中的字符串处理函数和库函数。 3. 编写一些涉及数组和指针以及字符串处理函数的程序,提高自己在这方面的能力。 第五周: 1. 学习C语言中的结构体和共用体,了解它们的定义和用法。 2. 学习文件操作函数,如打开、读取和写入文件。 3. 编写一些结合结构体、共用体和文件操作的程序,巩固知识。 第六周: 1. 复习前面学到的知识点,解答可能遇到的问题。 2. 尝试解决一些实际问题,如编写一个简单的计算器程序。 3. 阅读一些C语言的经典程序和书籍,加深对C语言的理解。 以上只是一个简单的学习计划,你可以根据自己的情况和学习进度进行调整。记住,坚持练习和实践是学习C语言的关键。祝你学习顺利!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~简

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值