【问题标题】:Why does my program let me change a value of a const char in C?为什么我的程序允许我更改 C 中 const char 的值?
【发布时间】:2020-08-01 10:15:05
【问题描述】:

我使用 CodeBlocks for Windows 作为我的 IDE。我的程序有问题,我想了解一下。在我展示的程序中,我将一个变量声明为char*,然后我的程序将其转换为const char*。我希望这个char* 是只读的,但我可以修改,因为我不知道是什么原因。

这是我的代码:

#include <stdio.h>
#include <windows.h>

int main() {
    int i, d;
    char *array[5][5]; // This is the variable that I declare as char*
    for (i = 0; i != 5; i++) {
        for (d = 0; d != 5; d++) {
            array[i][d] = "   "; // I can change its value because it is a char* type variable
        }
    }
    for (i = 1; i != 0; i++) {
        printf("%s %s %s %s %s\n", array[0][0], array[0][1], array[0][2], array[0][3], array[0][4]);
        printf("%s %s %s %s %s\n", array[1][0], array[1][1], array[1][2], array[1][3], array[1][4]);
        printf("%s %s %s %s %s\n", array[2][0], array[2][1], array[2][2], array[2][3], array[2][4]);
        printf("%s %s %s %s %s\n", array[3][0], array[3][1], array[3][2], array[3][3], array[3][4]);
        printf("%s %s %s %s %s\n", array[4][0], array[4][1], array[4][2], array[4][3], array[4][4]);
        array[0][0] = "AAA";  // Again I can change its value
        array[1][0] = "BBB";  // And again...
        Sleep(1000);//This is only to see the results
        system("cls");
        if (i == 5) {
            array[0][0] = strdup(array[1][0]); // I was trying to copy two strings using the strcpy() but the program stopped, and the problem was that the destiny string was a const char* type string and that couldn't be, even though I could change it before  
            strcpy(array[0][0], array[1][0]);
        }
    }
    return 0;
}

【问题讨论】:

  • "programm 把它变成一个 const char*":我不知道你的意思。你能准确解释你观察到的东西吗?编译器错误、运行时错误、意外行为?很可能解释与神奇地改变类型的对象不同。
  • @NateEldredge 这是一个运行时错误,当行“array[0][0] = strdup(array[1][0]);”不存在时,当 strcpy 行应该工作程序停止运行,并出现消息“进程返回-1073741819(0xC0000005)”。
  • const char* 不包含数组;它包含一个指针。指针是常量。
  • 了解strcpy 和malloc。正确的方式是:array[i][d] = malloc( 4 ); strcpy( array[i][d], " " );
  • char *array[5][5] 没有声明 char *。它声明了一个数组

标签: c pointers constants c-strings string-literals


【解决方案1】:

char *array[5][5] 将array 定义为指向可修改char 的指针的可修改二维数组。你可以修改它的元素,它们指向的字符串也可以修改。

虽然" " 是一个字符串文字,如果不调用未定义的行为就无法修改,但C 标准不会将其键入为const char[],而是作为char [],当存储到一个元素时它会衰减为char * array.

当你直接写strcpy(array[0][0], array[1][0])时,你试图用array[1][0]指向的字符串的内容来修改array[0][0]指向的内存。由于您将字符串文字的地址存储在array[0][0] 中,因此您实际上是在尝试修改存储字符串文字的内存,它具有未定义的行为。

相反,strdup(array[1][0]) 分配一个由array[1][0] 指向的字符串的副本,并返回一个可以存储到array[0][0] 中的指针。下一条语句strcpy(array[0][0], array[1][0]); 将原始字符串复制到其副本之上,这是允许的,但无效。您可以删除此对 strcpy() 的调用。

建议将array 声明为const char *array[5][5],使strcpy(array[0][0], array[0][1]) 产生警告,但允许array[0][0] = strdup(array[0][1])。但是请注意,一旦您在此数组中混合了字符串文字和分配的字符串,您将无法判断哪些应该被释放,哪些不能。所有分配的内存将在退出时释放,但如果程序运行很长时间并不断修改这个数组,无法进行适当的内存管理将导致内存泄漏和潜在的无限内存使用。

一些编译器提供作为const char[] 类型字符串文字的扩展(例如:gcc -Wwrite-strings)。使用此选项,将" " 存储到array[0][0] 时会收到警告。默认情况下使用此设置是一种很好的做法,但可能需要在大型程序中进行大量修改,在许多地方添加const,这个过程称为const中毒。在此过程中发现潜在错误并不罕见。更一般地,如果函数定义中的指针参数不直接或间接用于修改它指向的对象,则应将其声明为const。它可以帮助读者理解代码,也可以帮助编译器生成更好的可执行代码。

【讨论】:

    【解决方案2】:

    您有一个二维指针数组。

    char * array[5][5];
    ^^^^^^
    

    所以这些陈述

        array[0][0] = "AAA";//Again I can change its value
        array[1][0] = "BBB";//And again.
    

    没有改变最初指向的字符串。他们更改了作为其值的指针。现在指针array[0][0] 指向文字"AAA",而指针array[1][0] 指向文字"BBB"。

    在此声明中

    array[0][0] = strdup(array[1][0]); 
    

    您再次没有更改指针array[0][0] 指向的字符串文字。您更改了指针本身的值。在此语句之后,指针array[0][0] 指向函数strdup 返回的动态分配内存,动态分配的数组包含指针array[1][0] 指向的字符串字面量的副本。这就是strdup 函数的作用。

    此声明

    strcpy(array[0][0], array[1][0]);
    

    是多余的,因为指针array[0][0]指向的动态分配数组已经存储了指针array[1][0]指向的字符串字面量的副本。

    注意,例如这样的声明

    const char *p = "AAA";
    

    表示您不能更改作为字符串文字“AAA”的指向对象。但是您可以更改指针本身的值,例如

    p = "BBB"; 
    

    此语句未更改字符串文字 "AAA"。它更改了指针 p 的值,该指针现在指向字符串文字 "BBB"。

    如果你希望指针也是不可修改的,那么你应该像这样声明它

    const char * const p = "AAA";
    

    在这种情况下是这样的声明

    p = "BBB"; 
    

    会产生编译错误。

    【讨论】:

    • 我有点困惑。我不明白为什么需要第一个 const 。在“const char *p”中,指针本身的值是可以改变的,那么与“char *p”和“const char *p”有什么区别呢?第二个例子让我有同样的问题。在“const char * const p”中,第二个 const 使指针的值不可更改,但我不太明白第一个代表什么。
    • @KazzioBots 假设您有一个声明为 char s[] = "ABC"; 的数组。和一个声明为 char *p = s; 的指针在这种情况下,指针指向数组的第一个字符。使用指针,您可以更改数组,例如 p[0] = 'Z';如果指针声明为 const char *p = s;那么这个语句 p[0] = 'Z';会产生错误。指向的数据是恒定的。
    【解决方案3】:

    该数组是一个包含 25 个指向 char 的指针的数组。

    数组指向的地方是只读内存中的文字。

    下面的程序表明指针是必须改变的。

    #include <stdio.h>
    //#include <windows.h>
    //#include <unistd.h>   // sleep()
    //#include <stdlib.h>   // system()
    #include <string.h>   // strcpy()
    
    int main( void )
    {
        int i, d;
        char *array[5][5]; // declare a 2d array of pointers to char
    
        // initialize the array
        for (i = 0; i != 5; i++) 
        {
            for (d = 0; d != 5; d++) 
            {
                array[i][d] = "abcd"; // I can change its value because it is a char* type variable
                // NO, this is changing where the pointer points
            }
        }
    
        for (i = 0; i <2; i++) 
        {
            printf("%s %s %s %s %s\n", array[0][0], array[0][1], array[0][2], array[0][3], array[0][4]);
            printf("%s %s %s %s %s\n", array[1][0], array[1][1], array[1][2], array[1][3], array[1][4]);
            printf("%s %s %s %s %s\n", array[2][0], array[2][1], array[2][2], array[2][3], array[2][4]);
            printf("%s %s %s %s %s\n", array[3][0], array[3][1], array[3][2], array[3][3], array[3][4]);
            printf("%s %s %s %s %s\n", array[4][0], array[4][1], array[4][2], array[4][3], array[4][4]);
            puts("");
    
            array[0][0] = "defg";  //changes the  the pointer, not the data
        }
    
        //wait for user input
        int ch;
        while( (ch = getchar()) != '\n' );
        getchar();
        return 0;
    }
    

    程序的输出是:

    abcd abcd abcd abcd abcd
    abcd abcd abcd abcd abcd
    abcd abcd abcd abcd abcd
    abcd abcd abcd abcd abcd
    abcd abcd abcd abcd abcd
    
    defg abcd abcd abcd abcd <-- note the changed pointer now points to the new data
    abcd abcd abcd abcd abcd
    abcd abcd abcd abcd abcd
    abcd abcd abcd abcd abcd
    abcd abcd abcd abcd abcd
    

    我修改了程序以使其可移植。这并没有改变基本概念。

    注意:对数组 I.E. 的裸引用。 “abcd”降级为数组第一个字节的地址。因此,设置 5x5 数组的嵌套循环正在设置地址,而不是地址指向的值

    【讨论】:

      猜你喜欢
      • 2023-04-02
      • 1970-01-01
      • 2013-05-08
      • 2010-10-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-09
      • 1970-01-01
      • 2021-11-01
      相关资源
      最近更新 更多