如何在C中更改常量数组

时间:2018-10-04 05:41:23

标签: c

我创建了一个具有7个元素的常量数组string,并在数组末尾添加了前哨字符。但是,当我使用scanf("%s", string)并输入包含7个以上元素的新字符串(例如“ 10987654321”)时,将修改此常量数组。怎么会这样我的意思是,这是常量数组;如何更改它,并接收我在代码开头声明的7个以上元素?有人可以为我解释吗?

#include<stdio.h>
int main(){
    const char string[8] = "1234567";

    printf("string initialize is: %s", string);
    printf("\n\n");
    printf("scanfing d\n");
    scanf("%s", string);
    printf("string now: %s\n", string); 
    return 0;
}

我的输出是:

string initialize is: 1234567
scanfing d
10987654321
string now: 10987654321

4 个答案:

答案 0 :(得分:4)

这是未定义的行为,因为您应始终将用const声明的变量视为不可变(不可修改)。

特定的实现可能允许更改某些const变量,但是在您显示的代码中,它仍然是UB,并且应始终避免使用此类代码。

答案 1 :(得分:1)

这是未定义的行为。格式说明符%s用于char*,但是您传递了const char*(数组衰减)。

未定义行为表示标准未为您的程序定义任何行为。它可以打印您看到的内容,可以打印原始字符串,可以打印乱码,可以崩溃或发生任何事情。

还请启用编译器警告。这将被捕获:

  

警告:格式指定类型'char *',但参数具有类型   'const char *'[-Wformat]

scanf("%s", string);

       ~~   ^~~~~~

       %7s

关于写作超出您的分配范围。考虑到数组不是const(可变),则向其写入超过7个字符也将是未定义行为。

答案 2 :(得分:1)

在继续您的评论以及您所遇到的困难之后,您了解了为什么可以在const调用中写入scanf限定数组,并将字符数组string声明为{ {1}}通过发出编译器警告(由const选项提供)来保护程序员免于更改string的内容。它不会使数组-Wformat本身不可变。如果尝试更改内容,它将发出一条通知(警告),即您试图写入一个常量对象。它不会将存储本身更改为只读,也不会将存储string的数据段更改为string

如果您随后放弃该警告并忽略编译器告诉您的内容,则取决于编译器,您很可能成功更改了.rodata的内容,但是您调用了 Undefined Behavior >这样做。参见C11 Standard - 6.7.3 Type qualifiers(p6)。此外,当您超出数组的范围书写时,还会再次导致未定义的行为。

例如,输入您的代码:

string

当您在正确启用警告的情况下尝试编译该程序时,例如

#include <stdio.h>

int main (void) {

    const char string[8] = "1234567";

    printf ("string initialize is: %s, string\n\nscanfing d\n", string);
    scanf ("%s", string);
    printf ("string now: %s\n", string); 

    return 0;
}

编译器将发出类似于以下内容的警告:

$ gcc -Wall -Wextra -o /tmp/bin/conststring conststring.c

这表示“ 警告:写入常量对象”。不要那样做。

注意:即使使用conststring.c: In function ‘main’: conststring.c:8:5: warning: writing into constant object (argument 2) [-Wformat=] scanf ("%s", string); ^ 启用了完全警告,VS编译器的较早版本也不会标记警告)

此外,假设没有/Wall限定符,您想通过在const format-specifier中包含一个 field-width 修饰符来限制数组边界,以限制可以读取的字符数,例如

"%s"

注意:field-width修饰符不能是变量或命名常量,它必须是实际数值,使其成为使用幻数的唯一可接受的次数之一

因此,简而言之,您有两个主要问题:(1)您向编译器作出承诺,只将 scanf ("%7s", string); 视为常量,然后转而破坏了承诺;和(2)您写的内容超出了数组的范围。两者都调用未定义行为

答案 3 :(得分:-1)

  1. string被分配在堆栈上,因为它是const char[]而不是const char *。发生的情况是在堆栈上分配了一个char数组,并将字符串复制到该数组中。如果您使用const char *格式,则大多数编译器会将字符串放入二进制文件的只读段中,从而在您实际尝试写入字符串时导致程序崩溃。在堆栈上,没有这种保护。

  2. scanf使用可变参数,这意味着编译器不知道它将修改您传入的数组(无论如何都可以这样做,但是C标准并不要求这样做。) / p>