为什么我可以在这个例子中修改 char *?
Why am I able to modify char * in this example?
我无法理解 char*
的工作原理。
在下面的示例中,struses 由 main() 调用。
我创建了一个 buf 来存储 const 变量,因为我想制作 s1
的可修改副本,然后我只调用 sortString()
.
这个版本对我来说很有意义,因为我知道 char[]
可以修改:
#include "common.h"
#include <stdbool.h>
void sortString(char string[50]);
bool struses(const char *s1, const char *s2)
{
char buf[50];
strcpy(buf, s1); // <===== input = "perpetuity";
sortString(buf);
printf("%s\n", buf); // prints "eeipprttuy"
return true;
}
void sortString(char string[50])
{
char temp;
int n = strlen(string);
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
if (string[i] > string[j])
{
temp = string[i];
string[i] = string[j];
string[j] = temp;
}
}
}
}
但是,在这个版本中,我故意将类型更改为char*
,这应该是只读的。为什么我仍然得到相同的结果?
#include "common.h"
#include <stdbool.h>
void sortString(char *string);
bool struses(const char *s1, const char *s2)
{
char buf[50];
strcpy(buf, s1);
sortString(buf);
printf("%s\n", buf);
return true;
}
void sortString(char *string) // <==== changed the type
{
char temp;
int n = strlen(string);
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
if (string[i] > string[j])
{
temp = string[i];
string[i] = string[j];
string[j] = temp;
}
}
}
}
这就是为什么我认为 char *
是只读的。尝试修改 read[0]
:
后出现总线错误
char * read = "Hello";
read[0]='B';// <=== Bus error
printf("%s\n", read);
编译器调整该函数声明的数组类型参数的类型
void sortString(char string[50]);
指向元素类型的指针
void sortString(char *string);
所以例如这些函数声明是等价的并且声明相同的一个函数
void sortString(char string[100]);
void sortString(char string[50]);
void sortString(char string[]);
void sortString(char *string);
在此函数内
void sortString(char *string)
使用字符数组 buf
存储传递数组的副本(或通过指向它的指针传递的字符串文字)
char buf[50];
strcpy(buf, s1);
sortString(buf);
所以没有问题。 s1
可以是指向字符串文字的指针。但是字符串文字的内容被复制到正在更改的字符数组buf
中
至于这段代码
char * read = "Hello";
read[0]='B';
printf("%s\n", read); <=== still prints "Hello"
那么它有未定义的行为,因为您不能更改字符串文字。
来自 C 标准(6.4.5 字符串文字)
7 It is unspecified whether these arrays are distinct provided their
elements have the appropriate values. If the program attempts to
modify such an array, the behavior is undefined.
注意在C++中与C相反的字符串文字有常量字符数组类型。在 C 中也建议使用限定符 const
声明指向字符串文字的指针,以避免未定义的行为,例如
const char * read = "Hello";
顺便说一下,函数 sortString
对传递的字符串中的元素进行了冗余交换。最好用下面的方式来声明和定义
// Selection sort
char * sortString( char *s )
{
for ( size_t i = 0, n = strlen( s ); i != n; i++ )
{
size_t min_i = i;
for ( size_t j = i + 1; j != n; j++ )
{
if ( s[j] < s[min_i] )
{
min_i = j;
}
}
if ( i != min_i )
{
char c = s[i];
s[i] = s[min_i];
s[min_i] = c;
}
}
return s;
}
char *
不代表只读。 char *
仅表示指向 char
.
的指针
您可能被告知不能修改字符串文字,例如 "Hello"
。事实并非如此。一个正确的说法是 C 标准没有定义当您尝试修改字符串文字时会发生什么,并且 C 实现通常将字符串文字放在只读内存中。
我们可以使用 const
限定符定义对象,表示我们不打算修改它们并允许编译器将它们放置在只读内存中(尽管它不是必须的)。如果我们从头开始定义 C 语言,我们将指定字符串文字是 const
限定的,来自字符串文字的指针将是 const char *
.
但是,C刚开始开发的时候,并没有const
,字符串字面量产生的指针只是char *
。 const
限定符后来出现,更改字符串文字为 const
限定为时已晚,因为所有旧代码都使用 char *
.
因此,char *
可能指向不应修改的字符串文字中的字符(因为未定义行为)。但是char *
一般来说并不代表只读。
关于
char * read = "Hello";
read[0]='B';
printf("%s\n", read); // still prints "Hello"
您被 C 规范中的向后兼容性问题绊倒了。
字符串常量 是只读的。然而,char *
是指向 可修改 数据的指针。
字符串常量的类型 应该是 const char [N]
其中 N 是由常量的内容给出的 char
的数量加一。但是,const
在原始 C 语言(C89 之前)中并不存在。因此,在 1989 年,出现了大量使用 char *
指向字符串常量的代码。因此,C 委员会将字符串常量的类型设为 char [N]
,即使它们是只读的,以保持代码正常工作。
通过指向字符串常量的 char *
写入会触发未定义的行为;任何事情都可能发生。我本以为会发生崩溃,但写入被丢弃也不足为奇。
在 C++ 中,字符串常量的类型实际上是 const char [N]
,上面的片段将无法编译。一些 C 编译器有一个可选模式,您可以打开该模式将字符串常量的类型更改为 const char [N]
;例如,GCC 和 clang 有 -Wwrite-strings
命令行选项。将此模式用于新程序是个好主意。
您关于 char*
指向的区域不可修改的前提是错误的。这是完美的线:
char s[] = "abc"; // Same as: char s[4] = { 'a', 'b', 'c', 0 };
char *p = s; // Same as: char *p = &(s[0]);
*p = 'A';
printf("%s\n", p); // Abc
您出错的原因是您试图修改由字符串文字创建的字符串。这是未定义的行为:
char *p = "abc";
*p = 'A'; // Undefined behaviour
printf("%s\n", p);
人们通常会为此类字符串使用 const char *
。
const char *p = "abc";
*p = 'A'; // Compilation error.
printf("%s\n", p);
你的长例子可以简化为你的最后一个问题。
This is why I think char * is read only, get bus error after attempt
to modify read[0]
char * read = "Hello";
read[0]='B';
printf("%s\n", read); <=== Bus error
"Hello"
是一个 字符串文字 。尝试修改由总线错误显示的字符串文字。
您的指针正在引用不应修改的内存。
如何解决?您需要定义引用可修改对象的指针
char * read = (char []){"Hello"};
read[0]='B';
printf("%s\n", read);
因此,如您所见,将其声明为可修改并不是使其可修改。
我无法理解 char*
的工作原理。
在下面的示例中,struses 由 main() 调用。
我创建了一个 buf 来存储 const 变量,因为我想制作 s1
的可修改副本,然后我只调用 sortString()
.
这个版本对我来说很有意义,因为我知道 char[]
可以修改:
#include "common.h"
#include <stdbool.h>
void sortString(char string[50]);
bool struses(const char *s1, const char *s2)
{
char buf[50];
strcpy(buf, s1); // <===== input = "perpetuity";
sortString(buf);
printf("%s\n", buf); // prints "eeipprttuy"
return true;
}
void sortString(char string[50])
{
char temp;
int n = strlen(string);
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
if (string[i] > string[j])
{
temp = string[i];
string[i] = string[j];
string[j] = temp;
}
}
}
}
但是,在这个版本中,我故意将类型更改为char*
,这应该是只读的。为什么我仍然得到相同的结果?
#include "common.h"
#include <stdbool.h>
void sortString(char *string);
bool struses(const char *s1, const char *s2)
{
char buf[50];
strcpy(buf, s1);
sortString(buf);
printf("%s\n", buf);
return true;
}
void sortString(char *string) // <==== changed the type
{
char temp;
int n = strlen(string);
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
if (string[i] > string[j])
{
temp = string[i];
string[i] = string[j];
string[j] = temp;
}
}
}
}
这就是为什么我认为 char *
是只读的。尝试修改 read[0]
:
char * read = "Hello";
read[0]='B';// <=== Bus error
printf("%s\n", read);
编译器调整该函数声明的数组类型参数的类型
void sortString(char string[50]);
指向元素类型的指针
void sortString(char *string);
所以例如这些函数声明是等价的并且声明相同的一个函数
void sortString(char string[100]);
void sortString(char string[50]);
void sortString(char string[]);
void sortString(char *string);
在此函数内
void sortString(char *string)
使用字符数组 buf
存储传递数组的副本(或通过指向它的指针传递的字符串文字)
char buf[50];
strcpy(buf, s1);
sortString(buf);
所以没有问题。 s1
可以是指向字符串文字的指针。但是字符串文字的内容被复制到正在更改的字符数组buf
中
至于这段代码
char * read = "Hello";
read[0]='B';
printf("%s\n", read); <=== still prints "Hello"
那么它有未定义的行为,因为您不能更改字符串文字。
来自 C 标准(6.4.5 字符串文字)
7 It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.
注意在C++中与C相反的字符串文字有常量字符数组类型。在 C 中也建议使用限定符 const
声明指向字符串文字的指针,以避免未定义的行为,例如
const char * read = "Hello";
顺便说一下,函数 sortString
对传递的字符串中的元素进行了冗余交换。最好用下面的方式来声明和定义
// Selection sort
char * sortString( char *s )
{
for ( size_t i = 0, n = strlen( s ); i != n; i++ )
{
size_t min_i = i;
for ( size_t j = i + 1; j != n; j++ )
{
if ( s[j] < s[min_i] )
{
min_i = j;
}
}
if ( i != min_i )
{
char c = s[i];
s[i] = s[min_i];
s[min_i] = c;
}
}
return s;
}
char *
不代表只读。 char *
仅表示指向 char
.
您可能被告知不能修改字符串文字,例如 "Hello"
。事实并非如此。一个正确的说法是 C 标准没有定义当您尝试修改字符串文字时会发生什么,并且 C 实现通常将字符串文字放在只读内存中。
我们可以使用 const
限定符定义对象,表示我们不打算修改它们并允许编译器将它们放置在只读内存中(尽管它不是必须的)。如果我们从头开始定义 C 语言,我们将指定字符串文字是 const
限定的,来自字符串文字的指针将是 const char *
.
但是,C刚开始开发的时候,并没有const
,字符串字面量产生的指针只是char *
。 const
限定符后来出现,更改字符串文字为 const
限定为时已晚,因为所有旧代码都使用 char *
.
因此,char *
可能指向不应修改的字符串文字中的字符(因为未定义行为)。但是char *
一般来说并不代表只读。
关于
char * read = "Hello";
read[0]='B';
printf("%s\n", read); // still prints "Hello"
您被 C 规范中的向后兼容性问题绊倒了。
字符串常量 是只读的。然而,char *
是指向 可修改 数据的指针。
字符串常量的类型 应该是 const char [N]
其中 N 是由常量的内容给出的 char
的数量加一。但是,const
在原始 C 语言(C89 之前)中并不存在。因此,在 1989 年,出现了大量使用 char *
指向字符串常量的代码。因此,C 委员会将字符串常量的类型设为 char [N]
,即使它们是只读的,以保持代码正常工作。
通过指向字符串常量的 char *
写入会触发未定义的行为;任何事情都可能发生。我本以为会发生崩溃,但写入被丢弃也不足为奇。
在 C++ 中,字符串常量的类型实际上是 const char [N]
,上面的片段将无法编译。一些 C 编译器有一个可选模式,您可以打开该模式将字符串常量的类型更改为 const char [N]
;例如,GCC 和 clang 有 -Wwrite-strings
命令行选项。将此模式用于新程序是个好主意。
您关于 char*
指向的区域不可修改的前提是错误的。这是完美的线:
char s[] = "abc"; // Same as: char s[4] = { 'a', 'b', 'c', 0 };
char *p = s; // Same as: char *p = &(s[0]);
*p = 'A';
printf("%s\n", p); // Abc
您出错的原因是您试图修改由字符串文字创建的字符串。这是未定义的行为:
char *p = "abc";
*p = 'A'; // Undefined behaviour
printf("%s\n", p);
人们通常会为此类字符串使用 const char *
。
const char *p = "abc";
*p = 'A'; // Compilation error.
printf("%s\n", p);
你的长例子可以简化为你的最后一个问题。
This is why I think char * is read only, get bus error after attempt to modify read[0]
char * read = "Hello";
read[0]='B';
printf("%s\n", read); <=== Bus error
"Hello"
是一个 字符串文字 。尝试修改由总线错误显示的字符串文字。
您的指针正在引用不应修改的内存。
如何解决?您需要定义引用可修改对象的指针
char * read = (char []){"Hello"};
read[0]='B';
printf("%s\n", read);
因此,如您所见,将其声明为可修改并不是使其可修改。