笔记,随时更新
C++注释:
/* */
//
数据类型
使用编程语言进行编程时,需要用到各种变量来存储各种信息。变量保留的是它所存储的值的内存位置。这意味着,当您创建一个变量时,就会在内存中保留一些空间。操作系统会根据变量的数据类型,来分配内存和决定在保留内存中存储什么。
C++ 变量作用域
作用域是程序的一个区域,一般来说有三个地方可以定义变量:
- 在函数或一个代码块内部声明的变量,称为局部变量。
- 在函数参数的定义中声明的变量,称为形式参数。
- 在所有函数外部声明的变量,称为全局变量。
整数常量
整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。
整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。
定义常量:
在 C++ 中,有两种简单的定义常量的方式:
使用 #define 预处理器。
使用 const 关键字。
如图:
请注意,把常量定义为大写字母形式,是一个很好的编程实践。
C++ 存储类
存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。下面列出 C++ 程序中可用的存储类:
auto
register
static
extern
mutable
thread_local (C++11)
从 C++ 11 开始,auto 关键字不再是 C++ 存储类说明符,且 register 关键字被弃用。
- auto 存储类
自 C++ 11 以来,auto 关键字用于两种情况:声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符。
- register 存储类
register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 ‘&’ 运算符(因为它没有内存位置)。寄存器只用于需要快速访问的变量,比如计数器。还应注意的是,定义 ‘register’ 并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。
{
register int miles;
}
- static 存储类
static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。 - extern 存储类
extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 ‘extern’ 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。
当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。
extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候,如下所示:
第一个文件:main.cpp
#include <iostream>
int count ;
extern void write_extern();
int main()
{
count = 5;
write_extern();
}
第二个文件:support.cpp
#include <iostream>
extern int count;
void write_extern(void)
{
std::cout << "Count is " << count << std::endl;
}
&取地址
*取地址对应的数据
#include <iostream>
using namespace std;
#define LENGTH 10
int main(int argc, const char * argv[]) {
// insert code here...
int a = 6;
int *ptr;
ptr = &a;
cout << *ptr << endl;
}
重点:
cout 只会对char数组的指针,打印 它存的 地址所指向的内容,其他类型的指针,不会智能判断,而是只输出指针 存的地址,而不是指向的内容
循环
while(condition)
{
statement(s);
}
for ( init; condition; increment )
{
statement(s);
}
for 语句允许简单的范围迭代:(和python中的for类似)
int my_array[5] = {1, 2, 3, 4, 5};
// 每个数组元素乘于 2
for (int &x : my_array)
{
x *= 2;
cout << x << endl;
}
// auto 类型也是 C++11 新标准中的,用来自动获取变量的类型
for (auto &x : my_array) {
x *= 2;
cout << x << endl;
}
do
{
statement(s);
}while( condition );
无限循环
for(; ;){}
判断
if(boolean_expression)
{
// 如果布尔表达式为真将执行的语句
}
if(boolean_expression)
{
// 如果布尔表达式为真将执行的语句
}
else
{
// 如果布尔表达式为假将执行的语句
}
if(boolean_expression 1)
{
// 当布尔表达式 1 为真时执行
}
else if( boolean_expression 2)
{
// 当布尔表达式 2 为真时执行
}
else if( boolean_expression 3)
{
// 当布尔表达式 3 为真时执行
}
else
{
// 当上面条件都不为真时执行
}
switch(expression){
case constant-expression :
statement(s);
break; // 可选的
case constant-expression :
statement(s);
break; // 可选的
// 您可以有任意数量的 case 语句
default : // 可选的
statement(s);
}
函数
在 C++ 中,函数由一个函数头和一个函数主体组成。下面列出一个函数的所有组成部分:
返回类型:一个函数可以返回一个值。return_type 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 void。
函数名称:这是函数的实际名称。函数名和参数列表一起构成了函数签名。
参数:参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。
函数主体:函数主体包含一组定义函数执行任务的语句。
链接:http://www.runoob.com/cplusplus/cpp-functions.html
参数可以带默认值,就像python
一些计算函数:
产生随机数:
数组
C++ 支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。
所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。
数组名可以作为指向这个数组第一个元素的指针:
cout << myarray1 << endl;
以上代码会输出这个数组第一个元素的地址。0x7fff5fbff6b0
如果使用间接取值运算符。
cout << *myarray1 << endl;
就会输出这个地址的值
或者
cout << *(myarray1 + 3) << endl;
会取出 myarray1[4]的值
C++ 传数组给一个函数,数组类型自动转换为指针类型,因而传的实际是地址。
如果一个函数需要接收一个数组作为参数,有三种声明方式:
void myFunction(int *param){}
void myFunction(int param[10]){}
void myFunction(int param[]){}
这三种形式都说明这里的参数需要一个指针,因此传值的时候传数组名就可以了。
上边说的是把数组当做参数传入函数。接下来,
如果一个函数要返回数组怎么办。
直接看代码:
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
// 要生成和返回随机数的函数
int * getRandom( )
{
static int r[10];
// 设置种子
srand( (unsigned)time( NULL ) );
for (int i = 0; i < 10; ++i)
{
r[i] = rand();
cout << r[i] << endl;
}
return r;
}
// 要调用上面定义函数的主函数
int main ()
{
// 一个指向整数的指针
int *p;
p = getRandom();
for ( int i = 0; i < 10; i++ )
{
cout << "*(p + " << i << ") : ";
cout << *(p + i) << endl;
}
return 0;
}
可以看出两点: 1.不能返回整个数组,仍然是返回数组名,也就是指向第一个元素的指针。
2.函数中创建的数组需要static。个人理解是函数内的变量是局部变量,会随着函数运行的结束而消失。但是函数返回的不是引用,而是指针。如果这个局部变量消失了,返回的值就无法再找到这个地址。所以需要static
字符串
字符串实际上是使用 null 字符 ‘\0’ 终止的一维字符数组。
两种c语言方式:
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
char greeting[] = "Hello";
一些函数:
以下C++中提供了String类,更直观一些:
string str1 = "Hello";
string str2 = "world";
string str3;
str3 = str1;
cout << str3 << endl;
cout << str1 + str2 << endl;
输出:
Hello
Helloworld
#include <iostream>
#include <string>
using namespace std;
int main()
{
//定义一个string类对象
string http = "www.runoob.com";
//打印字符串长度
cout<<http.length()<<endl;
//拼接
http.append("/C++");
cout<<http<<endl; //打印结果为:www.runoob.com/C++
//删除
int pos = http.find("/C++"); //查找"C++"在字符串中的位置
cout<<pos<<endl;
http.replace(pos, 4, ""); //从位置pos开始,之后的4个字符替换为空,即删除
cout<<http<<endl;
//找子串runoob
int first = http.find_first_of("."); //从头开始寻找字符'.'的位置
int last = http.find_last_of("."); //从尾开始寻找字符'.'的位置
cout<<http.substr(first+1, last-first-1)<<endl; //提取"runoob"子串并打印
return 0;
}
指针
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
type *var-name;
这里带星号,是为了定义这是一个指针。而使用这个指针的时候,这个指针是var-name。
而*var-name代表这个指针指向的位置的值。
例子:
#include <iostream>
using namespace std;
int main ()
{
int var = 20; // 实际变量的声明
int *ip; // 指针变量的声明
ip = &var; // 在指针变量中存储 var 的地址
cout << "Value of var variable: ";
cout << var << endl;
// 输出在指针变量中存储的地址
cout << "Address stored in ip variable: ";
cout << ip << endl;
// 访问指针中地址的值
cout << "Value of *ip variable: ";
cout << *ip << endl;
return 0;
}
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。
int *ptr = NULL;
可以这样写:
目前的指针与数组最后一个数据的位置进行比较,如果小于,就自增,如下代码:
while ( ptr <= &var[MAX - 1] )
{
cout << "Address of var[" << i << "] = ";
cout << ptr << endl;
cout << "Value of var[" << i << "] = ";
cout << *ptr << endl;
// 指向上一个位置
ptr++;
i++;
}
指针数组: 此时我们需要一个数组,里边存储的都是指针:
int *ptr[MAX]
在这里,把 ptr 声明为一个数组,由 MAX 个整数指针组成。因此,ptr 中的每个元素,都是一个指向 int 值的指针。
解释:如果是int ptr[MAX],则认为是ptr里存储了MAX个int类型的值
而此时int类型变成了int *,则认为ptr中存储了MAX个指向int型数据的指针。
C++ 指向指针的指针(多级间接寻址)
指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。
一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。例如,下面声明了一个指向 int 类型指针的指针:
int **var;
int var = 10;
int *ptr = NULL;
int **pptr = NULL;
ptr = &var;
pptr = &ptr;
cout << **pptr << endl;
要记得:如果函数返回指针,指针一定不能指向局部变量的地址,要使用static。
引用
int i = 17;
int& r = i;
double& s = d;
说白了就是用type&这种语法给之前已有的变量再取一个名字
也就是这个变量有两个名字,一个是原本的变量名,一个是引用。
引用和变量名在使用的时候用法一致。
引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。
既然引用就是变量的一个别名,那么为什么需要引用呢?
引用通常用于函数参数列表和函数返回值
·
C++之所以增加引用类型, 主要是把它作为函数参数,以扩充函数传递数据的功能。
C++ 函数传参:
(1)将变量名作为实参和形参。这时传给形参的是变量的值,传递是单向的。如果在执行函数期间形参的值发生变化,并不传回给实参。因为在调用函数时,形参和实参不是同一个存储单元。
(2) 传递变量的指针。形参是指针变量,实参是一个变量的地址,调用函数时,形参(指针变量)指向实参变量单元。这种通过形参指针可以改变实参的值。
(3) C++提供了 传递变量的引用。形参是引用变量,和实参是一个变量,调用函数时,形参(引用变量)指向实参变量单元。这种通过形参引用可以改变实参的值。
void swap(int& x, int& y)
{
int temp;
temp = x; /* 保存地址 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 x 赋值给 y */
return;
}
可以看出,使用引用,在函数内交换x, y的值,是真正改变了内存中x, y的值,与指针有同样效果。
通过使用引用来替代指针,会使 C++ 程序更容易阅读和维护。
.
C++ 函数可以返回一个引用,方式与返回一个指针类似。
当函数返回一个引用时,则返回一个指向返回值的隐式指针。这样,函数就可以放在赋值语句的左边。
关于这个问题,在编码过程中我只被报了一个警告,并没有报错。
时间、日期
C++ 标准库没有提供所谓的日期类型。C++ 继承了 C 语言用于日期和时间操作的结构和函数。为了使用日期和时间相关的函数和结构,需要在 C++ 程序中引用 <ctime>
头文件。
tm 结构在 C/C++ 中处理日期和时间相关的操作时,显得尤为重要
tm结构具体包括什么见上边的链接。
输入输出
这里是C++ 编程中最基本和最常见的 I/O 操作。更多功能后续介绍。
C++ 的 I/O 发生在流中,流是字节序列。
标准输入流(cin)
标准错误流(cerr):
预定义的对象 cerr 是 iostream 类的一个实例。cerr 对象附属到标准错误设备,通常也是显示屏,但是 cerr 对象是非缓冲的,且每个流插入到 cerr 都会立即输出。
标准日志流(clog):
预定义的对象 clog 是 iostream 类的一个实例。clog 对象附属到标准错误设备,通常也是显示屏,但是 clog 对象是缓冲的。这意味着每个流插入到 clog 都会先存储在缓冲在,直到缓冲填满或者缓冲区刷新时才会输出。
加粗部分为clog和cerr的区别。
良好的编程实践告诉我们,使用 cerr 流来显示错误消息,而其他的日志消息则使用 clog 流来输出。
结构
为了定义结构,您必须使用 struct 语句。struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下:下面是声明一个结构体类型 Books,变量为 book:
struct Book{
char bookname[50];
char authername[50];
char subject[50];
int bookid;
}book;
访问结构成员:
struct Book{
char bookname[50];
char authername[50];
char subject[50];
int bookid;
};
Book book1;
strcpy(book1.bookname, "go");
strcpy(book1.authername, "xxx");
strcpy(book1.subject, "life");
book1.bookid = 102;
cout << "name: " << book1.bookname << endl;
cout << "auther: " << book1.authername << endl;
cout << "subject: " << book1.subject << endl;
cout << "id: " << book1.bookid << endl;
struct Book *book2;
book2 = &book1;
cout << book2->bookname;
如上,也可以定义指向结构的指针,让他指向已有结构实例的地址,然后使用->符号来取得结构中的值。
结构可以作为函数参数:
void printBook( struct Books book )
{
cout << "书标题 : " << book.title <<endl;
cout << "书作者 : " << book.author <<endl;
cout << "书类目 : " << book.subject <<endl;
cout << "书 ID : " << book.book_id <<endl;
}
重点内容:
char * bookname;
bookname = "name";
或者
char * bookname;
bookname = (char *)malloc(50);
strcpy(bookname,"name");
或者char bookname[50];
strcpy(bookname,"name");
以上可以看出,如果先定义一个指针,如果没有初始化指针的指向,就可以将它任意指向我想要的,例如“name”
如果定义了[50],说明已经指向了一块长度为50的内存,或者说使用malloc申请了一块内存。这时这个指针已经初始化了指向,就是not assignable的了,不能再给它赋值,只能将值复制到这块内存里边去。
char bookname[50];
bookname = “go”;
这种是不对的。
typedef struct{
int age;
string name;
}Person;
另一种定义结构的方式。