C风格字符串,指针,数组

12

我不太理解C风格字符串是什么。提前祝你新年快乐。

我知道: 指针保存一个内存地址。对指针进行解引用将给出该内存位置的数据。

int x = 50;
int* ptr = &x;    //pointer to an integer, holds memory address of x

cout << "&x: " << &x << endl;  //these two lines give the same output as expected
cout << "ptr: " << ptr << endl;

cout << "*ptr: " << dec << (*ptr) << endl;  //prints out decimal number 50
                                           //added dec, so the program doesnt 
                //continue to printout hexidecimal numbers like it did for the 
                 //the memory addresses above
cout << "&ptr: " << &ptr << endl;  //shows that a pointer, like any variable,
                                  //has its own memory address

现在我不理解的是(以上内容是我的困惑来源): 声明字符串的方法有多种。我正在学习C++,但你也可以使用C风格的字符串(了解一下很好,尽管比C++字符串差)

C++:

string intro = "Hello world!"; 
//the compiler will automatically add a null character, \0, so you don't have to
//worry about declaring an array and putting a line into it that is bigger than 
//it can hold. 

C语言风格:

char version1[7] = {'H','i',' ','y','o','u','\0'};
char version2[] = "Hi you"; //using quotes, don't need null character? added for you?
char* version3 = "Hi you";

我在Version3遇到了麻烦。这里有一个指向char的指针。我知道数组名是指向数组第一个元素的指针。

cout << " &version3: " << &version3 << endl; //prints out location of 'H'
cout << " *version3: " << *version3 << endl; //prints out 'H'
cout << "  version3: " <<  version3 << endl; //prints out the whole string up to
                                             //automatically inserted \0

以前,在“我所知道的”部分,打印指针名称会打印出它所持有的地址。在这里,打印指针名将打印出整个字符串。双引号“Hi you”是否告诉程序:“嘿,我知道你是一个指针,并且已初始化为'H'的位置,但因为我看到这些双引号,所以在内存位置向前移动1个字节,并打印出直到达到\0的所有内容”(char大小为1个字节,因此移动1个字节)。

为什么打印指针会打印出字符串?以前,打印指针名称会打印出它初始化的内存地址。

编辑: cout << &version3 是否打印出“H”的位置还是指针version3的位置,该指针保存了“H”的内存地址?


相关:请参见我对https://dev59.com/5mIi5IYBdhLWcg3w9gJy#20794936的回答。 - Andrey Mishchenko
3
在这里加上“提前祝你新年快乐”有点随意。哈哈 - Fiddling Bits
2
我认为将C字符串称为“劣质”的说法有点过分,特别是考虑到其中一个(通常)是基于另一个实现的。 - Andrey Mishchenko
1
我建议你的新年决心是在发布问题之前先搜索网络和StackOverflow! - Thomas Matthews
请查看comp.lang.c FAQ,特别是第4节(指针),第6节(数组和指针)和第8节(字符和字符串)。 - Keith Thompson
3个回答

11

使用 cout 输出一个 char* 的方式与使用 cout 输出一个 int* 不同。为了兼容 C 风格字符串,重载的 << 运算符接受一个 char* 参数,并将其视为 C 风格字符串。如果想要输出 char* 变量所存储的内存地址,可以将其强制转换为 void*

是的,如果你编写以下任意一个:

char *s1 = "hi lol";
char s2[] = "hi haha";

在字符串的末尾会为你添加一个NUL(\0)终止符。这两者之间的区别在于,s1是一个指向字符串常量的指针,根据C标准,你不应该修改它的内容,而s2是一个数组,在栈上为你分配了一块内存块,其初始化为包含值"hi haha"的块,你可以自由地修改其内容。为数组分配的内存量恰好足以容纳用作初始化器的字符串,并且会自动确定,这就是为什么方括号可以为空的原因。

顺便提一下:在C中,如果你输入char s[3] = "abc";,那么s将被初始化为{'a','b','c'}没有 NUL 终止符!这是因为标准中有一个条款,即如果数组中有足够的空间,则在此上下文中的字符串将使用 NUL 终止符进行初始化(或类似的措辞)。在C++中则不是这种情况。更多信息请参见No compiler error when fixed size char array is initialized without enough room for null terminator

编辑,针对你补充的问题:如果你有char *s = "...",并且你cout << &s;,它将打印存储指针s的地址,而不是s所持有的地址(s所引用的字符串的第一个元素的地址)。


6
我知道数组名是指向数组中第一个元素的指针。

不是这样的。在大多数情况下它会隐式地被转换成一个指针。

在 "Hi you" 的双引号周围加上双引号是否会让程序知道:"嘿,我知道你是一个指针,并且你被初始化为'H'的位置,但因为我看到这些双引号,所以在内存位置上向前跳过1个字节并打印直到你遇到\0为止"?

不会。 类型才是重要的。

std::ostream::operator<< 有一个通用指针(void *)的重载和一个 const char * 的重载。所以当你写 cout << "some char array or pointer";时,它将调用那个重载,而不是其他指针类型的重载。这个重载的行为不同:它会打印出直到NUL终止符的所有内容,而不是指针的数值。


4
你在这里提出了两个问题,其中一个更加普遍,我会首先回答那个。
指针是内存中位置的“句柄”(称为地址)。该位置的内容是一个值。该值的解释取决于指针的“类型”。可以这样理解:地址就像房子的位置,例如10 Main Street。 10 Main Street的内容就是该地址处房子的内部信息。在C(和C ++)中,可以这么说:
int *p;

一个可以保存整数地址的变量。
int x;

这是一个整数类型的变量。

p = &x;

通过将指向x的地址存储在p中,使p“指向”x。

x = 10;

将值10存储在x中。

*p == x

因为*p表示将p的内容作为一个值使用,然后与x进行比较。

p == &x

因为&x告诉我们将x的地址用作值,然后将其与类型为指针的变量p进行比较。

C(和C ++)中表示零个或多个字符序列的字符串由字符"和"表示。在内存中,这些值按顺序存储,并且会自动以尾随的空字节终止,它是一个具有值0的字节。假设您正在使用8位ASCII字符编码,“Hello,world!”字符串在内存中存储为:

65 101 108 108 111 44 32 87 111 114 108 33 0

您可以使用以下代码片段自行查看这一点:

char *p = "Hello, World!";
int len = strlen(p);
int i;

for (i = 0; i <= len;  ++i)
{
    std::cout << std::dec << (int) p[i] << ' ';
}
std::cout << std::endl;

由于编译器会自动向常量字符串添加空(零字节),因此以下两个数组的大小相同:

char a1[7] = { 'H', 'i', ' ', 'y', 'o', 'u', '\0' };
char a2[] = "Hi you";

std::cout << strcmp(a1, a2) << std::endl

简而言之,'C风格字符串'是由字符数组构成的,并以空字符结尾,这样就回答了你的第一个问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接