用字符指针输入 vs. 字符数组

5
考虑这段代码。
#include<stdio.h>
int main(void)
{
   char* a;
   scanf("%s",a);//&a and &a[0] give same results-crashes 
   printf("%s",a);
   return 0;
}

为什么这段代码会导致崩溃?而使用字符数组的代码却能正常工作?
#include<stdio.h>
int main(void)
{
   char a[100];
   scanf("%s",&a[0]);//works fine
   printf("%s",a);
   return 0;
}

字符数组和指针的区别是什么?但我知道指针只是指向第一个元素,即&a[0]应该可以正常工作,但上面的代码对于a、&a和&a[0]都会崩溃。我想了解的主要内容是,如果我坚持使用scanf,如何输入字符指针?如果我的表述不清楚,请见谅。谢谢!


请学习如何标记您的代码,使其易于阅读。 - AProgrammer
抱歉,我是一个新手 SO。我再次道歉。 - Ashish Yadav
抱歉,这不是作业,否则我会打上标签的。 - Ashish Yadav
我建议在您的两个示例中更改数组/字符串名称,这样回答时更容易区分它们。 - JXG
现在是指出“数组并不总是等同于指针!”的绝佳时机。 - Michael Foukarakis
6个回答

9
因为char* a;为字符指针在栈上分配了空间,而char a[100];则为100个字符分配了空间。
在前一种情况下,您需要为指针分配一些实际的内存。导致崩溃的原因是没有初始化,并且当您使用scanf时,数据将被写入其中。如果只使用a,那么我们称之为崩溃类型一。
如果您使用&a,它将会覆盖指针本身,这将使您得到一个指向不知道哪里的指针,导致崩溃类型二。这是假设您输入的字符数没有超过指针的溢出 - 这将破坏调用堆栈,导致另一种崩溃情况(类型三)。
作为建议,请不要使用没有边界检查的输入函数,除非您绝对控制所提供给它们的数据。即使是使用char[100],有人也可以通过输入超过缓冲区容量的字符来导致堆栈溢出。
我发现最好的方法是使用fgets(它可以限制读取的字符数),结合sscanf使用(虽然如果您只获取一个字符串,则不真正需要sscanf)。

4

a没有指向任何东西。实际上,它未初始化,因此指向某个地方,但不是有效的地方。

您可以像您已经尝试过的那样,在堆栈上提供存储:

#include <stdio.h>
int main()
{
  char x[100];
  char *a = x; // a points to the storage provided by x on the stack
  scanf("%s",a); // should work fine
  printf("%s",a);
  return 0;
}

请注意,printf("%s",x);a 指向的是同一个内存地址 x,所以你的字符串将会存在这个地址上。
或者你可以使用 malloc 让内存管理来处理它。
#include <stdio.h>
#include <stdlib.h>
int main()
{
  char *a = malloc (100*sizeof(char)) // a points to the storage provided by malloc
  if (a != null)
    {
      perror ("malloc"); // unable to allocate memory.
      return 1;
    }
  scanf("%s",a); // should work fine
  printf("%s",a);
  free (a); // just remember to free the memory when you're done with it.
  return 0;
}

HTH.

编辑
此外,在评论开始之前...这是非常不安全的代码,但我猜你只是想理解指针,所以我试图给你一个正确方向的提示。(如果不是,你需要研究读取有限数量的数据,确保它适合你的缓冲区,如果它来自文件以外的其他源头,你需要确保你已经获取了所有数据,等等)。


2
在使用 a 前,您需要使用 malloc() 为其分配内存。

@qrdl 我想了解关于内存崩溃的原因。 - Ashish Yadav
3
如果没有分配内存,你的变量 a 将指向内存中的某个随机位置,很可能不属于进程的内存空间。因此,试图访问这块内存会导致一个分段错误。为避免此问题,应该先分配内存给 a - qrdl
int *a; 只是声明了一个指针,它并没有分配任何内存空间。scanf 假设你提供的指针指向一个足够大的内存空间以满足其需要。由于你从未将 a 指向任何地方,程序会崩溃。 - T.E.D.

2

scanf需要一个地方存放读取的字符。您可以传递一个指向字符数组的指针(您在第二个示例中所做的),或者一个指向以其他方式获得的内存的指针(如malloc())。 在您的第一个示例中,您传递了一个未初始化的指针,因此您观察到的问题。


@aprogrammer 你的意思是我需要像这样初始化指针吗? char* a="" - Ashish Yadav
你需要使用指向可写内存的指针值来初始化指针变量。你在这里提出的是初始化它,这比你之前做的好,但你不能写入那个内存(即使你能写入,它可能太小了)。 - AProgrammer

1
在你的第一段代码中,a 是一个未初始化的字符指针。 你试图写入未分配的内存或保留的内存。
你需要使用 malloc() 为输入字符串分配内存。
int main(void) {
  char* s;
  /* ... */
  s = malloc(100 * sizeof(*s));
  if (s == NULL) {
    return 1;
  /* ... */
}

1
在第一个例子中,a 是一个“悬空指针” - 它是一个包含某个随机内存位置地址的指针。在大多数情况下,访问此地址将导致崩溃。底线:您需要为 a 分配内存。

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