char 和 char*(指针)

14

我想了解指针的工作原理,所以我创建了这个小程序。首先我创建了一个指向char类型的p指针。

第一个问题是:如果我创建一个指针,它的值是一个内存地址(如果我将其指向非指针对象),但在我的例子中,它是"haha"。为什么char*会这样工作?我如何使用cin >> p给它添加值?

我的第二个问题是,我创建了一个q char,它具有我创建它时*p指针的值。但它的值和地址也是"h",为什么?它必须是这个char对象的内存地址!这没意义 :D (mingw - gcc)

#include <iostream>

int main() 
{
 /* char *p;
    cin >> p;                      //forexample: haha */

    char * p = "haha";
    char q = *p;
    std::cout << "&q = " << &q << std::endl;   //&q = h
    std::cout << "q  = " <<  q << std::endl;   //q = h

    return 0;
}

更多信息:如果我先分配内存 char a [100];然后 char *p = a; 那么 &q = h»ŢĹ,显示的是“h”和一些混乱的东西。但它应该是一个内存地址!我的问题是,为什么它不是地址呢?


2
这不安全。p没有任何内存供您输入。 - chris
5个回答

15

char* p; 看作是内存中的地址。由于您没有对这个指针进行初始化,因此它不指向任何东西,您无法使用它。

为了安全起见:
要么将指针初始化为零:

char *p = 0; // nullptr in C++11

或者初始化为一些自动值

void foo() {
  char a[100];
  char *p = a;
}

或全局内存:

char a[100];
void foo() {
  char *p = a;
}

或者获取动态内存:

char* p = new char [100];

如果p不是NULL,那么你可以使用它来读取数据并从它读取...


对于你误解的operator >> (std::istream&, char* p)。该运算符期望p指向某个内存(自动、全局、动态-无所谓)- 它不会自己分配内存。它只是从输入流中读取字符直到出现空格,并将其复制到p指向的内存中 - 但是p必须已经指向某个内存。


关于获取char q;的地址。当然你可以获取q的地址:&q,它的类型是char* p。但是&qp不同,而且这个q=*p只是将p指向的第一个字符复制到q中,它不能改变q的地址 - 它的地址是不可更改的。对于cout << &q - operator << (ostream&, char* p)期望p指向以NULL结尾的字符串 - 而&q指向包含"H"的内存,但是这个字符之后是什么没有人知道 - 所以你将在屏幕上得到一些垃圾。使用cout << q打印单个字符。


所以,你说cin不分配内存,那我的“哈哈”在哪里?一个指针怎么能包含整个字符串?q是一个简单的char变量,所以它可以包含p的任何字符,而我没有分配我的“哈哈”文本,这就是为什么我的“h”也没有内存地址的原因吗?这很清楚,但我有一个问题:当我将字符复制到q(char q = *p)时,它实际上并没有复制,只是指向那个未分配的数据?所以问题无处不在,我的哈哈文本没有被分配,所以我不能从中获得任何地址? - Iburidu
2
因为您没有给 p 分配任何值,甚至不是 0 - 所以它指向某个随机地址。您将 "haha" 复制到程序中的某个随机内存中 - 这可能会导致任何事情发生,这是未定义的行为 - 它甚至可能会导致向您的老板发送电子邮件,告诉他您有更好的工作 - 但也可能发生这种情况,即此随机内存未被使用 - 因此,您的程序似乎正常工作。 - PiotrNycz
是的,但在我的例子中,p等于一个内存地址。我用a[100]创建了你的例子,而p是“哈哈”,不等于一个内存地址,但它应该是! - Iburidu
考虑以下代码:char a[100]; char *p = a; strcpy(p, "HA HA"); p 不等于字符串常量 "HA HA" 的地址 (p != "HA HA"),但它仍然等于 a (p == a)。 但是,strcmpy(p, "HA HA") == 0,这意味着字符已从 "HA HA" 复制到了以 p 表示的 a[100] 中。 - PiotrNycz
p==ap==&a[0] 是因为 a == &a[0]。而且 (*p == 'H') 和 (a[0] == 'H') - PiotrNycz
显示剩余4条评论

4

关于指针,需要学习和记住的第一件事情是确保为它们分配内存,否则你的程序将无法正常运行。

为了分配内存,你需要修改代码,以便“cin”可以将用户输入写入已分配的内存中:

int main() {
    char *p = new char[1024];      // allocate memory so cin
    cin >> p;                      //forexample: haha
    char q = *p;
    cout << "&q = " << &q << endl; 
    cout << "q = " << q << endl;   
    return 0;
}

现在,字符指针是一个变量,它指向内存中包含一组字符的位置(不一定是一个字符,可能是多个字符,也可能没有字符,例如特殊值null),而字符变量实际上只持有一个字符(不是一组字符)。
处理指针时的基本运算符是&(取地址)和*(取值)。&检索变量的地址,因此如果我们有[char q;],那么[&q]将是一个字符指针。另一方面,*检索给定指针处的内存中的值,因此如果我们有[char *p;],那么[*p]将是p指向的内存中的字符。
现在回到你的例子,为了说明,我们在注释中进行了说明。
int main() {
    // allocate a place of 1024 character in memory
    // and let p points to that place
    char *p = new char[1024];      

    // call cin to read input from the user and save
    // it in memory at the location pointed to by p
    // NOTE: cin would put an extra NULL character 
    // at the end to terminate the string
    cin >> p;                      //forexample: haha

    // Now p would be pointing to a piece of memory like this
    // [h] [a] [h] [a] [\0]

    // use the value at operator to de-reference the pointer
    // p, the result would be a single character which 
    // will be the first character in memory p is pointing at
    char q = *p;

    // printing the value of (the address of q)
    // Note you have a problem here as cout will
    // be handling the memory starting at the address 
    // of q as a string while it is not, so you
    // will get a string starting with "h" and followed
    // by whatever is there in memory by the time of execution
    cout << "&q = " << &q << endl;

    // printing the value of the character q
    cout << "q = " << q << endl; 
    return 0;
}

我希望它有所帮助


是的,很清楚,如果我为p分配空间,那么它将向&q写入“h和一些混乱”,但如果我不为p分配空间,则仅写入“h”,这与*p相同。所以q=&q?:D但我认为PiotrNycz是正确的,因为“哈哈”没有被分配,所以它没有内存地址,因此&q(即p)无法指向任何地方,但为什么它在“h”之后停止呢? - Iburidu
1
@Iburidu,你的机器/编译器上发生的特定情况并不是普遍情况。在未分配的内存空间中存储数据的结果是不可预测的。在你的情况下,内存已经被填满了零,所以仅仅是偶然的机会使得打印以“&q”开头的字符串能够正确输出“h”,没有其他更多的原因。 - Hisham
好的,我通过添加char a[100]来分配内存;int* p=a; 但是为什么p == "haha",如果它应该是一个内存地址(&a[0])呢? - Iburidu
在C++中,数组被实现为指针,因此大多数情况下可以互换使用数组和指针。例如:int *p = new int[100]; 然后你可以做 p[0]=1; p[1]=50; ...等等。 - Hisham
@Iburidu 回到你的例子:cout会打印出字符串"haha",因为它会注意到你正在尝试打印一个char*...如果你想打印地址,你可以将其强制转换为(void *)或使用语句printf("%p", p); - Hisham

1

你应该有类似这样的东西:

#include <iostream>
using namespace std;

在你的程序顶部。或者可以省略 using 并引用 std::cinstd::cout

int main() {
    char *p;

p 是一个指针,但你还没有初始化它,所以它可能指向任何地方或者根本不指向任何地方。你需要对它进行初始化,例如:

p = new char[100];

...

    cin >> p;                      //forexample: haha

如果您已将p初始化为指向某个位置,那么这是可以的——除非您输入太多数据,否则可能会溢出它所指向的缓冲区。对于像这样的简单测试程序来说,这是可以接受的,但在实际应用中,您需要采取措施来避免这种情况。

    char q = *p;
    cout << "&q = " << &q << endl; //&q = h

&qchar* 类型,指向 char 的指针。将 char* 值发送到 cout 不会打印指针的值(内存地址);它会打印它所指向的字符串。(尽管我在运行时得到了一些奇怪的结果;可能是我漏掉了什么。)如果您想查看指针的值,请将其转换为 void*

count << "&q = " << (void*)&q << endl;

(或者您可以使用其中一种特定于C++的转换;我不确定哪种最好。)

    cout << "q = " << q << endl;   //q = h

这里的q只是一个char,因此它会将其值作为字符打印出来:h

    return 0;
}

是的,在我的代码中,我已经使用了前两行,但这很明显,所以我没有在这里放。我理解了它,但是cout << &q 必须是 cout << p,对吗?可实际上并不是。&q = "h" 而 p = "haha"。 - Iburidu

0
更好的方法是:
    int arraySize;
    cin>>arraySize;
    char *a=new char[arraySize];
    cin >> a;

0

字符串文字将存储在.rdata部分中,该部分将是只读的,因此使用std cin读取它会导致程序崩溃。

第二件事,当您编写以下内容时:

char *p = "some text";

那么指针p将指向一个已分配且只读的内存,因为rdata部分肯定会被Windows加载器分配和准备,但正如我所说,.rdata无法修改

然后当你写下这个:

char q = *p;

你只将 q 赋值给了 p 的第一个字符,因为 * 返回指针当前指向的值,所以如果你尝试这样做:

++p;
q = *p;

q将保存字符'a'而不是'h',因为指针是一个地址,它指向从第一个字符开始的一些字符,所以增加1会使指针指向第二个字符,q将保存这个值


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