数组就是一组名为数组元素或简称元素的内存位置,各个内存位置可以存储相同数据类型的数据项,我们可以用相同的变量名引用所有的内存位置。
存储各个元素所需的内存量取决于元素的类型,数组的所有元素都存储在连续的内存区域中。
声明数组type arrayName[size];
数组声明时,在紧随数组名的方括号内指出数组元素的数量。
初始化数组type arrayName[size]{ value 1,value 2,...,value n};
指定的初始值不能比数组元素多,但可以比数组的元素少。
如果少,列表中的初始值被分配从第一个元素(对应索引值0)开始的连续元素。没有得到初始值的数组元素初始化为0。
旧语法
type arrayName[] = { value 1,value 2,...,value n};
基于范围的for循环
使用for循环可以迭代数组中的所有元素。
for(type item:array) //可以使用auto关键字
{
//user code
}
C++库提供了——countof()函数,可以获取数组中的元素个数。
int a[]{1,2,3,4,5};
std::cout << _countof(a) << std::endl;
for (int item:a)
{
std::cout << item << std::endl;
}
多维数组
声明
type arrayName[size1][size2];
初始化
type arrayName[size1][size2]= {
{ value 1,value 2,...,value n},
{ value 1,value 2,...,value n}
};
int data1[2][4] { { 1,2,3,4},{ 5,6,7,8} };
//可以省略某些初始值
int data2[2][4] { { 1,2,3}, { 5,6} };
//用0初始化数组
int data2[2][4] { };
字符串
字符数组:char类型的数组。
字符串是附加有特殊字符(字符串结尾标志)的字符序列。这个特殊字符是‘ ’,有时候称为空字符,占1个字节,8位全为0。
非Unicode字符串中的每个字符占用一个字节,算上最后的空字符,字符串需要的字节数要比包含的字符串多1个字节。
#include "pch.h" #includeint main() { char a[] {"123456789"}; std::cout << a << std::endl << sizeof(a) << std::endl; wchar_t b[]{ L"ABC" }; std::wcout << b << std::endl << sizeof(b) << std::endl; }
前缀L表示字符串字面值是一个宽字符串(Unicode字符串)。
wcout标准输出流(宽)。
输入输入流成员函数 | Microsoft Docs
cin.get()和cin.geline(),从键盘输入的字符串序列,并将其以字符串的形式(以‘ ‘字符串终止)存入字符数组中。
函数的 get 变体指定缓冲区地址和要读取的最大字符数。
#include "pch.h" #includeint main() { char a[20] {}; std::cin.get(a,20); std::cout << a << std::endl << sizeof(a) << std::endl; }
输入:123456789
输出:123456789
输出: 20
getline成员函数类似于函数get。 这两个函数均允许用于指定输入终止字符的第三个参数。
#include "pch.h" #includeint main() { char a[20] {}; std::cin.getline(a,20,'5'); std::cout << a << std::endl << sizeof(a) << std::endl; }
输入:123456789
输出:1234
输出: 20
前缀L表示字符串字面值是一个宽字符串(Unicode字符串)。
R表示原字符串字面量的开头,字符串用"(和)"界定。
#include "pch.h" #includeint main() { char a[] { "123 n" }; std::cout << a << std::endl << sizeof(a) << std::endl; char b[]{ R"(123 n)" }; std::cout << b << std::endl << sizeof(b) << std::endl; char c[]{ R"*+(123 n)*+" }; std::cout << c << std::endl << sizeof(c) << std::endl; }
输出:
123
6
123 n
8
123 n
8
#include "pch.h" #includeint main() { char a[] { "123" }; for (auto item:a) { std::cout << item << std::endl; } }
输出:
1
2
3
间接数据访问 指针
指针变量存储特定类型的另一个变量的地址。指针变量有名称,还有类型,以指出其内容引用的是什么类型的变量。
声明指针在名称前面用*号表示指针变量。使用以字母p开始的变量名表示指针是通用惯例。
int* pb; int *pc;取值运算符
&,一元运算符,用于获取变量的地址,也称为引用运算符。
使用指针间接寻址运算符* 和 指针变量一起使用,访问指针指向的内容。
#include "pch.h" #includeint main() { int b {123456}; int* pb; pb = &b; std::cout << "b: " << b << std::endl; std::cout << "&b: " << &b << std::endl; std::cout << "pb: " << pb << std::endl; std::cout << "&pb: " << &pb << std::endl; std::cout << "*pb: " << *pb << std::endl; }
输出:
b: 123456
&b: 000000DBCF9BFB84
pb: 000000DBCF9BFB84
&pb: 000000DBCF9BFBA8
*pb: 123456
指针字面值nullptr不指向任何对象。
int b {123456};
int* pb{& b};
int* pb{ nullptr };
int* pb{ 0 }; //旧方法
int* pb{ NULL }; //旧方法
nullptr可以隐式转换成任何指针类型,但不能隐式转换为除bool类型以外的其他任何整数类型。
int* pb {nullptr};
if(!pb)
//user code
指向char类型的指针
const char* 类型的指针可以用字符串字面值初始化。
const char* pb{"123"};
该字面值的地址将是第一个字符的地址。
可以在非const的指针中存储字符串字面值的地址,但是C++11标准移除了这个功能,VC++仍然允许。
指针数组
sizeof操作符
sizeof操作符产生size_t类型的整数值,给出其操作数占用的字节数量,size_t是由标准库定义的类型。
_countof()可以得到数组元素的个数。
#include "pch.h" #includeint main() { int b[] {123,456,789}; std::cout << "sizeof b: " << sizeof b << std::endl; std::cout << "_countof b: " << _countof(b) << std::endl; }
sizeof b: 12
_countof b: 3
const的指针中存储const数组的地址,这将阻止修改用作初始化器的字面值,此时const指针的地址可以修改。
#include "pch.h" #includeint main() { const int a { 1 }; const int b { 2 }; const int* ptr{ &a }; ptr = { &b }; }
const type* const指针将阻止修改字面值和指针地址。
#include "pch.h" #includeint main() { const int a { 1 }; const int b { 2 }; const int* const ptr{ &a }; ptr = { &b }; //报错 }
| 指针 | 对象(的值) | 指针指向 |
| 指向常量对象的指针 | 不能修改 | 可以修改 |
| 指向某个对象的常量指针 | 可以修改 | 不能修改 |
| 指向常量对象的常量指针 | 不能修改 | 不能修改 |
数组可以直接给指针赋值。
或者用数组中某个元素的地址给指针赋值。
#include "pch.h" #includeint main() { int a[5]; int* b { nullptr}; b = a; std::cout << b << std::endl; b = &a[0]; std::cout << b << std::endl; }
指针可以加、减运算,也可以比较指针值。
#include "pch.h" #includeint main() { int data[5]{ 2,4,6,8,10 }; int* pdata{ nullptr }; pdata = &data[2]; std::cout << *(pdata + 1) << std::endl; pdata = &data[2]; std::cout << *(pdata - 1) << std::endl; }
指针与多维数组
#include "pch.h" #includeint main() { int data[2][3]{ {2,4,6},{1,2,3} }; int* pdata{ nullptr }; pdata = data[0]; //将1维数组的地址赋予指针 std::cout << *pdata << std::endl; pdata = &data[0][0]; std::cout << *pdata << std::endl; int (*pdata2)[3];//指针类型是int*[3],圆括号是必须得,否则声明的是一个指针数组。该指针只能用来存储规定维度的数组的地址。 pdata2 = data; std::cout << *pdata2 << std::endl; }
多维数组的指针形式
可以使用数组名的指针形式来引用数组的元素:
- 使用带2个索引值得数组名。
- 使用指针形式的数组名。
#include "pch.h" #includeint main() { int data[2][3]{ {2,4,6},{1,2,3} }; int* pdata{ nullptr }; pdata = data[0]; //将1维数组的地址赋予指针 std::cout << data[1][1] << std::endl; std::cout << *(*(data+1)+1) << std::endl; std::cout << *(data[1] + 1) << std::endl; }
动态分配内存
new操作符:动态分配内存。
delete操作符:释放new分配的内存。
#include "pch.h" #includeint main() { int* pdata = new int{ 123 }; std::cout << *pdata << std::endl; delete pdata; std::cout << *pdata << std::endl; //引发异常 }
释放指针指向的内存时,还应把该指针的值设置为nullptr,否则就会出现悬空指针,可以通过这种悬空指针访问已释放的内存,这会导致意外。
动态分配内存时,尤其是给数组或复杂的对象动态分配内存时,最好使用智能指针,不需要时,会自动释放内存。
为数组动态分配内存
#include "pch.h" #includeint main() { int* pdata{ new int[]{1,2,3,4,5,6,7,8,9} }; std::cout << *pdata << std::endl; delete [] pdata; //std::cout << *pdata << std::endl; //引发异常 pdata = nullptr; }
delete后的方括号指出要删除的是一个数组。还应该将指针设置为nullptr。
多维数组的动态分配内存
#include "pch.h" #include使用引用int main() { int(*pdata)[3]{ new int[][3]{{1,2,3},{4,5,6}} }; auto pdata2 { new int[][3]{{1,2,3},{4,5,6}} }; std::cout << **pdata << std::endl; std::cout << **pdata2 << std::endl; delete [] pdata; //std::cout << *pdata << std::endl; //引发异常 pdata = nullptr; }
引用是可用作其他对象的别名的一个名称。
引用分类:lvalue和rvalve。
lvalue是另一个变量的别名。
rvalve引用也可以用作变量的别名,但它与lvalue引用的区别在于,它也能引用rvalve,这实质上是一个暂存的临时值。
声明初始化lvalue
类型名称后面,变量名称前面的&表示声明一个lvalue引用。
#include "pch.h" #includeint main() { int a{ 1 }; int& lvalue{ a }; //声明变量的引用 const int& lvalue2 {2}; //声明变量的引用 std::cout << lvalue << std::endl; std::cout << lvalue2 << std::endl; }
基于范围的for循环中使用引用
#include "pch.h" #includeint main() { int a[]{ 1,2,3 }; for(int item:a) std::cout << item << std::endl; for (int& item : a) { item = item + 3; std::cout << item << std::endl; } for (const int& item : a) //不修改,提高性能用const { std::cout << item << std::endl; } }
创建rvalve引用
C++表达式,要么是lvalve,要么是rvalve。
在类型名后用两个&来指定一个rvalve引用。
#include "pch.h" #include字符串库函数int main() { int && a{2*3 }; //rvalve,仅演示用,不是使用rvalve的方式 std::cout << a; }
string标准头文件定义了string和wstring类。
一般来说,安全函数以_s结尾。
#include "pch.h"
#include
int main() {
CString aCString = CString(_T("A string"));
_tprintf(_T("%s"), (LPCTSTR)aCString);
}
通过使用当前区域设置或指定区域设置获取字符串的长度
strnlen、strnlen_s、wcsnlen、wcsnlen_s、_mbsnlen、_mbsnlen_l、_mbstrnlen、_mbstrnlen_l | Microsoft Docshttps://docs.microsoft.com/zh-cn/cpp/c-runtime-library/reference/strnlen-strnlen-s?view=msvc-170
追加字符串
strcat_s、wcscat_s、_mbscat_s、_mbscat_s_l | Microsoft Docs详细了解:strcat_s、wcscat_s、_mbscat_s、_mbscat_s_lhttps://docs.microsoft.com/zh-cn/cpp/c-runtime-library/reference/strcat-s-wcscat-s-mbscat-s?view=msvc-170复制字符串
strcpy_s、wcscpy_s、_mbscpy_s、_mbscpy_s_l | Microsoft Docs了解详细信息: strcpy_s、wcscpy_s、_mbscpy_s、_mbscpy_s_lhttps://docs.microsoft.com/zh-cn/cpp/c-runtime-library/reference/strcpy-s-wcscpy-s-mbscpy-s?view=msvc-170比较字符串
strcmp、wcscmp、_mbscmp、_mbscmp_l | Microsoft Docs了解:strcmp、wcscmp、_mbscmp、_mbscmp_lhttps://docs.microsoft.com/zh-cn/cpp/c-runtime-library/reference/strcmp-wcscmp-mbscmp?view=msvc-170返回不属于某个字符集的字符串中第一个字符的索引
strspn、wcsspn、_mbsspn、_mbsspn_l | Microsoft Docs了解详细信息: strspn、wcsspn、_mbsspn、_mbsspn_lhttps://docs.microsoft.com/zh-cn/cpp/c-runtime-library/reference/strspn-wcsspn-mbsspn-mbsspn-l?view=msvc-170



