从C++里的一大堆字符串说起

写数据库管理系统时遇到了不少关于C++字符串的问题,要命的是C++的字符串种类实在是太多了,今天试着搜集一些资料总结一下。

char foo[bar] 和 char* foo

char foo[bar]这种形式被称为“C风格字符串”,它是从C语言中流传下来的。它实际上完全就是char型的数组,只是你可以用这样的方式来给它赋值,看上去比较像“字符串”:

1
2
3
//这两行都定义了相同的字符串"Hello"
char foo1[] = "Hello"; //末尾有隐藏的'\0'
char foo2[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

char* foo是声明了一个字符指针,指向单个字符,可以将其当作以'\0'为末尾的字符串的首字符地址。如果要把字符串赋给它,需要先申请内存空间,再使用memcpy()strcpy()。当然,也可以不分配空间,直接用十分危险的指针赋值

1
2
3
4
char* foo1 = (char*)calloc(6, sizeof(char));
strcpy(foo1, "Hello");

char* foo2 = "Hello";

CString

CStringMFC的类,使用它需要包含afx.h

什么是MFC?

微软基础类库(英语:Microsoft Foundation Classes,简称MFC)是一个微软公司提供的类库(class libraries),以C++类的形式封装了Windows API,并且包含一个(也是微软产品的唯一一个)应用程序框架,以减少应用程序开发人员的工作量。其中包含的类包含大量Windows句柄封装类和很多Windows的内建控件和组件的封装类。

总之,作为Visual C++的一部分,这是一个上古遗留之物。不愧是十年前的课设……

CString是MFC中最常用的字符串类。其数据成员只有一个,那就是字符串在内存中的起始地址。它还有许多方法,如多种构造函数、用于连接和比较的运算符重载、用于格式化的Format,以及各种用于对字符串进行替换、查找、删除的方法。这个类将C++中极为复杂、包含大量内存操作的字符串操作简化了不少。

string

string之前一直弄不太明白。这明明是每个语言都应该有的基本东西,但是鉴于C和C++之间乱七八糟的关系以及错综复杂的各种标准,就连这种“基本”也轮廓模糊到让人难以提起兴趣探索。

需要知道的是,**string是C++标准库中的类。**确切地说,string类定义于C++标准库的<string>头文件。

关键的问题是,什么是C++标准库?这个名字很容易和标准模板库(Standard Template Libraries, STL)混淆,但实际上它们最开始是不同的东西。C++被标准化最早是1998年的ISO/IEC 14882:1998,即所谓的C++98。而STL始于C++尚未被标准化的1993年,它很大程度上影响了几年后C++标准中标准库的制定,因此其内容与C++标准库有非常大的重合。但需要注意的是,STL不完全是C++标准库的一部分,C++标准里也从来没有“STL”这一说法。C++标准库中的“STL”可以说是STL的一个“Fork”。

C++标准库包含一系列不带.h后缀的头文件,用尖括号引用。其中除宏定义以外的内容包含在名称空间std中,这也是许多C++程序都有一句using namespace std;的原因。

这是cppreference上对std::qsort的一段示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>     //C++输入/输出流标准头文件
#include <cstdlib> //C++标准库中来自C标准库的部分,去掉.h并在前面加c
#include <climits> //同上

int main()
{
int a[] = {-2, 99, 0, -743, 2, INT_MIN, 4};
constexpr std::size_t size = sizeof a / sizeof *a;

std::qsort(a, size, sizeof *a, [](const void* a, const void* b) //指定名称空间std
{
int arg1 = *static_cast<const int*>(a);
int arg2 = *static_cast<const int*>(b);

if(arg1 < arg2) return -1;
if(arg1 > arg2) return 1;
return 0;

// return (arg1 > arg2) - (arg1 < arg2); // possible shortcut
// return arg1 - arg2; // erroneous shortcut (fails if INT_MIN is present)
});

for(int ai : a)
std::cout << ai << ' ';
}

LPSTR,以及各种乱七八糟的

这就是较为难以理解的东西了,名字让人一头雾水。LPSTR算是最简单的形式,还有什么LPCTSTR,PCTSTR等等,这都是些啥?

其实这些全大写的神秘数据类型依然来自MFC,它们都是Windows特色数据类型。STR显然代表的是字符串,重要的是它们的前缀。

前缀中主要包含这么几个字母:

  • LP:长指针Long Pointer,代表这是一个32位指针。说是Long是因为上古Windows中的Pointer还是16位。
  • C:常量Const,代表这是一个常量。
  • T:代表使用_T宏,这个宏是用于动态支持Unicode这样的宽字节编码(即用多个字节代表一个字符)的。如果Unicode有定义,那么带T前缀的字符串就是Unicode编码的,否则依然是窄字节编码ANSI。
  • W:宽Wide,代表使用宽字节编码,如Unicode。

此外,MFC中还有神秘的BYTEWORDDWORD,其实就是分别对应unsigned charunsigned shortunsigned long

关于ANSI和Unicode,另外再开一篇文章讲好了。