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

C++ const char *p与char * const p的错误使用案例

2022-08-237.2k 阅读

C++ const char *p 与 char * const p 的错误使用案例

const char *p 的含义及常见错误

在 C++ 中,const char *p 表示 p 是一个指向 const char 类型的指针。这意味着指针所指向的内容是常量,不能通过该指针去修改它所指向的字符。但指针本身的值(即它所指向的地址)是可以改变的。

试图通过指针修改常量内容的错误

下面是一个典型的错误示例:

#include <iostream>

int main() {
    const char *p = "Hello";
    // 以下代码试图通过指针修改常量内容,会导致编译错误
    p[0] = 'h'; 
    return 0;
}

在上述代码中,p 指向了一个字符串常量 "Hello"。当尝试通过 p[0] = 'h'; 修改字符串的第一个字符时,编译器会报错。这是因为 "Hello" 是一个字符串常量,存储在只读内存区域,而 const char *p 声明了不能通过 p 去修改其所指向的内容。不同的编译器可能给出不同的错误提示,但核心都是不允许对常量内容进行修改。例如,在 GCC 编译器下,可能会提示 error: assignment of read - only location '*((char *)p + 0)'

函数参数传递与修改误解

在函数中使用 const char * 作为参数时,也容易出现误解导致错误。

void printString(const char *str) {
    // 错误尝试:试图修改传入的常量字符串
    str[0] = 'X'; 
    std::cout << str << std::endl;
}

int main() {
    const char *message = "World";
    printString(message);
    return 0;
}

printString 函数中,参数 strconst char * 类型,这表明函数不应该修改 str 所指向的内容。但在函数内部尝试修改 str[0],这会导致编译错误。正确的做法是,若函数只是用于读取字符串内容,就应保持 const char * 的声明,不进行任何修改操作。

混淆指针指向改变与内容修改

虽然 const char *p 允许指针指向其他地址,但有时开发者可能会在需要改变指针指向时,错误地以为可以同时修改内容。

#include <iostream>

int main() {
    const char *p1 = "First";
    const char *p2 = "Second";
    p1 = p2; // 正确,指针可以指向其他地址
    // 但以下操作是错误的
    p1[0] = 's'; 
    return 0;
}

在这个例子中,p1 = p2; 是合法的,因为 const char * 类型的指针本身的值可以改变,即可以指向其他地址。然而,在 p1 重新指向 p2 所指向的字符串后,试图通过 p1 修改字符串内容仍然是不允许的,会导致编译错误。

char * const p 的含义及常见错误

char * const p 表示 p 是一个常量指针,即指针本身的值(所指向的地址)是固定的,不能改变,但通过该指针可以修改其所指向的内容。

试图改变常量指针的地址

#include <iostream>

int main() {
    char str1[] = "Hello";
    char str2[] = "World";
    char * const p = str1;
    // 以下代码试图改变常量指针的地址,会导致编译错误
    p = str2; 
    return 0;
}

在上述代码中,p 被声明为 char * const 类型,一旦它被初始化为指向 str1,就不能再让它指向其他地址。当执行 p = str2; 时,编译器会报错。例如,GCC 编译器可能提示 error: assignment of read - only variable 'p'。这是因为常量指针的地址是固定的,不允许重新赋值。

未初始化常量指针

#include <iostream>

int main() {
    char * const p; // 错误,常量指针必须初始化
    char str[] = "Hello";
    p = str; 
    return 0;
}

由于 char * const p 是常量指针,其值在声明后不能改变,所以必须在声明时进行初始化。上述代码中先声明了 p 而未初始化,然后再尝试赋值,这是不允许的,会导致编译错误。正确的做法是在声明时就初始化,如 char * const p = str;

对指向内容修改的误解

虽然 char * const p 允许修改所指向的内容,但在实际应用中,可能会因为对其特性的误解而出现逻辑错误。

#include <iostream>

void modifyString(char * const str) {
    // 假设这里原本只想修改字符串的第一个字符,但错误地重新赋值了指针
    char newStr[] = "New String";
    str = newStr; 
    std::cout << str << std::endl;
}

int main() {
    char message[] = "Original";
    modifyString(message);
    std::cout << message << std::endl;
    return 0;
}

modifyString 函数中,参数 strchar * const 类型,本意可能只是修改 message 的内容。但在函数内部,错误地对 str 进行了重新赋值,这是不允许的,会导致编译错误。正确的做法应该是直接修改 str 所指向的内容,例如 str[0] = 'N';

复杂场景下的错误

嵌套指针与 const 的混淆

当涉及到嵌套指针和 const 修饰符时,错误更容易发生。

#include <iostream>

int main() {
    const char * const *pp; 
    char * const *pp2; 
    char ** const pp3; 

    const char *str = "Hello";
    char *str2 = "World";

    // 以下赋值操作错误示例
    pp = &str2; 
    pp2 = &str; 
    pp3 = &str; 

    return 0;
}

在上述代码中,const char * const *pp; 表示 pp 是一个指向 const char * 类型的常量指针。char * const *pp2; 表示 pp2 是一个指向 char * const 类型的指针。char ** const pp3; 表示 pp3 是一个常量指针,它指向 char ** 类型。

当执行 pp = &str2; 时,会出现错误,因为 pp 应该指向 const char * 类型,而 str2char * 类型,类型不匹配。同样,pp2 = &str; 也会出错,因为 pp2 期望指向 char * const 类型,而 strconst char * 类型。pp3 = &str; 错误,因为 pp3 应该指向 char ** 类型,而 strconst char * 类型。

函数返回值与 const char *p 和 char * const p

在函数返回值方面,若不清晰 const char *char * const 的区别,也会导致错误。

const char * createString1() {
    char temp[] = "Temp String";
    return temp; 
}

char * const createString2() {
    char temp[] = "Temp String";
    return temp; 
}

int main() {
    const char *result1 = createString1();
    char * const result2 = createString2();
    return 0;
}

createString1 函数中,返回了一个指向局部数组 tempconst char * 指针。这里存在一个问题,temp 是局部变量,在函数结束时会被销毁,返回指向它的指针会导致悬空指针问题。

createString2 函数中,同样返回了指向局部数组 tempchar * const 指针,也存在悬空指针问题。正确的做法可以是动态分配内存并返回指针,同时要注意内存的释放,以避免内存泄漏。例如:

const char * createString1() {
    char *str = new char[12];
    std::strcpy(str, "Temp String");
    return str; 
}

char * const createString2() {
    char *str = new char[12];
    std::strcpy(str, "Temp String");
    return str; 
}

int main() {
    const char *result1 = createString1();
    char * const result2 = createString2();
    // 使用完后记得释放内存
    delete[] result1;
    delete[] result2;
    return 0;
}

模板与 const char *p 和 char * const p 的错误交互

在模板编程中,const char *char * const 也可能引发错误。

template <typename T>
void printValue(T value) {
    std::cout << value << std::endl;
}

int main() {
    const char *str = "Hello";
    char * const str2 = "World";
    printValue(str);
    printValue(str2); 
    return 0;
}

在这个简单的模板函数 printValue 中,虽然它看似可以接受任何类型的参数并打印。但在实际应用中,如果模板函数内部对参数进行了不符合 const char *char * const 特性的操作,就会出现问题。例如,如果模板函数试图修改 const char * 所指向的内容,或者试图改变 char * const 的指针地址,都会导致编译错误。

与其他 C++ 特性结合时的错误

与类成员函数结合的错误

const char *char * const 作为类成员函数的参数或返回值时,容易出现错误。

class MyClass {
public:
    const char * getMessage() {
        char temp[] = "Message";
        return temp; 
    }
};

int main() {
    MyClass obj;
    const char *msg = obj.getMessage();
    return 0;
}

MyClassgetMessage 函数中,返回了一个指向局部数组 tempconst char * 指针,这会导致悬空指针问题,与前面函数返回值的错误类似。

若将函数修改为如下形式:

class MyClass {
public:
    char * const getMessage() {
        char temp[] = "Message";
        return temp; 
    }
};

int main() {
    MyClass obj;
    char * const msg = obj.getMessage();
    return 0;
}

同样存在悬空指针问题,因为 temp 是局部变量,函数结束后会被销毁。

与 STL 容器结合的错误

在使用 STL 容器时,const char *char * const 也可能带来错误。

#include <vector>
#include <iostream>

int main() {
    std::vector<const char *> vec;
    const char *str1 = "First";
    const char *str2 = "Second";
    vec.push_back(str1);
    vec.push_back(str2);

    // 试图修改容器中 const char * 所指向的内容,错误
    vec[0][0] = 'f'; 

    std::vector<char * const> vec2;
    char *str3 = "Third";
    char *str4 = "Fourth";
    vec2.push_back(str3);
    // 试图改变容器中 char * const 的指针地址,错误
    vec2[0] = str4; 

    return 0;
}

在第一个 std::vector<const char *> 中,当尝试通过 vec[0][0] = 'f'; 修改 const char * 所指向的内容时,会导致编译错误。在第二个 std::vector<char * const> 中,当执行 vec2[0] = str4; 试图改变 char * const 的指针地址时,也会导致编译错误。

总结常见错误及避免方法

  1. *对于 const char p
    • 常见错误:试图通过指针修改其所指向的常量内容;在函数参数传递时误解其不能修改内容的特性;混淆指针指向改变与内容修改。
    • 避免方法:牢记通过 const char * 指针不能修改其所指向的内容;在函数参数声明为 const char * 时,确保函数内部不进行修改操作;明确指针指向改变和内容修改的区别,仅在需要改变指针指向时进行合法操作。
  2. 对于 char * const p
    • 常见错误:试图改变常量指针的地址;未初始化常量指针;对指向内容修改的误解,错误地重新赋值指针而非修改内容。
    • 避免方法:在声明 char * const 指针时务必初始化;牢记常量指针地址不可改变;在函数中使用 char * const 作为参数时,清楚只能修改其所指向的内容,不能重新赋值指针。
  3. 复杂场景及结合其他特性时
    • 常见错误:嵌套指针与 const 修饰符混淆;函数返回值导致悬空指针;模板与 const char *char * const 交互错误;与类成员函数、STL 容器结合时出现不符合其特性的操作。
    • 避免方法:仔细分析嵌套指针中 const 的作用和类型匹配;在函数返回指针时,确保返回的指针指向的内存不会在函数结束后被销毁;在模板函数中,确保对 const char *char * const 参数的操作符合其特性;在类成员函数和 STL 容器中使用 const char *char * const 时,严格遵循其常量或可修改的规则。

通过对这些错误案例的分析和了解,开发者可以在 C++ 编程中更加准确地使用 const char *pchar * const p,避免因对其特性的误解而导致的各种编译错误和运行时问题,从而编写出更加健壮和可靠的代码。在实际编程中,不断实践并加深对这些概念的理解,将有助于提高编程的质量和效率。同时,借助编译器的错误提示信息,能够更快速地定位和解决因 const char *char * const 使用不当而产生的问题。例如,在面对复杂的嵌套指针和 const 修饰符组合时,通过逐步分析和编译调试,可以清晰地理解错误产生的原因并加以修正。在使用 STL 容器存储 const char *char * const 时,要特别注意容器操作是否符合指针的特性,避免意外的修改操作导致错误。在类的设计中,对于成员函数涉及 const char *char * const 的参数和返回值,要进行全面的考量,确保类的行为符合预期。总之,深入理解 const char *pchar * const p 的特性并正确运用,是 C++ 开发者必备的技能之一。