MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

C++字符常量与字符串常量的差异及sizeof运算结果

2021-01-152.5k 阅读

C++字符常量与字符串常量的基本概念

字符常量

在C++ 中,字符常量是用单引号括起来的单个字符。例如:

char ch = 'a';

这里的 'a' 就是一个字符常量。C++ 中的字符常量基于ASCII 编码(在许多系统中),每个字符都对应一个唯一的整数值。字符常量本质上是一个 char 类型的数据,它在内存中占用1 个字节的空间。

字符串常量

字符串常量是用双引号括起来的零个或多个字符的序列。例如:

const char* str = "Hello, World!";

这里的 "Hello, World!" 就是一个字符串常量。字符串常量在内存中以字符数组的形式存储,并且以一个空字符 '\0' 作为结束标志。也就是说,即使我们看到的字符串是 "Hello",实际上在内存中存储的是 'H', 'e', 'l', 'l', 'o', '\0',总共6 个字符。

字符常量与字符串常量在内存中的存储方式

字符常量的内存存储

字符常量在内存中以其对应的ASCII 值进行存储。例如,字符 'a' 的ASCII 值是97,在内存中就以二进制形式 01100001 存储(假设是8 位系统)。由于字符常量只占用1 个字节,所以它的存储非常简单直接。

#include <iostream>
int main() {
    char ch = 'a';
    std::cout << "The ASCII value of 'a' is: " << static_cast<int>(ch) << std::endl;
    return 0;
}

上述代码将字符 'a' 转换为 int 类型并输出其ASCII 值。

字符串常量的内存存储

字符串常量在内存中是连续存储的字符数组,末尾自动添加 '\0'。例如,字符串 "test" 在内存中的存储如下(假设内存地址从低到高):

内存地址内容
0x1000't'
0x1001'e'
0x1002's'
0x1003't'
0x1004'\0'
#include <iostream>
int main() {
    const char* str = "test";
    for (int i = 0; str[i] != '\0'; ++i) {
        std::cout << "Character at position " << i << " is: " << str[i] << std::endl;
    }
    return 0;
}

这段代码遍历字符串 "test" 并逐个输出字符,展示了字符串在内存中的存储方式。

字符常量与字符串常量的类型差异

字符常量的类型

字符常量的类型是 char。这意味着当我们定义一个字符常量时,它的类型是明确的 char 类型。例如:

char ch1 = 'A';
char ch2 = 65; // 这里65 是'A' 的ASCII 值,也可以赋值给char 类型

在C++ 中,字符常量可以参与数值运算,因为它本质上是一个整数值(基于ASCII 编码)。例如:

#include <iostream>
int main() {
    char ch = 'a';
    int result = ch + 1;
    std::cout << "The result of 'a' + 1 is: " << static_cast<char>(result) << std::endl;
    return 0;
}

上述代码将字符 'a' 与1 相加,得到 'b' 的ASCII 值并转换为字符输出。

字符串常量的类型

字符串常量的类型是 const char*。这表示字符串常量实际上是一个指向 const char 类型的指针。例如:

const char* str = "Hello";

这里的 "Hello" 是一个字符串常量,它的类型是 const char*。这意味着我们不能通过这个指针修改字符串的内容,否则会导致编译错误。例如:

// 以下代码会导致编译错误
const char* str = "Hello";
str[0] = 'h';

如果我们想要修改字符串的内容,可以使用 char 数组来存储字符串:

char str[] = "Hello";
str[0] = 'h';

sizeof运算符与字符常量

sizeof字符常量

sizeof 运算符用于获取一个数据类型或变量所占用的字节数。当应用于字符常量时,sizeof 返回1,因为字符常量在C++ 中占用1 个字节。例如:

#include <iostream>
int main() {
    char ch = 'a';
    std::cout << "The size of 'a' is: " << sizeof('a') << " bytes" << std::endl;
    return 0;
}

上述代码输出 The size of 'a' is: 1 bytes,证明字符常量 'a' 占用1 个字节。

sizeof字符常量与不同编译器实现

虽然标准规定字符常量占用1 个字节,但在某些编译器中,可能会将字符常量视为 int 类型(这种情况较少见)。例如,在一些旧的编译器中:

#include <iostream>
int main() {
    std::cout << "The size of 'a' is: " << sizeof('a') << " bytes" << std::endl;
    return 0;
}

可能会输出 The size of 'a' is: 4 bytes,因为编译器将 'a' 视为 int 类型。但在现代标准C++ 编译器中,这种情况已经很少见,字符常量通常被视为 char 类型,占用1 个字节。

sizeof运算符与字符串常量

sizeof字符串常量

sizeof 应用于字符串常量时,它返回的是字符串常量包括末尾 '\0' 在内的总字节数。例如:

#include <iostream>
int main() {
    const char* str = "Hello";
    std::cout << "The size of \"Hello\" is: " << sizeof("Hello") << " bytes" << std::endl;
    return 0;
}

上述代码输出 The size of "Hello" is: 6 bytes,因为 "Hello" 加上末尾的 '\0' 总共占用6 个字节。

sizeof字符串常量与指针

需要注意的是,如果我们将 sizeof 应用于指向字符串常量的指针,它返回的是指针本身的大小,而不是字符串的大小。例如:

#include <iostream>
int main() {
    const char* str = "Hello";
    std::cout << "The size of the pointer str is: " << sizeof(str) << " bytes" << std::endl;
    return 0;
}

在32 位系统中,指针大小通常为4 个字节,在64 位系统中,指针大小通常为8 个字节。所以上述代码在64 位系统中会输出 The size of the pointer str is: 8 bytes

字符常量与字符串常量的使用场景

字符常量的使用场景

  1. 字符处理:当需要处理单个字符时,字符常量非常有用。例如,在字符加密算法中,可能需要对单个字符进行移位操作。
#include <iostream>
char encryptChar(char ch) {
    return static_cast<char>(ch + 1);
}
int main() {
    char ch = 'a';
    char encryptedCh = encryptChar(ch);
    std::cout << "The encrypted character of 'a' is: " << encryptedCh << std::endl;
    return 0;
}
  1. 条件判断:在条件判断中,字符常量常用于比较。例如,判断输入的字符是否为特定字符。
#include <iostream>
int main() {
    char ch;
    std::cout << "Enter a character: ";
    std::cin >> ch;
    if (ch == 'y') {
        std::cout << "You entered 'y'" << std::endl;
    } else {
        std::cout << "You did not enter 'y'" << std::endl;
    }
    return 0;
}

字符串常量的使用场景

  1. 文本输出:字符串常量广泛用于输出文本信息。例如,在 std::cout 中输出提示信息。
#include <iostream>
int main() {
    std::cout << "Welcome to the program!" << std::endl;
    return 0;
}
  1. 字符串操作:在字符串处理函数中,字符串常量作为输入参数。例如,strcpy 函数用于复制字符串。
#include <iostream>
#include <cstring>
int main() {
    char destination[20];
    const char* source = "Hello, World!";
    std::strcpy(destination, source);
    std::cout << "Copied string: " << destination << std::endl;
    return 0;
}

字符常量与字符串常量的相互转换

字符常量转字符串常量

要将字符常量转换为字符串常量,可以使用 std::string 类或字符数组。例如,使用 std::string 类:

#include <iostream>
#include <string>
int main() {
    char ch = 'a';
    std::string str(1, ch);
    std::cout << "The string from char is: " << str << std::endl;
    return 0;
}

使用字符数组:

#include <iostream>
int main() {
    char ch = 'a';
    char str[2];
    str[0] = ch;
    str[1] = '\0';
    std::cout << "The string from char is: " << str << std::endl;
    return 0;
}

字符串常量转字符常量

从字符串常量中提取字符常量相对简单,因为字符串常量本质上是字符数组。可以通过索引获取单个字符。例如:

#include <iostream>
int main() {
    const char* str = "Hello";
    char ch = str[0];
    std::cout << "The first character of the string is: " << ch << std::endl;
    return 0;
}

常见错误与陷阱

字符常量相关错误

  1. 多字符常量:在C++ 中,字符常量只能包含一个字符。例如,'ab' 是错误的,会导致编译错误。
// 以下代码会导致编译错误
char ch = 'ab';
  1. 字符常量类型混淆:虽然字符常量可以参与数值运算,但在某些情况下可能会导致类型混淆。例如:
#include <iostream>
int main() {
    char ch1 = 'a';
    char ch2 = 'b';
    int result = ch1 - ch2;
    std::cout << "The result of 'a' - 'b' is: " << result << std::endl;
    return 0;
}

这里需要注意 ch1 - ch2 的结果是一个 int 类型,因为字符常量在运算时会被提升为 int 类型。

字符串常量相关错误

  1. 字符串常量修改:由于字符串常量的类型是 const char*,试图修改字符串常量的内容会导致编译错误。例如:
// 以下代码会导致编译错误
const char* str = "Hello";
str[0] = 'h';
  1. 字符串常量与指针混淆:在使用 sizeof 运算符时,容易混淆字符串常量和指向字符串常量的指针。例如:
#include <iostream>
int main() {
    const char* str = "Hello";
    std::cout << "The size of the string is: " << sizeof(str) << " bytes" << std::endl;
    return 0;
}

上述代码本意是获取字符串 "Hello" 的大小,但实际上获取的是指针 str 的大小。正确的做法是对字符串常量本身使用 sizeof

#include <iostream>
int main() {
    const char* str = "Hello";
    std::cout << "The size of the string is: " << sizeof("Hello") << " bytes" << std::endl;
    return 0;
}

字符常量与字符串常量在函数参数中的应用

字符常量作为函数参数

当字符常量作为函数参数时,它以 char 类型传递。例如:

#include <iostream>
void printChar(char ch) {
    std::cout << "The character is: " << ch << std::endl;
}
int main() {
    printChar('a');
    return 0;
}

在这个例子中,字符常量 'a' 被传递给 printChar 函数,函数将其作为 char 类型处理并输出。

字符串常量作为函数参数

字符串常量作为函数参数时,以 const char* 类型传递。例如,在 strlen 函数中:

#include <iostream>
#include <cstring>
int main() {
    const char* str = "Hello";
    std::cout << "The length of the string is: " << std::strlen(str) << std::endl;
    return 0;
}

这里字符串常量 "Hello" 作为 const char* 类型传递给 std::strlen 函数,函数通过指针遍历字符串直到遇到 '\0' 来计算字符串长度。

字符常量与字符串常量在面向对象编程中的应用

字符常量在类中的应用

在类中,字符常量可以作为成员变量或用于成员函数的操作。例如,定义一个表示字符的类:

#include <iostream>
class Character {
private:
    char ch;
public:
    Character(char c) : ch(c) {}
    void printCharacter() {
        std::cout << "The character is: " << ch << std::endl;
    }
};
int main() {
    Character charObj('a');
    charObj.printCharacter();
    return 0;
}

在这个类中,字符常量 'a' 被用于初始化 Character 类的对象。

字符串常量在类中的应用

字符串常量在类中常用于表示对象的名称或描述等。例如,定义一个表示学生的类:

#include <iostream>
#include <string>
class Student {
private:
    std::string name;
public:
    Student(const char* n) : name(n) {}
    void printName() {
        std::cout << "The student's name is: " << name << std::endl;
    }
};
int main() {
    Student studentObj("Alice");
    studentObj.printName();
    return 0;
}

在这个类中,字符串常量 "Alice" 被用于初始化 Student 类的对象的 name 成员变量。

字符常量与字符串常量在模板编程中的应用

字符常量在模板中的应用

字符常量可以在模板编程中作为模板参数。例如:

#include <iostream>
template<char ch>
class CharacterPrinter {
public:
    void print() {
        std::cout << "The character is: " << ch << std::endl;
    }
};
int main() {
    CharacterPrinter<'a'> printer;
    printer.print();
    return 0;
}

在这个模板类中,字符常量 'a' 作为模板参数,使得可以根据不同的字符常量实例化不同的模板类对象。

字符串常量在模板中的应用

字符串常量在模板编程中应用相对复杂,因为字符串常量的类型是 const char*。但是,我们可以通过模板元编程来实现一些基于字符串常量的操作。例如,计算字符串长度的模板:

#include <iostream>
template<const char* str, size_t n = 0>
struct StringLength {
    static const size_t value = (str[n] == '\0') ? n : StringLength<str, n + 1>::value;
};
int main() {
    const char* str = "Hello";
    std::cout << "The length of the string is: " << StringLength<str>::value << std::endl;
    return 0;
}

在这个模板中,通过递归计算字符串常量的长度,展示了字符串常量在模板编程中的应用。

字符常量与字符串常量在不同平台下的兼容性

字符常量的平台兼容性

字符常量在不同平台下通常具有较好的兼容性,因为它们基于ASCII 编码(在大多数系统中)。然而,在一些非ASCII 编码的系统中,可能需要特别注意字符的表示。例如,在使用UTF - 8 编码的系统中,某些字符可能需要多个字节表示,但字符常量仍然遵循C++ 标准,占用1 个字节。在这种情况下,可能需要使用宽字符类型 wchar_tchar16_tchar32_t 来处理多字节字符。

字符串常量的平台兼容性

字符串常量在不同平台下也存在一些兼容性问题。例如,在Windows 系统中,字符串默认使用Unicode 编码(通常是UTF - 16),而在Linux 系统中,字符串通常使用UTF - 8 编码。当在不同平台间移植代码时,如果涉及字符串常量的处理,可能需要进行编码转换。例如,可以使用iconv 库在不同编码之间进行转换。此外,不同平台下字符串的存储方式和函数库的实现也可能略有不同,需要注意代码的兼容性。

字符常量与字符串常量对程序性能的影响

字符常量对程序性能的影响

字符常量由于占用空间小,在内存使用和处理速度上通常对程序性能影响较小。在频繁处理单个字符的场景中,例如字符加密算法或文本解析中,字符常量的高效性得以体现。由于字符常量占用1 个字节,在内存读写操作中效率较高,并且在CPU 缓存中占用空间也较小,有利于提高缓存命中率,从而提升程序性能。

字符串常量对程序性能的影响

字符串常量对程序性能的影响相对复杂。一方面,字符串常量的存储方式(以 const char* 指向,末尾带 '\0')在字符串操作函数中,如 strlenstrcpy 等,需要遍历字符串直到 '\0',这在处理长字符串时可能会有一定的性能开销。另一方面,由于字符串常量在程序中通常是共享的,多个指针可以指向同一个字符串常量,这在一定程度上节省了内存空间。在现代编译器中,对于字符串常量的优化也在不断改进,例如字符串常量的内联存储等,以提高程序性能。然而,如果在程序中频繁创建和销毁长字符串常量,可能会导致内存碎片,影响程序的整体性能。因此,在使用字符串常量时,需要根据具体的应用场景进行合理的设计和优化。