我创建了一个具有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
答案 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)
string
被分配在堆栈上,因为它是const char[]
而不是const char *
。发生的情况是在堆栈上分配了一个char
数组,并将字符串复制到该数组中。如果您使用const char *
格式,则大多数编译器会将字符串放入二进制文件的只读段中,从而在您实际尝试写入字符串时导致程序崩溃。在堆栈上,没有这种保护。
scanf
使用可变参数,这意味着编译器不知道它将修改您传入的数组(无论如何都可以这样做,但是C标准并不要求这样做。) / p>