【C++基础】第1章:C++初探

1、写一个Hello World

在这里插入图片描述
在上图这个程序中,我们需要一个能具备输出Hello World功能的函数.
C++中的函数是啥?

函数

函数是一段能被反复调用的代码,可以接收输入,进行处理并(或)产生输出。它主要由返回类型、函数名、形参列表、函数体组成。

返回类型

表示了函数返回结果的类型,可以为void。如上图中int main()中的int。

函数名

用于函数调用。如上图中int main()中的main。

形参列表

表示函数接收的参数类型(可以为空,可以为void,可以无形参)。int main()中()里面表示main函数所包含的所有形参列表(函数里可以有形式参数,我们调用该函数时,是用实际的参数传进该函数,替换掉形参)

以下代码是有一个形参的情况。

#include <iostream>
//定义fun函数
void fun(const char* pInfo)//const char* pInfo是一个形参
{
 std::cout << pInfo << std::endl;//大括号里面是函数体
 return;//这么写也可以。表示从fun函数中退出,但没返回值。但return 0;就不行
}
int main()  //mian函数比较特殊。他是程序的入口,main函数体里面可以通过“函数名”(如fun)来搜索想要调用的函数
{
 fun("Hello World");//在mian函数里面调用fun函数,把实参"Hello World"赋给形参pInfo
 fun("Hello xzx");//一个函数是可以被反复调用的
}

以下代码是形参列表里面有多个形参的情况。在main函数里面调用fun函数,把实参"Hello World"赋给形参pInfo,把实参0赋给形参pValue(和JAVA中的方法很像)。

#include <iostream>
//定义fun函数
void fun(const char* pInfo, int pValue )  //const char* pInfo是一个形参,const char*指定了pInfo类型。int pValue 是另一个参数,参数之间用逗号划分
{
 std::cout << pInfo << std::endl;  //大括号里面是函数体
}
int main()  //mian函数比较特殊。他是程序的入口,main函数体里面可以通过“函数名”(如fun)来搜索想要调用的函数
{
 fun("Hello World",0);  //在main函数里面调用fun函数,把实参"Hello World"赋给形参pInfo,把0赋给pValue
 fun("Hello xzx",9);  //一个函数是可以被反复调用的
}

在这里插入图片描述

形参列表可以为空

#include <iostream>
//定义fun函数
void fun()
{
 std::cout << "xzx" << std::endl;//大括号里面是函数体
}
int main()//mian函数比较特殊。他是程序的入口,main函数里面可以通过“函数名”来搜索想要调用的函数{
 fun();//在mian函数里面调用fun函数
}

在这里插入图片描述
形参列表也可以写viod。但通常省略

#include <iostream>
//定义fun函数
void fun(void)
{
 std::cout << "xzx" << std::endl;//大括号里面是函数体
}
int main()//mian函数比较特殊。他是程序的入口,main函数里面可以通过“函数名”来搜索想要调用的函数{
 fun();//在mian函数里面调用fun函数
}

在这里插入图片描述
形参列表可以无形参变量

形参列表中有两个形参,但第二个形参 int 我们没有给他名称,在fun函数里面我们没办法获取这个形参所对应的实参值。但这样可以保证main函数调用fun函数时,接口还是fun(“xxx”, 0)的形式,接口不变。在mian函数里面调用fun函数,把实参"Hello World"赋给形参pInfo,但无法把实参0赋给fun函数中的另一个形参(因为它没有名称,在fun函数里面我们没办法获取这个形参所对应的实参值)。

#include <iostream>
//定义fun函数
void fun(const char* pInfo, int)//形参列表中有两个形参,但第二个形参我们没有给他名称,在fun函数里面我们没办法获取这个形参所对应的实参值。但这样可以保证main函数调用fun函数时,接口还是fun("xxx", 0)的形式,接口不变。
{
 std::cout << pInfo << std::endl;//大括号里面是函数体
 return;//这么写也可以。表示从fun函数中退出,但没返回值。但return 0;就不行
}
int main()//mian函数比较特殊。他是程序的入口,main函数里面可以通过“函数名”来搜索想要调用的函数
{
 fun("Hello World", 0);//在mian函数里面调用fun函数,把实参"Hello World"赋给形参pInfo
 fun("Hello xzx", 9);//一个函数是可以被反复调用的
}

函数体

包含了函数具体的执行逻辑。

main函数

main函数是特殊的函数,作为整个程序的入口。操作系统在调用c++程序时,最开始调用的函数就是main函数。

int main()
 fun("Hello World", 0);
 fun("Hello xzx", 9);
 return 0;  //通常使用0来表示正常返回,0返回给操作系统}

c++定义函数时不加void的话,都需在函数体最后写return,但main函数例外,可以不写,默认return 0。其次main函数的返回类型一定是int,表示程序的返回值。通常使用0来表示正常返回。

形参列表可以为空(main函数的形参列表要么为空,要么为:int, char []*)

int main(int argc, char* argv[])//表示两个形参
{
 fun("Hello World", 0);
 fun("Hello xzx", 9);//一个函数是可以被反复调用的 return 0;
}

形参名称可变,但类型不能变

int main(int a, char* r[]){  //形参名称可变,但类型不能变
 fun("Hello World", 0);
 fun("Hello xzx", 9);//一个函数是可以被反复调用的
 return 0;
}

形参名称没有也可以,形参类型在就行

int main(int, char* []){  //形参名称没有也可以,形参类型在就行
 fun("Hello World", 0);
 fun("Hello xzx", 9);//一个函数是可以被反复调用的
 return 0;
}

内建类型

(内建)类型可为一段存储空间赋予实际的意义。我们熟知的基本数据类型也属于内置类型,如int, byte等(byte占一个字节,int占4个字节)。
在这里插入图片描述
那么,我们如何来引用这些数据呢?以下面代码为例,我们通过pInfo,pValue来引用内存中所定义的某一块字节,从而获取数据。如 fun(“Hello World”,0) 中的fun传入 “Hello World”,0 之后,系统会找一段内存来存储"Hello World",再找另外一段内存来存储0,再使用pInfo,pValue来引用这段内存。

#include <iostream>
//定义fun函数
void fun(const char* pInfo, int pValue )//const char* pInfo是一个形参,const char*指定了pInfo类型。int pValue 是另一个参数,参数之间用逗号划分
{
 std::cout << pInfo << std::endl;//大括号里面是函数体
 return;//这么写也可以。表示从fun函数中退出,但没返回值。但return 0;就不行
}
int main()//mian函数比较特殊。他是程序的入口,main函数里面可以通过“函数名”来搜索想要调用的函数
{
 fun("Hello World",0);//在main函数里面调用fun函数,把实参"Hello World"赋给形参pInfo
 fun("Hello xzx",9);//一个函数是可以被反复调用的
}

语句

C++程序是一组数组,而每个数组又是一组语句。语句表明了需要执行的操作。

主要可划分为:表达式+分号(以分号作为结束)的语句、语句块、if/while等语句。

注释

会被编译器忽略的内容(用于编写说明或去除不使用的语句)。它有两种注释形式:/**/ 与//

2. 系统I/O

2.1. iostream

iostream是标准库所提供的IO接口,用于与用户交互。我们可以使用 #include < iostream > 这个头文件实现系统IO(头文件通过include来引用)。

2.1.1. #include “”和#include < >的区别

如果使用#include “”,系统会从当前目录开始寻找头文件。如:#include “myheader.h”,系统会在write helloworld.cpp所在的头文件目录下寻找myheader.h。
在这里插入图片描述
如果使用#include <>,系统会从操作系统的环境变量所指示的路径寻找头文件。

2.1.2. 输入流

cin;(用户键盘输入)

2.1.3. 输出流

cout / cerr / clog(通常输出到屏幕上)

输入流、输出流在程序中的简单使用如下:

#include <iostream>
int main()
{
 int x;
 std::cout << "How old are you:"; std::cin >> x;
 std::cout << "You are" << x;
}

在这里插入图片描述

2.1.4. 输出流的区别:

输出目标不同(cerr输出的是错误信息);

是否立即刷新缓冲区

cerr 和 clog的区别:cerr输出的是错误信息,我们需要尽快的看到这些信息,从而使我们能更加全面的对系统进行了解,故cerr会立即刷新缓存区。但clog是日志信息,采用的是不立即刷新缓存区的方式。

如果我们想立即看到我们打印的东西,但没看到打印输出时,可能是缓冲区没有被刷新。那应该咋办?

一方面,可以使用cerr,cerr只要往外输出了,就刷新缓存区了。

另一方面,若使用cout或clog时也想刷新缓存区(使用cout时,是在程序结束时才刷新缓存区),则可通过std::fush ; 或std::endl来刷新缓存区。

如:

#include <iostream>
int main()
{
 std::cout << "output from cout" << std::flush;}

在执行到flush时,就会刷新缓存区。这样就能保证程序执行完这条语句之后, “output from cout” 这个信息就已经能被终端用户所看到。
在这里插入图片描述
而如果使用std::endl,不仅会刷新缓存区,还会换行:
在这里插入图片描述
但一般只有在必要时才刷新缓存区。

2.2. 名字空间

在C/C++中,变量、函数和类都是大量存在的,这些变量、函数、类的名称都将作用于全局作用域中,可能会导致很多的冲突。所以,C++中引入namespace关键字,目的是对标识符的名称进行本地化,以避免命名冲突或名字污染

2.2.1. 如何设置namespace?

namespace NameSpace1 //NameSpace1 就是我们定义的一个名字空间的名称

2.2.2. 如何在函数中调用名字空间中的函数?

如:main函数中如何调用名字空间中的fun函数?如果在main函数中,想调用NameSpace1 中的fun函数,像以下代码这么写,系统会认为是在全局名字空间中调用fun函数,但实际上全局名字空间中并没有fun函数。而且也不知道想调用的是NameSpace1 中的fun还是NameSpace2中的fun。

#include <iostream>
namespace NameSpace1 
{ 
 void fun( )
 { }
}
namespace NameSpace2
{
 void fun()
 { }
}
int main()
{
 fun( );//这么写的话,系统会认为是在全局名字空间中调用fun函数,但实际上全局名字空间中并没有fun函数
}

其实,访问名字空间中元素的 3 种方式:域解析符 ::using 语句名字空间别名

域解析符 ::

使用域解析符 ::,在main函数在调用NameSpace1中的fun函数。

#include <iostream>
namespace NameSpace1 
{ 
 void fun( )
 { }
}
namespace NameSpace2
{
 void fun()
 { }
}
int main()
{
 NameSpace1::fun( );//使用域解析符 ::,在main函数在调用NameSpace1中的fun函数。
}

使用域解析符 ::,在main函数在调用NameSpace2中的fun函数。

#include <iostream>
namespace NameSpace1 
{ 
 void fun( )
 { }
}
namespace NameSpace2
{
 void fun()
 { }
}
int main()
{
 NameSpace2::fun( );
}

using 语句

使用using namespace NameSpace1,在main函数内部,既能看到全局名字空间里所有函数,又能看到NameSpace1里面所有的函数。此时再去调用fun函数,首先会在全局名字空间里查找fun函数,如果全局名字空间里没有fun,再去NameSpace1查找。

#include <iostream>
namespace NameSpace1 
{ 
 void fun( )
 { }
}
namespace NameSpace2
{
 void fun()
 { }
}
int main()
{
 using namespace NameSpace1;//在main函数内部,既能看到全局名字空间里所有函数,又能看到NameSpace1里面所有的函数
 fun();//这时,首先会在全局名字空间里查找fun函数,如果全局名字空间里没有fun,再去NameSpace1查找。
}

名字空间别名

为NameSpace1这个名字空间赋予一个简单的别名ns1,然后再使用类似域解析符的方式调用fun函数。

#include <iostream>
namespace NameSpace1 
{ 
 void fun( )
 { }
}
namespace NameSpace2
{
 void fun()
 { }
}
int main()
{
 namespace ns1 = NameSpace1;//为NameSpace1这个名字空间赋予一个简单的别名ns1
 ns1::fun();//别名空间调用fun函数
}

2.2.3. std 名字空间

std 是c++标准库里的名字空间。

std::cout << "output from cout" << std::flush;

如,cout函数,flush函数都包含在std这个名字空间里。访问名字空间中的变量和访问名字空间中的函数方法是同样的操作。

2.2.4. 名字空间与名称改编( name mangling )

从上一章我们知道,整个程序编译完之后会编译出.o文件,再链接成可执行文件。

假设有这么个程序:

#include <iostream>
namespace NameSpace1 
{ 
 void fun( )
 { }
 int x;
}
namespace NameSpace2
{
 void fun()
 { }
}
int main()
{
}

我们来看一下编译完是什么样?
在这里插入图片描述
其中,HelloWorld* 是可执行程序(链接后的结果),main.cpp.o是编译后的结果。我们使用nm命令罗列main.cpp.o所有外部链接(链接:比如说,我要在一个目标文件里想要调用fun1,而第二个目标文件里有fun1的定义,我们就要把这两个目标文件链接起来):
在这里插入图片描述
T main函数是一个外部链接,B _ZN10NameSpace11xE是整型变量x的链接名称。T _ZN10NameSpace13funEv是NameSpace1中fun的链接名称,T _ZN10NameSpace23funEv是NameSpace2中fun的链接名称。

我们在源代码中的NameSpace1和NameSpace2中定义了fun函数和x变量,但在外部链接中没有找到fun和x,为什么呢?

因为,NameSpace1和NameSpace2都有fun,这是两个不同的fun定义,都需要对外提供链接,即提供两个不同的链接。故不能把两个不同的链接名字都叫fun,如果都叫fun,就没办法区别这两个不同的链接了,故c++内部会引入mangling机制,会对fun的链接名称自行改编。

我们可把链接名字变回去:
在这里插入图片描述

2.3. C / C++ 系统IO比较

printf: 使用直观,但容易出错

cout: 不容易出错,但书写冗长

C++ 20格式化库:新的解决方案(把一段内容解析成字符串,但很多编译器未支持)

#include <iostream>
#include <cstdio>  //引用#include <cstdio>头文件,再去使用printf(c语言的操作)
int main( )
{
 int x = 9;
 std::cout << "I have " << x << " pens\n";//c++的输出,如果改成float x = 9,std也会自动匹配输出相应的数据类型
printf("I have %d pens\n", x);//c语言的输出,%d是一个整数占位符,后面会用x替换掉占位符
}

在这里插入图片描述

3. 猜数字与控制流

上面讲的程序都是顺序执行结构的,下面来说一下分支执行结构的。

3.1. if 语句

if 语句用于分支选择。

条件部分用于判断是否执行。

语句部分指要执行的操作。
在这里插入图片描述
假定有个数是42,用户在键盘输入数字来猜。如果输入的是42,则返回:你猜对了。猜错了,则返回:你猜错了。

#include <iostream>
int main( )
{
 int x = 42;
 std::cout << "Please input a number: \n";
 int y = 0;//用户输入的信息保存在y中 std::cin >> y;
 if (y == 42)
 {
  std::cout << "You are right!\n";
 }
 else
 {
  std::cout << "You are wrong!\n";
 }
}

在这里插入图片描述
其中,== 与 = 操作的区别:

= 操作:用于赋值,将数值保存在变量所对应的内存中

== 操作:用于判断两个值是否相等

注:可以将常量放在 == 左边以防止误用(但我不喜欢)

如果不用==,而用=:if( )括号里面是布尔值,if(42)等价于if(true),最后结果只会输出You are right!,只有if((0)才是if(fasle)。

#include <iostream>
int main( )
{
 int x = 42;
 std::cout << "Please input a number: \n";
 int y = 0;//用户输入的信息保存在y中
 std::cin >> y;
 if (y = 42)//if()括号里面是布尔值,if(42)等价于if(true),最后结果只会输出You are right!,if((0)才是if(fasle)
 {
 std::cout << "You are right!\n";
 }
 else
 {
 std::cout << "You are wrong!\n";
 }
}

在这里插入图片描述
if(负数)也会true

#include <iostream>
int main( )
{
 int x = 42;
 std::cout << "Please input a number: \n";
 int y = 0;//用户输入的信息保存在y中
 std::cin >> y;
 if (y = -100)//if()括号里面是布尔值,if(42)等价于if(true),最后结果只会输出You are right!,只有if((0)才是if(fasle),if(负数)也是true
 {
 std::cout << "You are right!\n";
 }
 else
 {
 std::cout << "You are wrong!\n";
 }
}

在这里插入图片描述

3.2. while 语句

while 语句用于循环执行。

条件部分:用于判断是否执行

语句部分:要执行的操作
在这里插入图片描述
我们把猜数字程序的功能换一下:如果用户输入错误了,可继续修改猜测。(用while语句)

#include <iostream>
int main( )
{
 int x = 42;
 int y = 0;//用户输入的信息保存在y中
 while (x != y)
 {
 std::cout << "Please input a number: \n";
 std::cin >> y;
 }

在这里插入图片描述

c++能干什么?

我们上面讨论的,c++可以引入函数,可以封装一些具体的方法逻辑,我们可以在函数内部进行顺序执行、分支执行、循环执行等处理。

但除此之外,C++和c语言还能通过结构体来进行操作。上面讨论的都是关于操作的工作。而结构体更多关注的是数据。而面向对象也是基于C语言的结构体演化派生而来的。

4. 结构体与自定义数据类型

结构体即将相关的数据放置在一起。这个结构体就是JAVA中的 class,里面可以是封装了某些数据(类似JAVA中的属性),也可以是封装了某些功能/函数(类似JAVA中的方法/成员函数),在需要这些功能的时候,直接调用这些封装好相关功能的结构体即可。

但需注意:

结构体是一个由程序员定义的数据类型,可以容纳许多不同的数据值。在过去,面向对象编程的应用尚未普及之前,程序员通常使用这些从逻辑上连接在一起的数据组合到一个单元中。一旦结构体类型被声明并且其数据成员被标识,即可创建该类型的多个变量,就像可以为同一个类创建多个对象一样。

虽然今天结构体较少使用,但知道它们是什么,以及如何使用它们仍然很重要,这并不仅仅是因为可以在较老的程序中遇到它们,还因为在某些情况下,类的实例(类的对象)无法使用这时必须使用结构体

声明结构体的方式和声明类的方式大致相同,其区别如下:

♥ 使用关键字 struct 而不是关键字 class。

♥ 尽管结构体可以包含成员函数但它们很少这样做。所以,通常情况下结构体声明只会声明成员变量

♥ 结构体声明通常不包括 public 或 private 的访问修饰符。(和类的权限不同)

♥ 类成员默认情况是私有的,而结构体的成员则默认为 public。程序员通常希望它们保持公开,只需使用默认值即可。

4.1. 为什么需要结构体?

内建数据类型int x,float y 太过于简单。如果我想写一个程序去表示二维平面上的点(表示坐标),写成以下这样,是比较麻烦的:
在这里插入图片描述
如果使用结构体,在应付大型数据时,更好一些。Point p中的p是一个Point结构体(或者说p是一个Point结构体变量)。

#include <iostream>
struct Point//引入了一个结构体Point,里面包含坐标
{
 int x;
 int y;
};
void fun(Point p)
{}
int main()
{
 Point p;//Point是p的类型,类似于int x中int是x的类型。
 p.x;//通过p.x,p.y去访问结构体里面的元素
 p.y;
}

4.2. 如何调用结构体里的数据(属性)或函数(方法)?

#include <iostream>
struct Point//引入了一个结构体Point,里面包含坐标
{
 int x;
 int y;
};
void fun(Point p)
{}
int main()
{
 Point p;//Point是p的类型,类似于int x中int是x的类型。
 p.x;//通过p.x,p.y去访问结构体里面的元素
 p.y;
}

我们可以通过点操作符( . )访问内部元素。如上,我们如果想在main函数中调用Point结构体的数据,类似JAVA,我们先在main函数里造对象p(通过“方法 对象名”去造对象),即Point p(这在c++中被称为结构体的变量),造完对象以后,再通过“对象.属性”(即上面所说的通过点操作符( . ))访问结构体内部元素,即通过p.x,p.y去访问访问结构体Point里面的元素(类似JAVA,类里面的属性)。

4.3. 结构体的变量可以作为函数的输入参数

如void fun(Point p),把结构体Point p用作fun函数的形参,或者说是结构体变量Point p作为函数的参数(形参)。

与JAVA的联系:和JAVA中,使用对象作为fun函数的形参类似。

#include <iostream>
struct Point//引入了一个结构体Point,里面包含坐标
{
 int x;
 int y;
};
void fun(Point p)//Point p可以作为函数的输入参数
{
 p.x;//通过p.x,p.y去访问结构体里面的元素
 p.y;
}
int main()
{
 Point p;//Point是p的类型,类似于int x中int是x的类型。
}

4.4. 结构体变量可以作为函数的返回类型

如Point fun(Point p),把结构体Point p用作fun函数的返回类型,最后return结构体。

#include <iostream>
struct Point//引入了一个结构体Point,里面包含坐标
{
 int x;
 int y;
};
Point fun(Point p)//Point结构体也可以作为返回类型,把它类似成int
{
 p.x = p.x + 1;//通过p.x,p.y去访问结构体里面的元素
 p.y;
 return p;
}
int main()
{
 Point p;//Point是p的类型,类似于int x中int是x的类型。
 p.x = 4;//通过p.x,p.y去访问结构体里面的元素
 p.y = 7;
 std::cout << p.x << ' ' << p.y << '\n';
}

在这里插入图片描述
(上图更正,放错图了,应把4改成5)

4.5. 结构体内可以引入成员函数

以上的结构体只包含了数据,实际上可以引入成员函数,更好地表示函数与数据的相关性。如下图,定义了一个IncX成员函数。

调用结构体中成员函数的方式,和调用结构体中成员变量的方式并无二致。都是类似JAVA中的“对象.成员函数(类中的方法)”去调用结构体中的函数功能。

#include <iostream>
struct Point//引入了一个结构体Point,里面包含坐标
{
 int x;
 int y;
 void IncX()//Point结构体内引入成员函数IncX,此时的结构体Point像类
 {
 x = x + 1;
 }
};
int main()
{
 Point p;//Point是p的类型,类似于int x中int是x的类型。
 p.x = 4;//通过p.x,p.y去访问结构体里面的元素
 p.y = 7;
 p.IncX();//对象.成员函数(方法)去调用结构体中的函数功能
 std::cout << p.x << ' ' << p.y << '\n';
}

在这里插入图片描述
参考:C++结构体完全攻略(超详细)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

cashapxxx

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

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

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

打赏作者

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

抵扣说明:

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

余额充值