文章目录
1. 数组
1.1 一维数组
1.1.1 固定长度数组
指定数组长度的表达式应该为字面量,而非变量(C++98/03标准)。C++11及以后的标准,引入可变数组,但以变量作为数组长度并非标准规定。
// 声明时就初始化
int arr[5] = {1, 2, 3, 4, 5};
int arr[5] = {1, 2, 3}; // arr = {1, 2, 3, 0, 0}
// 先声明,后初始化
int arr[5];
for( int i=0; i<5; i++){
arr[i] = i;
}
// 用变量指定数组长度之一:依赖于特定编译器支持,非标准
const int arrSize = 10;
int arr[arrSize] = {1, 2, 3, 4, 5};
// 用变量指定数组长度之二:定义编译时常量来
constexpr int arrSize = 5;
int arr[arrSize];
1.1.2 动态数组
/* 使用malloc */
int arrSize = 10;
int *arr = reinterpret_cast<int*>(malloc(sizeof(int) * arrSize));
for( int i=0; i<arrSize; i++){
arr[i] = i;
}
// 销毁
free(arr);
arr = nullptr;
/* 使用new关键字 */
int arrSize = 10;
int *arr = new int[arrSize];
for( int i=0; i<arrSize; i++){
arr[i] = i;
}
delete[] arr; // delete[] 专门用于释放动态数组内存
1.2 二维数组
1.2.1 固定形式
在定义多维数组时,除了最左边的维度可以省略,其他维度都必须明确指定大小。
// 指定行列长度
int rowNum = 5;
int colNum = 5;
int arr[rowNum][colNum];
for(int i=0; i<=rowNum; i++){
for(int j=0; j<=colNum; j++){
arr[i][j] = i+j;
}
}
// 仅指定列长度,必须声明时初始化
int arr[][5] = {
{1, 2, 3, 4, 5},
{1, 2, 3 },
{1, 2,}
}; // 得到3行5列的矩阵
1.2.2 动态形式
/* malloc */
int rowNum = 5;
int colNum = 5;
int **arr = reinterpret_cast<int**>(malloc(sizeof(int *) * rowNum));
for(int i=0; i<rowSize; i++){
arr[i] = reinterpret_cast<int*>(malloc(sizeof(int) * colNum));
for(int j=0; j<colNums; j++){
arr[i][j] = i+j;
}
}
// 释放内存
for(int i=0; i<rowNum; i++){
free(arr[i]);
}
free(arr);
arr = nullptr;
/* new */
int rowNum = 5;
int colNum = 5;
int **arr = new int*[rowNum];
for(int i=0; i<rowSize; i++){
arr[i] = new int[colNum];
for(int j=0; j<colNum; j++){
arr[i][j] = i+j;
}
}
for(int i=0; i<rowNum; i++){
delete[] arr[i];
}
delete[] arr;
arr = nullptr;
1.3 数组作为函数参数
1.3.1 函数参数之一维数组
像int[9], int[]这样的函数参数声明,在执行中将会退化为int *
而传入int[n]类型的实参,将隐式类型转换为int *
// 数组形式
void printArr1(int arr[], int arrSize){
for(int i=0; i<arrSize; i++){
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
// 指针形式
void printArr2(int *arr, int arrSize){
for(int i=0; i<arrSize; i++){
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
// 调用
const int arrSize = 10;
int arr[arrSize] = {1, 2, 3};
printArr1(arr, arrSize); // 实参类型int [10], 函数参数为int[]类型,退化为int *类型,隐式类型转换:int[10] -> int *
printArr2(arr, arrSize); // 实参类型int [10], 函数参数类型为int *,隐式类型转换:int[10] -> int *
1.3.2 函数参数之二维数组
二维数组作为函数参数时也会退化为指针。具体来说,int [m][n] 和 int [][n] 都会退化为指向长度为 n 的一维数组的指针,即 int (*)[n]。
// 只在C语言C99标准以后允许这种参数形式:int [][cols],cols为同一参数列表中的参数
void printArr1_2(const int rows, const int cols, int arr[][cols]){
for(int i=0; i<rows; i++){
for(int j=0; j<cols; j++){
std::cout << arr[i][j] << " ";
}
std::cout << std::endl;
}
}
// C++中作为函数参数的二维数组:二维指针形式
void printMatrix1(int rows, int cols, int **arr){
for(int i=0; i<rows; i++){
for(int j=0; j<cols; j++){
std::cout << arr[i][j] << " ";
}
std::cout << std::endl;
}
}
// C++中作为函数参数的二维数组:int[m][n]形式,其中m,n为字面量
void printMatrix2(int rows, int cols, int arr[5][5]){ // int [5][5] -> int (*)[5]
for(int i=0; i<rows; i++){
for(int j=0; j<cols; j++){
std::cout << arr[i][j] << " ";
}
std::cout << std::endl;
}
}
// C++中作为函数参数的二维数组:int[m][n]形式,其中m,n为字面量
void printMatrix3(int rows, int cols, int arr[][5]){ // int [][5] -> int (*)[5]
for(int i=0; i<rows; i++){
for(int j=0; j<cols; j++){
std::cout << arr[i][j] << " ";
}
std::cout << std::endl;
}
}
int main(){
// 调用
const int rows = 3;
const int cols = 5;
int mat1[][5] = {
{1, 2, 3},
{1, 2},
{1}
};
int mat2[3][5] = {
{1, 2, 3},
{1, 2},
{1}
};
int **mat3 = new int*[5];
for(int i=0; i<5; i++){
mat3[i] = new int[5];
}
for(int i=0; i<5; i++){
for(int j=0; j<5; j++){
mat3[i][j] = i+j;
}
}
printMatrix1(3, 5, mat1); // error: int[3][5] -/-> int **
printMatrix1(3, 5, mat2); // error: int[3][5] -/-> int **
printMatrix1(3, 5, mat3); // int ** --> int **
printMatrix2(3, 5, mat1); // int [3][5] --> int (*)[5]
printMatrix2(3, 5, mat2); // int [3][5] --> int (*)[5]
printMatrix2(3, 5, mat3); // error: int ** -/-> int (*)[5]
printMatrix3(3, 5, mat1); // int [3][5] --> int (*)[5]
printMatrix3(3, 5, mat2); // int [3][5] --> int (*)[5]
printMatrix3(3, 5, mat3); // error: int ** -/-> int (*)[5]
}
1.4 数组作为返回值
返回值类型不能是int[]或者int [][4]这种类型,函数要返回数组只能通过
int* careateArr(int arrSize){
int *arr = new int[arrSize];
for(int i = 0; i < arrSize; i++){
arr[i] = i;
}
return arr;
}
// 调用
const int arrSize = 10;
int *arr = createArr(arrSize);
1.5 补充
1.5.1 reinterpret_cast和static_cast的区别
reinterpret_cast:不做类型检查,不安全。一般用于转换指针的类型,指针和整数类型之间互换。
static_cast:做类型价差,安全。一般用于转换数值类型,基类和派生类转换,显式地做隐式类型转换。
// reinterpret_cast: 转换指针类型
void *p1 = nullptr;
int *p2 = reinterpret_cast<int *>(p);
// static_cast:转换数值类型
int a = 10;
float af = static_cast<float>(a);
// static_cast:基类和派生类之间转换(向上转安全(派生类指针转基类指针),向下转不安全(基类指针转派生类指针))
class Base {};
class Derived : public Base {};
Derived derived;
Base* basePtr = static_cast<Base*>(&derived); // 向上转换,安全
1.5.2 多维数组一维化
i n d = i 1 ⋅ ∏ j > 1 n j + i 2 ⋅ ∏ j > 2 n j + . . . + i q − 1 ⋅ n q + i q , j ∈ N ; i k = 0 , 1 , 2 , . . . , n k − 1 , 其中 k = 1 , 2 , . . . , q ind = i_1\cdot\prod_{j>1}n_j+i_2\cdot\prod_{j>2}n_j+...+i_{q-1}\cdot n_q+i_q, \space j\in N; i_k=0,1,2,...,n_{k-1},其中k=1,2,...,q ind=i1⋅j>1∏nj+i2⋅j>2∏nj+...+iq−1⋅nq+iq, j∈N;ik=0,1,2,...,nk−1,其中k=1,2,...,q
2 string字符串
2.1 转换
2.1.1 C字符串和string字符串互换
char s1[32] = "Hello, World!";
std::string s2(s1); // C字符串转string
const char *s3 = s2.c_str(); // string转C字符串
2.1.2 数值类型转和string字符串互换
仅限数值类型
#include<string>
// 数值类型转换为string类型
int a = 10;
float b = 1.11;
string aStr = to_string(a);
string bStr = to_string(b);
// string类型转换为数值类型:stringstream
#include <sstream>
string s = "1.1";
float a = 0.0;
stringstream ss(s);
ss >> a;
string s = "1.1";
float a = 0.0;
stringstream ss(s);
ss >> a;
2.1.3 sstream用法
读取sentence,将word放在string数组里
#include <sstream>
vector<string> words;
string word;
string sentence = "hello world in C++"
stringstream ss(sentence);
while(ss >> word){
words.push_back();
}
stringstream对象绑定到新的string
#include <sstream>
string s1 = "hello";
string s2 = "world";
stringstream ss(s1);
string rStr;
ss >> rStr;
// ss绑定到新的string对象s2上,绑定后最好重置流状态再使用
ss.str(s2)
ss.clear();
ss >> rStr;
2.2 子串
2.2.1 查找
// 查找函数原型
size_t find (constchar* s, size_t pos = 0) const;
size_t find (charc, size_t pos = 0) const;
size_t rfind (constchar* s, size_t pos = npos) const;
size_t rfind (charc, size_t pos = npos) const;
size_t find_first_of (const char* s, size_t pos = 0) const; // 从给定位置,在母串中查找第一个位于子串的字符
size_t find_first_not_of (const char* s, size_t pos = 0) const; // 从给定位置,在母串中查找第一个不在子串中的字符
size_t find_last_of(const char* s, size_t pos = npos) const; // 从母串中查找最后一个位于子串的字符的位置
size_tfind_last_not_of (const char* s, size_t pos = npos) const; // 从母串中查找最后一个没有位于子串中的字符的位置
// case1:第一次出现的位置
std::string s = "abctext123abc";
std::string substr = "text";
int pos = s.find(substr); // 3
// case2:最后一次出现的位置(也可使用正则表达式进行复杂查找)
std::string s = "abctext123textabc";
std::string substr = "text";
int pos = s.rfind(substr); // 10
2.2.2 替换
std::string s = "abctext123abc";
std::string substr = "text";
std::string substitute = "TEXT";
int substrPos = s.find(substr);
s.replace(pos, substr.length(), substitute);
// 字符串替换
a.replace(6, 10, "C++11"); // "hello, world!!!" -> "hello, C++11!!!"
a.replace(0, 5, 3, 'a'); // "hello, C++11" -> "aaaa, C++11"
a.replace(begin_it, end_it, "xxx") // 将区间内的子串替换为"xxx"
2.2.3 获取
std::string s = "abctext123abc";
std::string substr = s.substr(3,4); // substr = "text"
2.2.4 合并
// +
std::string s1 = "Hello";
std::string s2 = "World";
std::string s3 = s1 + ", " + s2; // Hello, World
// append
std::string s1 = "Hello";
std::string s2 = "World";
s1.append(s2); // HelloWorld
str1.append(s2, 3, 3); // Hellold
// stringstream
#include<sstream>
std::string s1 = "Hello";
std::string s2 = "World";
std::stringstream ss;
ss << s1 << s2;
std::string s3 = ss.str(); // HelloWorld
// sprintf
const char* str1 = "Hello";
const char* str2 = "World";
char result[100];
std::snprintf(result, sizeof(result), "%s%s", str1, str2); // result = "HelloWorld"
以分隔符分割字符串
// 1. 使用istream流
char split = '.';
string token;
istringstream iss(str);
while( getline( iss, token, split ) ){
cout << token << endl;
}
// 2.使用strtok,strtok_r()/strtok_s()
char s[] = "hello,world,this,is,a,test";
// strtok()方式,非线程安全
const char split[] = ";, ?"
char *token = NULL;
token = strtok(str, ",");
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, ",");
}
// strtok_r()/strtok_s(),线程安全
char *ptr = NULL, *token = NULL;
token = strtok_r( s, split, &ptr );
while( token != NULL){
cout << token << endl;
token = strotok_r( NULL, split, &ptr );
}
// 3.find() + substr()
void splitStr(const string& rawStr, const char split, vector<string>& res){
if (rawStr == "")
return;
string token;
string tmpStr = rawStr + split;
size_t pos = tmpStr.find(split);
while ( pos != -1 ){
token = tmpStr.substr(0, pos);
res.push_back(token);
tmpStr.replace( 0, pos + 1, "");
pos = tmpStr.find(split);
}
}
其他操作
// 遍历字符:下标法,迭代器(正向、反向)
// 插入单个字符
string s;
s.push_back('a');
s.insert( s.begin(), 'b' );
// 删除字符
a.erase(0, 7); // "hello, world!!!" -> "world"
a.erase(iter); // 删除迭代器iter指向的字符
a.erase(begin_ir, end_it); // 删除去区间内的字符
// 大小写转换:tolower(), toupper()
for( size_t i = 0; i < a.size() ; ++i )
a[i] = tolower(a[i]); // toupper(a[i])
// 大小写转换:STL transform
#include <algorithm>
transform( a.begin(), a.end(), a.begin(), ::tolower )
// 排序
sort(a.begin(), a.end());
2.3 时间字符串
2.3.1 获取时间戳
// C++11以后:方法一:先转换时间按点对象为指定精度的时间点对象,再计算该单位下的时间戳
#include <chrono>
auto now = chrono::system_clock::now(); // 获取当前时间点对象
auto now_ms = chrono::time_point_cast<chrono::milliseconds>(now); // 将时间点对象转换为毫秒精度的时间点对象
auto duration = now_ms.time_since_epoch(); // 获取以毫秒计数的时间间隔对象(从基准时间开始计)
long long timestamp = duration.count(); // 获取毫秒级时间戳
// C++11以后:方法二,先得到时间间隔对象,再将时间间隔对象转换为指定时间单位,计算该单位下的时间戳
#include <chrono>
auto now = chrono::system_clock::now(); // 获取当前时间点对象
auto d = now.time_since_epoch(); // 获取时间间隔对象
auto timestamp = chrono::duration_cast<chrono::seconds>(d).count(); // 获取秒级时间戳
// C风格
#include<ctime>
time_t timestamp = std::time(nullptr); // time_t是long的别名,秒级
2.3.2 时间戳和时日期字符串相互转换
时间戳转时间日期字符串
#include <sstream>
#include <iomanip>
#include <ctime>
std::string timestampToDateString(long long timestamp) {
// 将时间戳转换为 std::time_t 类型
std::time_t time = static_cast<std::time_t>(timestamp);
// 使用 std::localtime 将时间转换为本地时间结构
std::tm* localTime = std::localtime(&time);
// 使用 std::ostringstream 格式化时间
std::ostringstream oss;
oss << std::put_time(localTime, "%Y-%m-%d %H:%M:%S"); // from <iomanip>
return oss.str();
}
时间日期字符串转时间戳
long long stringToTimestamp(const std::string& dateString) {
std::tm tm = {};
std::istringstream ss(dateString);
// 按照指定格式解析输入的时间日期字符串
ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
if (ss.fail()) {
std::cerr << "Failed to parse the date string." << std::endl;
return -1;
}
// 将 tm 结构体转换为以秒为单位的时间戳
return std::mktime(&tm);
}
// 调用stringToTimestamp
std::string dateString = "2024-10-01 12:00:00";
long long timestamp = stringToTimestamp(dateString);
if (timestamp != -1) {
std::cout << "Timestamp (seconds): " << timestamp << std::endl;
}
3 文件操作
3.1 使用文件流对象写
3.1.1 基本用法
/*
文件打开模式
- ios::out 覆写模式,以此模式打开已存在文件,文件会被截顶(truncated),则输入数据将覆盖文件原有内容
- ios::app 追加模式,以此模式打开已存在文件则写入数据将追加到原有数据之后
*/
#include <fstream>
using namespace std;
int main() {
// 创建一个输出文件流对象时打开文件
ofstream outFile1("output.txt", ios::out); // 如果没有改文件,则会创建一个
// 先创建一个输入文件流对象,再打开文件
ofstream outFile2;
outFile2.open("output.txt", ios::app);
string content1 = "Hello, World!";
string content2 = "C++ Programming";
outFile1 << content1 << endl;
outFile2 << content2 << endl;
outFile1.close();
outFile2.close();
return 0;
}
3.1.2 指定位置读取:seekg,即seek get
/* output.txt
Hello, World!
C++ Programming
*/
#include <iostream>
#include <fstream>
using namespace std;
int main() {
// 创建输入文件流对象时,打开文件
string s;
ifstream inFile1("output.txt", ios::in);
inFile1.seekg(-5, ios::end); // 将读指针从文件末尾往前移动5个字节。最后5个字符为"ming\n"
inFile1 >> s; // 碰到第一个空字符("\n")终止
cout << s << endl; // "ming"
inFile1.seekg(5, ios::beg); // 将读指针从文件起始位置往后移动5个字节
inFile1 >> s; // 从","开始直到遇到第一个空字符(" ")
cout << s << endl; // ","
int curPos = inFile1.tellg();// 获取输入文件对象的当前指针位置
cout << curPos << endl; // 6
inFile1.seekg(1, ios::cur); // 跳过","后的空字符" ",文件指针在第7个字节处(第7个字节尾,第8个字节首)
inFile1 >> s; // 遇到的第一个空字符为("\n")
cout << s << endl; // "World!"
inFile1.close();
return 0;
}
3.2 使用文件流对象读
3.2.1 基本用法
#include <iostream>
#include <fstream>
using namespace std;
int main() {
// 创建输入文件流对象时,打开文件
ifstream inFile1("output.txt", ios::in);
string s;
inFile1 >> s;
while(getline(inFile1, s)){
cout << s << endl;
}
inFile1.close();
return 0;
}
3.2.2 指定位置写入:seekp,即seek put。
#include <iostream>
#include <fstream>
using namespace std;
int main() {
// 创建输入文件流对象时,打开文件
string s;
ofstream outFile("output.txt", ios::out);
outFile << "Hello, world!";
outFile.seekp(7, ios::beg);
outFile.put('W');
outFile.seekp(-8, ios::end);
outFile.put(';');
outFile.seekp(1, ios::cur); // "Hello; World!"
outFile << "C++!"; // "Hello; C++!d"
outFile.close();
return 0;
}
3.3 顺序文件
指文件中的数据按照顺序依次存储,在访问时也只能按照顺序依次读取或写入,即从文件开头开始,逐个数据项地进行操作,不能直接跳转到文件中间的某个位置进行读写。
3.3.1 创建顺序文件
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
int main(){
ofstream outClientFile( "clients.txt", ios::out );
if ( !outClientFile ){
cerr << "File could not be opened" << endl;
// 将EXIT_SUCCESS传递给exit(),表明程序正常退出,将EXIT_FAILURE传递给exit(),表明程序异常退出。EXIT_SUCCESS和EXIT_FAILURE定义在<cstdlib>中
exit( EXIT_FAILURE );
}
cout << "Enter the account, name, and balance." << endl
<< "Enter end-of-file to end input.\n? ";
int account;
string name;
double balance;
while ( cin >> account >> name >> balance ){
outClientFile << account << ' ' << name << ' ' << balance << endl;
cout << "? ";
}
}
3.3.2 读取顺序文件
#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstdlib>
using namespace std;
void outputLine( int, const string&, double );
int main(){
ifstream inClientFile( "clients.txt", ios::in );
if( !inClientFile ){
cerr << "File could not be opened" << endl;
exit(EXIT_FAILURE);
}
int account;
string name;
double balance;
cout << left << setw( 10 ) << "Account" << setw( 13 )
<< "Name" << "Balance" << endl << fixed << showpoint;
while ( inClientFile >> account >> name >> balance )
outputLine( account, name, balance );
}
void outputLine( int account, const string &name, double balance ){
cout << left << setw( 10 ) << account << setw( 13 ) << name
<< setw( 7 ) << setprecision( 2 ) << right << balance << endl;
}
3.4 随机存取文件
允许程序直接访问文件中的任意位置,而不必按照数据的存储顺序依次访问。可以通过指定文件指针的位置,快速定位到文件中的特定位置进行读写操作。
3.4.1 创建随机存取文件
头文件:ClientData类的声明
#ifndef CLIENTDATA_H
#define CLIENTDATA_H
#include <string>
class ClientData{
public:
ClientData( int = 0, const std::string & = "",
const std::string& = "", double = 0.0 );
void setAccountNumber( int );
int getAccountNumber() const;
void setLastName( const std::string & );
std::string getLastName() const;
void setFirstName( const std::string & );
std::string getFirstName() const;
void setBalance( double );
double getBalance() const;
private:
int accountNumber;
char lastName[ 15 ];
char firstName[ 10 ];
double balance;
};
#endif
源文件:ClientData类的定义
#include <string>
#include "ClientData.h"
using namespace std;
ClientData::ClientData( int accountNumberValue, const string &lastNameValue,
const string &firstNameValue, double balanceValue )
: accountNumber( accountNumberValue ), balance( balanceValue )
{
setLastName( lastNameValue );
setFirstName( firstNameValue );
}
int ClientData::getAccountNumber() const{
return accountNumber;
}
void ClientData::setAccountNumber( int accountNumberValue ){
accountNumber = accountNumberValue;
}
string ClientData::getFirstName() const {
return firstName;
}
void ClientData::setFirstName( const string & firstNameString ) {
int length = firstNameString.size();
length = ( length < 10 ? length : 9 );
firstNameString.copy( firstName, length );
firstName[length] = '\0';
}
string ClientData::getLastName() const {
return lastName;
}
void ClientData::setLastName( const string & lastNameString ) {
int length = lastNameString.size();
length = ( length < 10 ? length : 9 );
lastNameString.copy( lastName, length );
lastName[length] = '\0';
}
double ClientData::getBalance() const {
return balance;
}
void ClientData::setBalance( double balanceValue ) {
balance = balanceValue;
}
源文件:创建随机存取文件
#include <iostream>
#include <fstream>
#include <cstdlib>
#include "../MyClass/ClientData/ClientData.h"
using namespace std;
int main(){
ofstream outCredit( "credit.dat", ios::out | ios::binary );
if( !outCredit ){
cerr << "File could not be opened." << endl;
exit( EXIT_FAILURE );
}
ClientData blankClient;
for( int i = 0; i < 100; ++i ){
outCredit.write( reinterpret_cast<const char * >( &blankClient ), sizeof(ClientData) );
}
}
3.4.2 读取随机存取文件
#include <iostream>
#include <fstream>
#include <cstdlib>
#include "../MyClass/ClientData/ClientData.h"
using namespace std;
int main(){
int accountNumber;
string lastName;
string firstName;
double balance;
fstream outCredit( "credit.dat", ios::out | ios::in | ios::binary );
if( !outCredit ){
cerr << "File could not be opened." << endl;
exit( EXIT_FAILURE );
}
cout << "Enter acoount number (1 to 100, 0 to end input)\n?";
ClientData client;
cin >> accountNumber;
while( accountNumber > 0 && accountNumber <= 100 )
{
cout << "Enter lastname, firstname, balance\n?";
cin >> lastName;
cin >> firstName;
cin >> balance;
client.setAccountNumber(accountNumber);
client.setLastName(lastName);
client.setFirstName(firstName);
client.setBalance(balance);
outCredit.seekp((client.getAccountNumber() - 1) * sizeof( ClientData ));
outCredit.write(reinterpret_cast<const char *>(&client), sizeof(ClientData));
cout << "Enter account number\n?";
cin >> accountNumber;
}
}
4. 参考
Deitel, H. M., & Deitel, P. J. (2020). C++ How to Program (11th ed.). Pearson.

被折叠的 条评论
为什么被折叠?



