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

C++字符常量与字符串常量的区别及其sizeof计算

2024-09-067.7k 阅读

C++字符常量与字符串常量的区别

字符常量的本质

在C++ 中,字符常量是用单引号括起来的单个字符。例如:'a''Z''5' 等。从本质上来说,字符常量在内存中是以对应的ASCII码值(或者更准确地说,在C++ 支持的字符集编码下对应的数值)来存储的。

'a' 为例,在ASCII码表中,'a' 对应的十进制数值是97。当编译器处理 'a' 这个字符常量时,它实际上是将数值97存储在内存中合适的位置。这里需要注意的是,虽然我们看到的是字符 'a',但计算机内部是以数值形式处理和存储的。

C++ 中的字符常量占用一个字节的内存空间,这是因为字符集(如ASCII码)通常只需要一个字节(8位)就可以表示所有的字符。即使是扩展的ASCII码,也只使用一个字节来表示128 - 255的额外字符。

#include <iostream>
int main() {
    char ch = 'a';
    std::cout << "字符 'a' 的ASCII码值是: " << static_cast<int>(ch) << std::endl;
    return 0;
}

在上述代码中,我们通过 static_cast<int>(ch) 将字符 'a' 转换为对应的整数值并输出。运行这段代码,会输出 字符 'a' 的ASCII码值是: 97

字符串常量的本质

字符串常量则是用双引号括起来的零个或多个字符的序列。例如:"Hello""123"""(空字符串)等。字符串常量在内存中的存储方式与字符常量有很大的不同。

在C++ 中,字符串常量实际上是以一个以空字符 '\0' 结尾的字符数组形式存储的。以 "Hello" 为例,它在内存中实际存储的是 'H''e''l''l''o''\0' 这6个字符。这里的 '\0' 是字符串的结束标志,它告诉程序这个字符串在哪里结束。

#include <iostream>
int main() {
    const char* str = "Hello";
    for (int i = 0; str[i] != '\0'; ++i) {
        std::cout << "字符: " << str[i] << " 地址: " << &str[i] << std::endl;
    }
    return 0;
}

在上述代码中,我们通过遍历字符串并输出每个字符及其地址,可以看到字符串在内存中的存储情况。'\0' 虽然不显示,但它确实存在于字符串的末尾,占据一个字节的空间。

字符常量与字符串常量的区别总结

  1. 表示形式:字符常量用单引号 ' ' 括起来,而字符串常量用双引号 " " 括起来。
  2. 内存存储:字符常量只存储一个字符对应的数值,占用一个字节;字符串常量以字符数组形式存储,并且末尾会自动添加 '\0' 作为结束标志,占用的字节数是字符串中字符数加上1。
  3. 数据类型:字符常量的类型是 char,而字符串常量的类型是 const char*(指向常量字符的指针)。

sizeof计算在字符常量与字符串常量中的应用

sizeof对字符常量的计算

sizeof 是C++ 中的一个操作符,用于计算数据类型或变量所占用的字节数。当对字符常量使用 sizeof 时,由于字符常量占用一个字节,所以 sizeof 返回的值始终为1。

#include <iostream>
int main() {
    char ch = 'a';
    std::cout << "字符常量 'a' 的sizeof结果: " << sizeof('a') << std::endl;
    std::cout << "字符变量ch的sizeof结果: " << sizeof(ch) << std::endl;
    return 0;
}

在上述代码中,无论是对字符常量 'a' 直接使用 sizeof,还是对存储字符常量的字符变量 ch 使用 sizeof,结果都是1。这再次验证了字符常量在C++ 中占用一个字节的事实。

sizeof对字符串常量的计算

对于字符串常量,sizeof 计算的是包含结束符 '\0' 在内的整个字符串所占用的字节数。

#include <iostream>
int main() {
    const char* str = "Hello";
    std::cout << "字符串常量 \"Hello\" 的sizeof结果: " << sizeof("Hello") << std::endl;
    std::cout << "指向字符串常量的指针str的sizeof结果: " << sizeof(str) << std::endl;
    return 0;
}

在这段代码中,sizeof("Hello") 的结果是6,因为 "Hello" 实际存储为 'H''e''l''l''o''\0' 共6个字符。而 sizeof(str) 的结果在32位系统上通常是4(指针大小为4字节),在64位系统上通常是8(指针大小为8字节),这是因为 str 是一个指针,sizeof 计算的是指针本身的大小,而不是指针所指向的字符串的大小。

字符串数组与 sizeof

当我们定义一个字符数组来存储字符串时,sizeof 的行为也很有趣。

#include <iostream>
int main() {
    char strArray[] = "Hello";
    std::cout << "字符数组strArray的sizeof结果: " << sizeof(strArray) << std::endl;
    return 0;
}

在上述代码中,sizeof(strArray) 的结果同样是6,因为 strArray 是一个字符数组,它的大小是根据初始化的字符串常量来确定的,包含了结束符 '\0'。这与直接对字符串常量使用 sizeof 的结果是一致的。

字符数组与指针在 sizeof 上的差异

#include <iostream>
int main() {
    char strArray[] = "Hello";
    const char* strPtr = "Hello";
    std::cout << "字符数组strArray的sizeof结果: " << sizeof(strArray) << std::endl;
    std::cout << "指针strPtr的sizeof结果: " << sizeof(strPtr) << std::endl;
    return 0;
}

这里可以清楚地看到差异,sizeof(strArray) 返回的是字符串包含结束符 '\0' 的实际大小,而 sizeof(strPtr) 返回的是指针的大小,这是因为指针只是存储了字符串常量在内存中的起始地址,而不是字符串本身。

字符常量与字符串常量在函数参数传递中的 sizeof 问题

字符常量作为函数参数

当字符常量作为函数参数传递时,它遵循C++ 的值传递规则。

#include <iostream>
void printCharSize(char ch) {
    std::cout << "函数内字符参数的sizeof结果: " << sizeof(ch) << std::endl;
}
int main() {
    char ch = 'a';
    printCharSize(ch);
    printCharSize('a');
    return 0;
}

在上述代码中,无论是传递字符变量 ch 还是直接传递字符常量 'a',在函数 printCharSizesizeof(ch) 的结果都是1,因为字符常量在传递过程中保持其占用一个字节的特性。

字符串常量作为函数参数

字符串常量作为函数参数传递时,情况有所不同。通常,字符串常量会被转换为指向其首字符的指针。

#include <iostream>
void printStrSize(const char* str) {
    std::cout << "函数内字符串指针的sizeof结果: " << sizeof(str) << std::endl;
}
int main() {
    const char* str = "Hello";
    printStrSize(str);
    printStrSize("Hello");
    return 0;
}

在这段代码中,无论是传递字符串指针 str 还是直接传递字符串常量 "Hello",在函数 printStrSizesizeof(str) 的结果都是指针的大小(32位系统上通常为4字节,64位系统上通常为8字节)。这是因为字符串常量在传递时被转换为指针,函数接收到的是指针,而不是字符串本身。

字符数组作为函数参数

当字符数组作为函数参数传递时,它会退化为指针。

#include <iostream>
void printArraySize(char arr[]) {
    std::cout << "函数内字符数组参数(实际为指针)的sizeof结果: " << sizeof(arr) << std::endl;
}
int main() {
    char strArray[] = "Hello";
    printArraySize(strArray);
    return 0;
}

在上述代码中,printArraySize 函数接收到的 arr 实际上是一个指针,所以 sizeof(arr) 的结果是指针的大小,而不是字符数组的实际大小。如果我们想要在函数内获取字符数组的实际大小,一种方法是在传递数组的同时传递数组的大小信息。

#include <iostream>
void printArraySize(char arr[], size_t size) {
    std::cout << "函数内字符数组的实际大小: " << size << std::endl;
}
int main() {
    char strArray[] = "Hello";
    printArraySize(strArray, sizeof(strArray));
    return 0;
}

这样,通过传递数组的大小信息,我们可以在函数内准确地知道字符数组的实际大小。

字符常量与字符串常量在类型兼容性方面与 sizeof 的关系

字符常量的类型兼容性

字符常量在C++ 中具有 char 类型。它可以隐式地转换为其他数值类型,如 intshort 等。在这些转换过程中,sizeof 的结果会根据目标类型而变化。

#include <iostream>
int main() {
    char ch = 'a';
    int num = ch;
    std::cout << "字符常量转换为int后的sizeof结果: " << sizeof(num) << std::endl;
    return 0;
}

在上述代码中,字符常量 'a' 被转换为 int 类型。在大多数系统上,int 类型占用4个字节,所以 sizeof(num) 的结果是4。

字符串常量的类型兼容性

字符串常量的类型是 const char*。它与其他指针类型在一定程度上具有兼容性。例如,可以将字符串常量赋值给 const char* 类型的指针。

#include <iostream>
int main() {
    const char* str = "Hello";
    std::cout << "字符串常量指针的sizeof结果: " << sizeof(str) << std::endl;
    return 0;
}

这里 sizeof(str) 的结果是指针的大小。如果我们尝试将字符串常量赋值给其他类型的指针,如 char*(非 const),会导致编译错误,因为字符串常量是常量,不能通过非 const 指针来修改。

字符数组与字符串常量的类型兼容性

字符数组可以用来初始化字符串常量,并且在这种情况下,字符数组的大小会根据字符串常量的长度(包括 '\0')来确定。

#include <iostream>
int main() {
    char strArray[] = "Hello";
    std::cout << "由字符串常量初始化的字符数组的sizeof结果: " << sizeof(strArray) << std::endl;
    return 0;
}

这里 sizeof(strArray) 的结果是6,因为字符数组的大小是根据字符串常量 "Hello" 包含结束符 '\0' 的长度来确定的。

字符常量与字符串常量在不同编译器下 sizeof 的表现

不同编译器对字符常量 sizeof 的一致性

在不同的C++ 编译器(如GCC、Clang、Visual Studio C++ 等)下,对于字符常量使用 sizeof 的结果通常是一致的,都返回1。这是因为C++ 标准规定字符常量占用一个字节,这是语言的基本特性,不受编译器实现的影响。

#include <iostream>
int main() {
    std::cout << "字符常量 'a' 的sizeof结果: " << sizeof('a') << std::endl;
    return 0;
}

无论在哪个编译器上运行上述代码,结果都是1。

不同编译器对字符串常量 sizeof 的一致性

对于字符串常量,不同编译器在使用 sizeof 时也具有一致性。sizeof 会返回包含结束符 '\0' 在内的字符串长度。

#include <iostream>
int main() {
    std::cout << "字符串常量 \"Hello\" 的sizeof结果: " << sizeof("Hello") << std::endl;
    return 0;
}

在不同编译器上运行这段代码,sizeof("Hello") 的结果都是6,这是因为这是C++ 标准规定的字符串常量存储和 sizeof 计算方式。

指针大小与编译器平台的关系

虽然对于字符常量和字符串常量本身的 sizeof 计算在不同编译器上具有一致性,但对于字符串常量指针的 sizeof 结果会因编译器平台而异。

在32位系统上,指针通常占用4个字节,而在64位系统上,指针通常占用8个字节。这会导致在不同平台下,sizeof(const char*) 的结果不同。

#include <iostream>
int main() {
    const char* str = "Hello";
    std::cout << "字符串常量指针的sizeof结果: " << sizeof(str) << std::endl;
    return 0;
}

在32位编译器平台上运行,结果可能是4;在64位编译器平台上运行,结果可能是8。

字符常量与字符串常量在内存布局中的 sizeof 体现

字符常量在内存中的布局与 sizeof

字符常量在内存中只占用一个字节的空间,存储的是字符对应的数值。例如,'a' 在内存中存储的是97(ASCII码值)。

#include <iostream>
int main() {
    char ch = 'a';
    std::cout << "字符常量 'a' 的地址: " << &ch << std::endl;
    std::cout << "字符常量 'a' 的sizeof结果: " << sizeof(ch) << std::endl;
    return 0;
}

通过输出字符常量的地址和 sizeof 结果,可以直观地看到字符常量在内存中占用一个字节,并且地址是该字节的起始位置。

字符串常量在内存中的布局与 sizeof

字符串常量在内存中以字符数组形式存储,末尾带有结束符 '\0'sizeof 计算的是整个包含结束符的字符数组的大小。

#include <iostream>
int main() {
    const char* str = "Hello";
    std::cout << "字符串常量 \"Hello\" 的首地址: " << str << std::endl;
    std::cout << "字符串常量 \"Hello\" 的sizeof结果: " << sizeof("Hello") << std::endl;
    return 0;
}

通过输出字符串常量的首地址和 sizeof 结果,可以看到字符串常量在内存中的布局情况,以及 sizeof 是如何准确反映包含结束符的字符串大小的。

字符数组与字符串常量内存布局及 sizeof 对比

#include <iostream>
int main() {
    char strArray[] = "Hello";
    const char* strPtr = "Hello";
    std::cout << "字符数组strArray的首地址: " << strArray << std::endl;
    std::cout << "字符数组strArray的sizeof结果: " << sizeof(strArray) << std::endl;
    std::cout << "字符串指针strPtr的首地址: " << strPtr << std::endl;
    std::cout << "字符串指针strPtr的sizeof结果: " << sizeof(strPtr) << std::endl;
    return 0;
}

在上述代码中,通过对比字符数组和字符串指针的首地址以及 sizeof 结果,可以清晰地看到字符数组在内存中存储实际的字符串内容,sizeof 反映的是数组大小;而字符串指针只是存储字符串常量的首地址,sizeof 反映的是指针大小。

字符常量与字符串常量在C++ 标准库中的应用与 sizeof

字符常量在C++ 标准库函数中的应用

C++ 标准库中有许多函数可以处理字符常量。例如,<cctype> 头文件中的函数用于检查和转换字符。

#include <iostream>
#include <cctype>
int main() {
    char ch = 'a';
    if (std::islower(ch)) {
        std::cout << "字符 'a' 是小写字母" << std::endl;
    }
    std::cout << "字符常量 'a' 的sizeof结果: " << sizeof(ch) << std::endl;
    return 0;
}

在上述代码中,std::islower 函数用于检查字符是否为小写字母。这里字符常量 'a' 作为参数传递,其 sizeof 结果仍然是1,这表明在标准库函数的应用中,字符常量的基本特性不变。

字符串常量在C++ 标准库函数中的应用

<cstring> 头文件中的函数用于处理字符串常量。例如,strlen 函数用于计算字符串的长度(不包括结束符 '\0')。

#include <iostream>
#include <cstring>
int main() {
    const char* str = "Hello";
    std::cout << "字符串 \"Hello\" 的长度: " << std::strlen(str) << std::endl;
    std::cout << "字符串常量 \"Hello\" 的sizeof结果: " << sizeof("Hello") << std::endl;
    return 0;
}

在这段代码中,std::strlen 计算的是字符串的实际长度,而 sizeof("Hello") 计算的是包含结束符 '\0' 的字符串大小。这体现了字符串常量在标准库函数应用中的特点,以及 sizeof 在这种情况下的不同计算方式。

字符串流与字符串常量及 sizeof

<sstream> 头文件中的字符串流可以用于处理字符串。例如,std::ostringstream 可以将数据转换为字符串。

#include <iostream>
#include <sstream>
int main() {
    int num = 123;
    std::ostringstream oss;
    oss << num;
    std::string str = oss.str();
    const char* cStr = str.c_str();
    std::cout << "转换后的字符串: " << cStr << std::endl;
    std::cout << "字符串常量的sizeof结果: " << sizeof(cStr) << std::endl;
    return 0;
}

在上述代码中,通过字符串流将整数转换为字符串,并获取其 const char* 形式。这里 sizeof(cStr) 计算的是指针的大小,而不是字符串的实际大小,这再次强调了字符串常量指针与字符串本身在 sizeof 计算上的区别。

字符常量与字符串常量在模板编程中的 sizeof 考量

字符常量在模板中的 sizeof

在模板编程中,字符常量的 sizeof 同样遵循其基本规则。

#include <iostream>
template <typename T>
void printSize(T value) {
    std::cout << "类型 " << typeid(T).name() << " 的sizeof结果: " << sizeof(value) << std::endl;
}
int main() {
    printSize('a');
    return 0;
}

在上述代码中,模板函数 printSize 可以接受不同类型的参数并输出其 sizeof 结果。当传递字符常量 'a' 时,sizeof('a') 的结果仍然是1,这表明在模板环境下字符常量的 sizeof 特性不变。

字符串常量在模板中的 sizeof

字符串常量在模板编程中也有其特点。

#include <iostream>
template <typename T>
void printStrSize(T str) {
    std::cout << "类型 " << typeid(T).name() << " 的sizeof结果: " << sizeof(str) << std::endl;
}
int main() {
    printStrSize("Hello");
    return 0;
}

在这段代码中,当传递字符串常量 "Hello" 时,sizeof(str) 的结果是指针的大小,因为字符串常量在传递给模板函数时被转换为指针。这与在普通函数中字符串常量作为参数传递时 sizeof 的计算结果一致。

模板特化与字符常量和字符串常量的 sizeof

我们可以通过模板特化来针对字符常量和字符串常量进行特殊处理。

#include <iostream>
template <typename T>
void printSize(T value) {
    std::cout << "通用类型 " << typeid(T).name() << " 的sizeof结果: " << sizeof(value) << std::endl;
}
template <>
void printSize<char>(char ch) {
    std::cout << "字符类型char的sizeof结果: " << sizeof(ch) << std::endl;
}
template <>
void printSize<const char*>(const char* str) {
    std::cout << "字符串指针类型const char*的sizeof结果: " << sizeof(str) << std::endl;
}
int main() {
    printSize('a');
    printSize("Hello");
    return 0;
}

在上述代码中,通过模板特化,我们可以分别针对字符常量和字符串常量指针提供更具体的 sizeof 输出,从而更好地处理它们在模板编程中的不同特性。

字符常量与字符串常量在面向对象编程中的 sizeof 问题

类中包含字符常量成员与 sizeof

当类中包含字符常量成员时,sizeof 计算类的大小时会考虑字符常量的大小。

#include <iostream>
class CharClass {
public:
    char ch;
};
int main() {
    CharClass obj;
    obj.ch = 'a';
    std::cout << "包含字符常量成员的类的sizeof结果: " << sizeof(obj) << std::endl;
    return 0;
}

在上述代码中,CharClass 类包含一个字符常量成员 chsizeof(obj) 的结果是1,因为字符常量成员占用一个字节,类的大小主要由其成员变量决定。

类中包含字符串常量成员与 sizeof

当类中包含字符串常量成员时,情况有所不同。字符串常量成员通常以指针形式存储。

#include <iostream>
class StrClass {
public:
    const char* str;
};
int main() {
    StrClass obj;
    obj.str = "Hello";
    std::cout << "包含字符串常量成员的类的sizeof结果: " << sizeof(obj) << std::endl;
    return 0;
}

在这段代码中,StrClass 类包含一个字符串常量指针成员 strsizeof(obj) 的结果是指针的大小(32位系统上通常为4字节,64位系统上通常为8字节),而不是字符串本身的大小。这是因为类中存储的是指向字符串常量的指针,而不是字符串的实际内容。

继承与字符常量和字符串常量成员的 sizeof

当涉及类的继承时,sizeof 的计算会考虑基类和派生类的所有成员。

#include <iostream>
class BaseCharClass {
public:
    char ch;
};
class DerivedCharClass : public BaseCharClass {
public:
    int num;
};
class BaseStrClass {
public:
    const char* str;
};
class DerivedStrClass : public BaseStrClass {
public:
    double dbl;
};
int main() {
    DerivedCharClass charObj;
    charObj.ch = 'a';
    charObj.num = 123;
    std::cout << "包含字符常量成员的继承类的sizeof结果: " << sizeof(charObj) << std::endl;
    DerivedStrClass strObj;
    strObj.str = "Hello";
    strObj.dbl = 3.14;
    std::cout << "包含字符串常量成员的继承类的sizeof结果: " << sizeof(strObj) << std::endl;
    return 0;
}

在上述代码中,对于包含字符常量成员的继承类 DerivedCharClasssizeof(charObj) 的结果是 sizeof(char) + sizeof(int),因为它包含了基类的字符常量成员和自身的整数成员。对于包含字符串常量成员的继承类 DerivedStrClasssizeof(strObj) 的结果是 sizeof(const char*) + sizeof(double),因为它包含了基类的字符串常量指针成员和自身的双精度浮点数成员。这展示了在面向对象编程中,继承关系下字符常量和字符串常量成员对类大小计算的影响。