Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/63.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
到底有多少种方法可以在C中声明字符串?(需要有关修饰符和范围的帮助)_C_String - Fatal编程技术网

到底有多少种方法可以在C中声明字符串?(需要有关修饰符和范围的帮助)

到底有多少种方法可以在C中声明字符串?(需要有关修饰符和范围的帮助),c,string,C,String,所以我想知道到底有多少种方法可以声明字符串。我知道类似的问题已经被问过好几次了,但我认为我的重点是不同的。作为C语言的初学者,我想知道哪种声明方法是正确的和更可取的,这样我就可以坚持使用它了 我们都知道可以用以下两种方法声明字符串 char str[] = "blablabla"; char *str = "blablabla"; 在读了堆栈溢出中的一些Q&A之后,我被告知字符串文本被放在内存的只读部分。因此,为了创建可修改的字符串,您需要在堆栈中创建字符串长度为+1的字符数组,并将每个字符复

所以我想知道到底有多少种方法可以声明字符串。我知道类似的问题已经被问过好几次了,但我认为我的重点是不同的。作为C语言的初学者,我想知道哪种声明方法是正确的和更可取的,这样我就可以坚持使用它了

我们都知道可以用以下两种方法声明字符串

char str[] = "blablabla";
char *str = "blablabla";
在读了堆栈溢出中的一些Q&A之后,我被告知字符串文本被放在内存的只读部分。因此,为了创建可修改的字符串,您需要在堆栈中创建字符串长度为+1的字符数组,并将每个字符复制到数组中。 这就是第一行代码所做的

但是,第二行代码所做的是创建一个字符指针,并将位于内存只读部分的第一个字符的地址分配给指针。因此,这种声明不涉及逐个字符的复制

如果我错了,请告诉我

到目前为止,这似乎是可以理解的,但真正让我困惑的是修饰语。例如,一些人建议

const char *str = "blablabla";
而不是

char *str = "blablabla";
因为如果我们做类似的事情

*(str + 1) = 'q';
static const char *str = "blablabla";
它将导致可怕的未定义行为

但有些人甚至更进一步,提出类似的建议

*(str + 1) = 'q';
static const char *str = "blablabla";
并说这将把字符串放入静态内存 它永远不会被修改以避免未定义的行为

那么,哪种方法才是声明字符串的正确方法呢

此外,我还想知道声明字符串时的作用域

比如说,

(您可以忽略这些示例,正如其他人所指出的,它们都有问题)

#包括
字符**strPtr();
int main()
{
printf(“%s”,*strprtr());
}
字符**strprtr()
{
字符str[]=“blabla”;
char*charPtr=str;
字符**strprtr=&charPtr;
返回strPtr;
}
将打印一些垃圾值

但是

#包括
字符**strPtr();
int main()
{
printf(“%s”,*strprtr());
}
字符**strprtr()
{
char*str=“blabla”;
/*正如other所指出的,我返回的是一个局部变量的地址*/
/*这会导致未定义的行为*/
字符**strprtr=&str;
返回strPtr;
}
会很好用的。(不,不是,这是未定义的行为。)

我想我应该把它作为另一个问题。
这个问题太长了。

您的许多困惑都来自于对C和字符串的一种常见错误理解:这一点在您的问题的标题中有明确说明。C语言没有本机字符串类型,因此实际上有方法在C中声明字符串

花些时间阅读能很好地解释这一点的文章

这一点很明显,因为您不能(明智地)执行以下操作:

char *a, *b;
// code to point a and b at some "strings"
if (a == b)
{
   // do something if strings compare equal
}
if
语句将比较指针的值,而不是它们所寻址的内存内容。因此,如果
a
b
指向内存的两个不同区域,每个区域都包含相同的数据,那么比较
a==b
将失败。如果
a
b
拥有相同的地址(即指向内存中的相同位置),则比较的唯一评估结果为“真”(即,非零)

C语言有一个惯例,还有一些语法上的糖分,使生活更容易

约定是“字符串”表示为以值0结尾的
char
序列(通常称为NUL,由特殊字符转义序列
'\0'
表示)。这种约定来自原始标准库(70年代)的API,它提供了一组字符串原语,如
strcpy()
。这些原语对于在该语言中做任何真正有用的事情都是如此重要,以至于通过添加语法糖(这都是在该语言脱离实验室之前),程序员的生活变得更加轻松

句法上的甜点在于“字符串文字”的存在:不多也不少。在源代码中,包含在双引号中的任何ASCII字符序列都被解释为字符串文字,编译器生成字符的副本(在“只读”内存中)加上一个终止NUL字节以符合约定。现代编译器检测重复的文本,只生成一个副本——但这不是我上次查看的标准要求。因此:

assert("abc" == "abc");
可以引发断言,也可以不引发断言——这强化了C没有本机字符串类型的说法。(对于这一点,C++也不具有字符串类!)< /P> 这样,如何使用字符串文字来初始化变量

您将看到的第一种(也是最常见的)形式是

char *p = "ABC";
这里,编译器在程序的“只读”部分中留出4个字节(假设
sizeof(char)==1
)的内存,并使用
[0x41,0x42,0x43,0x00]
对其进行初始化。然后用该数组的地址初始化
p
。您应该注意,这里正在进行一些
const
转换,因为字符串文本的底层类型是
const char*const
(指向常量字符的常量指针)。这就是为什么通常建议您将其写为:

const char *p = "ABC";
这是一个“指向常量字符的指针”——表示“指向只读内存的指针”的另一种方式

接下来的两种形式使用字符串文字来初始化数组

char p1[] = "ABC";
char p2[3] = "ABC";
请注意,这两者之间有一个关键的区别。第一行创建一个4字节数组。第二个创建一个3字节的数组

在第一种情况下,与前面一样,编译器创建一个4字节的常量数组,其中包含
[0x41、0x42、0x43、0x00]
。请注意,它添加了尾随NUL以形成“C字符串”。然后,它保留四个字节的RAM(在堆栈上用于局部变量,或在“静态”内存中)
char p1[] = "ABC";
char p2[3] = "ABC";

printf ("p1 = %s\n", p1); // Will print out "p1 = ABC"
printf ("p2 = %s\n", p2); // Will print out "p2 = ABC!@#$%^&*"