[英]Why does my program let me change a value of a const char in C?
I use CodeBlocks for Windows as my IDE.我使用 Windows 的 CodeBlocks 作为我的 IDE。 I'm having a problem in my program, and I would like to understand it.
我的程序有问题,我想了解一下。 In the program that I'm presenting, I declare a variable as a
char*
and my program turns it into a const char*
.在我展示的程序中,我将一个变量声明为
char*
并且我的程序将其转换为const char*
。 I would expect this char*
to be readonly, but I can modify for I don't know what reason.我希望这个
char*
是只读的,但我可以修改,因为我不知道是什么原因。
Here is my code:这是我的代码:
#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;
}
char *array[5][5]
defines array
as a modifiable 2D array of pointers to modifiable char
. char *array[5][5]
将array
定义为指向可修改char
的指针的可修改二维数组。 You can modify its elements and the strings they point to can also be modified.你可以修改它的元素,它们指向的字符串也可以修改。
Although " "
is a string literal that cannot be modified without invoking undefined behavior, the C Standard does not type it as const char[]
, but as char []
, which decays as a char *
when stored to an element of array
.尽管
" "
是一个字符串文字,如果不调用未定义的行为就无法修改,但 C 标准不会将其键入为const char[]
,而是作为char []
,当存储到array
的元素时,它会衰减为char *
。
When you write strcpy(array[0][0], array[1][0])
directly, you attempt to modify the memory pointed to by array[0][0]
with the contents of the string pointed to by array[1][0]
.当你直接写
strcpy(array[0][0], array[1][0])
时,你试图用array[1][0]
[ 指向的字符串的内容来修改array[0][0]
指向的 memory array[1][0]
。 Since you stored the address of a string literal in array[0][0]
, you are in fact trying to modify the memory where the string literal is stored, which has undefined behavior .由于您将字符串文字的地址存储在
array[0][0]
中,因此您实际上是在尝试修改存储字符串文字的 memory ,它具有未定义的行为。
Conversely, strdup(array[1][0])
allocates a copy of the string pointed to by array[1][0]
and returns a pointer which can be stored into array[0][0]
.相反,
strdup(array[1][0])
分配一个由array[1][0]
指向的字符串的副本,并返回一个可以存储到array[0][0]
的指针。 The next statement strcpy(array[0][0], array[1][0]);
下一条语句
strcpy(array[0][0], array[1][0]);
copies the original string on top of its copy, which is allowed and has no effect.将原始字符串复制到其副本之上,这是允许的并且没有效果。 You can remove this call to
strcpy()
.您可以删除对
strcpy()
的调用。
It is advisable to declare array
as const char *array[5][5]
, making strcpy(array[0][0], array[0][1])
produce a warning, but allowing array[0][0] = strdup(array[0][1])
.建议将
array
声明为const char *array[5][5]
,使strcpy(array[0][0], array[0][1])
产生警告,但允许array[0][0] = strdup(array[0][1])
。 Note however that once you mix string literals and allocated strings in this array, you will have no way to tell which should be freed and which must not.但是请注意,一旦您在这个数组中混合了字符串文字和分配的字符串,您将无法分辨哪些应该被释放,哪些不能。 All allocated memory will be released upon exit, but if the programs runs for a long time and keeps modifying this array, the inability to do proper memory management will force memory leaks and potentially unbounded memory use.
All allocated memory will be released upon exit, but if the programs runs for a long time and keeps modifying this array, the inability to do proper memory management will force memory leaks and potentially unbounded memory use.
Some compilers offer as an extension to type string literals as const char[]
(eg: gcc -Wwrite-strings
).一些编译器提供作为
const char[]
类型字符串文字的扩展(例如: gcc -Wwrite-strings
)。 With this option, you would get a warning when you store " "
into array[0][0]
.使用此选项,当您将
" "
存储到array[0][0]
时,您会收到警告。 Using this setting by default is good practice but may require extensive modifications in large programs, adding const
in many places, a process called const
poisoning .默认情况下使用此设置是一种很好的做法,但可能需要在大型程序中进行大量修改,在许多地方添加
const
,这个过程称为const
中毒。 It is not unusual to discover latent bugs during this process.在此过程中发现潜在错误并不罕见。 More generally, a pointer argument in a function definition should be declared
const
if it is not used directly or indirectly to modify the object it points to.更一般地,如果不直接或间接使用 function 定义中的指针参数来修改它指向的 object,则应将其声明为
const
。 It helps readers understand the code and may also help the compiler produce better executable code.它可以帮助读者理解代码,也可以帮助编译器生成更好的可执行代码。
You have a two-dimensional array of pointers.您有一个二维指针数组。
char * array[5][5];
^^^^^^
So these statements所以这些陈述
array[0][0] = "AAA";//Again I can change its value
array[1][0] = "BBB";//And again.
did not change the initially pointed strings.没有改变最初指向的字符串。 They changed the pointers that is their values.
他们更改了作为其值的指针。 Now the pointer
array[0][0]
points to the literal "AAA"
while the pointer array[1][0]
points to the literal "BBB"
.现在指针
array[0][0]
指向文字"AAA"
,而指针array[1][0]
指向文字"BBB"
。
In this statement在这份声明中
array[0][0] = strdup(array[1][0]);
you again did not change the string literal pointed to by the pointer array[0][0]
.您再次没有更改指针
array[0][0]
指向的字符串文字。 You changed the value of the pointer itself.您更改了指针本身的值。 After this statement the pointer
array[0][0]
points to the allocated dynamically memory returned by the function strdup
and the dynamically allocated array contains a copy of the string literal pointed to by the pointer array[1][0]
.在此语句之后,指针
array[0][0]
指向由 function strdup
返回的动态分配的 memory,并且动态分配的数组包含指针array[1][0]
指向的字符串文字的副本。 It is what the function strdup
does.这就是 function
strdup
所做的。
This statement这个说法
strcpy(array[0][0], array[1][0]);
is redundant because the dynamically allocated array pointed to by the pointer array[0][0]
already stores a copy of the string literal pointed to by the pointer array[1][0]
.是多余的,因为指针
array[0][0]
指向的动态分配的数组已经存储了指针array[1][0]
指向的字符串字面量的副本。
Pay attention to that for example a declaration like this请注意,例如这样的声明
const char *p = "AAA";
means that you may not change the pointed object that is the string literal "AAA".意味着您不能更改作为字符串文字“AAA”的指向 object。 But you may change the value of the pointer itself like for example
但是您可以更改指针本身的值,例如
p = "BBB";
This statement did not change the string literal "AAA"
.此语句没有更改字符串文字
"AAA"
。 It changed the value of the pointer p
that now points to the string literal "BBB"
.它改变了指针
p
的值,该指针现在指向字符串文字"BBB"
。
If you want that the pointer also would be non-modifiable then you should declare it like如果您希望指针也是不可修改的,那么您应该将其声明为
const char * const p = "AAA";
In this case a statement like this在这种情况下,像这样的声明
p = "BBB";
will generate a compiler error.会产生编译错误。
the array is an array of 25 pointers to char.该数组是一个包含 25 个指向 char 的指针的数组。
the place where the array points is the literal(s) in read-only memory.数组指向的位置是只读 memory 中的文字。
The following program shows that the pointer is what must be changed.以下程序显示指针是必须更改的。
#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;
}
the output of the program is:程序的output为:
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 modified the program to make it portable.我修改了程序以使其可移植。 This did not change the underlying concepts.
这并没有改变基本概念。
Note: a bare reference to an array IE "abcd" degrades to the address of the first byte of the array.注意:对数组 IE“abcd”的裸引用会降级为数组第一个字节的地址。 Therefore, the nested loop that is setting the 5x5 array is being set with addresses, not the value where the address points
因此,设置 5x5 数组的嵌套循环正在设置地址,而不是地址指向的值
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.