尝试在结构上使用 scanf 时出现分段错误 [英] Segmentation Fault when trying to use scanf on a struct

查看:20
本文介绍了尝试在结构上使用 scanf 时出现分段错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对 c 很陌生,现在我也很沮丧.这是我的代码:

I'm pretty new to c and I'm pretty frustrated at the moment as well. Here's the code I have:

typedef struct {

char* fName;
char* lName;
char* pNum;
char* address;
char* email;
} contactInfo;

void addContact(){
contactInfo *contact;
contact = (contactInfo *) malloc (sizeof(contactInfo));

printf("
[Add a contact]
First Name: ");
scanf("%s", contact->fName);
printf("%s", contact->fName);
}

由于某种原因,当我为 scanf 输入一个值时,它会给我一个分段错误.如果我尝试添加 &在contact->fName前面我也得到一个错误.

For some reason when I enter a value for the scanf it gives me a segmentation fault. If I try to add a & in front of the contact->fName I get an error as well.

代码有什么问题?

推荐答案

首先,别担心 - 开始 C 的挫折是正常的 :)

First off, don't worry - frustration is normal with beginning C :)

既然你说你是初学者,我已经写了一个很长的答案,解释了你可能想要做的一些其他改进.对不起,如果我涵盖了一些你已经知道的事情.总结如下:

Since you say you're a beginner, I've written a pretty long answer that explains some other improvements you might want to make. Sorry if I cover some things you already know. Here's a summary:

  1. 您需要为 char* 分配一些空间以指向 (这是导致崩溃的原因)
  2. 确保检查 malloc 的返回值
  3. 确保要求 scanf() 只读取字符串中可以容纳的字符数.
  4. 无需从 malloc 转换返回值.
  5. 记得 free() 任何你已经 malloc 的东西.
  1. You'll need to allocate some space for the char*s to point to (this is what's causing the crash)
  2. Make sure you check the return value from malloc
  3. Make sure ask scanf() to only read as many characters as you can hold in your string.
  4. No need to cast the return value from malloc.
  5. Remember to free() anything you've malloc-ed.

<小时>

您需要为 char*s 分配一些空间以指向

在 C 中,char* 表示指向 char 的指针".char* 通常用于字符串,因为您可以像数组一样索引指针 - 例如,假设:


You'll need to allocate some space for the char*s to point to

In C, a char* means "a pointer to a char". char* is commonly used for strings, since you can index pointers like they were arrays - for example, assuming:

 char *a = "Hello";

那么,a[1]的意思是a所指向的char之后的第一个char,在本例中为"e';

Then, a[1] means "the first char after the char pointed to by a, in this case 'e';

你有这个代码:

contactInfo *contact;
contact = (contactInfo *) malloc (sizeof(contactInfo));

此时,您已经声明了一个指向contactInfo 结构的指针,并为其分配了正确大小的内存.然而,结构中的指针目前不指向任何东西——所以当你的程序调用 scanf() 时它会崩溃.您还需要为即将阅读的字符分配空间,例如:

At this point, you've declared a pointer to a contactInfo struct, and allocated memory of the right size to it. However, the pointers inside the structure currently don't point to anything - so your program is crashing when it calls scanf(). You need to also allocate space for the characters you're about to read, for example:

contact->fName = malloc(sizeof(char) * 10);

将为 10 个字符分配空间.您需要为结构中的每个 char* 执行此操作.

Will allocate space for 10 chars. You'll need to do this for every char* in the struct.

我不想让你太担心的几个旁白:

A couple of asides that I don't want you to worry about too much:

  • 在 C 中,sizeof(char) 始终为 1,因此您可以编写 malloc(10),但在我看来,它的可读性较差.
  • 你也可以这样做:

  • In C, sizeof(char) is always 1, so you could have written malloc(10), but in my opinion that's less readable.
  • You can also do something like:

contact->fName = malloc(sizeof(*(contact->fName)) * 10);

这对于 fName 类型的变化是稳健的 - 您将始终为 fName 指向的任何 10 个分配足够的空间.

This is robust to changes in the type of fName - you'll always allocate enough space for 10 of whatever fName points to.

现在回到正轨 - 您还应该检查 malloc() 的返回值:

Back on track now - you should also check the return value from malloc():

contact->fName = malloc(sizeof(char) * 10);
if(contact->fName == NULL) {
   // Allocation failed 
}

在某些情况下,您可能能够从失败的分配中恢复(例如,尝试再次分配,但要求更少的空间),但开始:

In some cases, you might be able to recover from a failed allocation (say, trying to allocate again, but asking for less space), but to start with:

contact->fName = malloc(sizeof(char) * 10);
if(contact->fName == NULL) {
   printf(stderr,"Allocation of contact->fName failed");
   exit(EXIT_FAILURE);
}

应该没问题.许多程序员会为 malloc() 编写一个包装器,为他们进行错误检查,这样他们就不必再担心了.

Is probably fine. Many programmers will write a wrapper for malloc() that does this error checking for them, so that they no longer have to worry about it.

请注意,一旦您在 fName 中分配了 10 个字符,scanf() 可能会读取太多字符.您可以通过编写 "%Ns" 来明确告诉 scanf 限制,其中 N 是字符串中的最大字符数(最后的空终止符减去 1).所以,如果你分配了 10 个字符,那么你应该写:

Note that once you've allocated say 10 char in fName, scanf() might read too many characters. You can explictly tell scanf the limit by writing "%Ns" where N is the maximum number of characters in your string (minus 1 for the null terminator at the end). So, if you've allocated 10 characters, then you should write:

scanf("%9s", contact->fName);

无需从 malloc 转换返回值.

最后一点 - 您不需要在 C 中强制转换 malloc 的返回值,所以我会大概写:

 contact =  malloc (sizeof(contactInfo));

记得 free() 你分配的任何东西

您可能已经这样做了,但是每次您 malloc() 任何东西时,请确保在完成后您的代码中有相应的 free()用它.这告诉操作系统它可以取回内存.所以,如果你有什么地方

Remember to free() anything you've malloced

You might be doing this already, but every time you malloc() anything, make sure you have a corresponding free() in your code once you're done with it. This tells the operating system it can have the memory back. So, if you've got somewhere

 contact =  malloc (sizeof(contactInfo));

稍后,当您处理完该联系人后,您将需要以下内容:

Later on, when you're done with that contact, you'll need to have something like:

 free(contact);

避免内存泄漏.

一旦你释放了一些东西,你就不能再访问它了.因此,如果您在联系人中分配了字符串,则必须首先释放它们:

As soon as you've freed something, you aren't allowed to access it any more. So, if you've malloced strings inside the contact, you'll have to first free them:

 free(contact->fName);  // doing this in the other order might crash
 free(contact);  

关于免费的一些事情要记住:

A few things to remember about free:

  1. 你不能两次释放任何东西.为避免这种情况,一个好的做法是编写:

  1. You can't free anything twice. To avoid this, a good practice is to write:

 if(contact != NULL) free(contact); 
 contact = NULL;

如果你这样写,那么你还需要在创建它们时将所有指针初始化为 NULL.当您创建其中包含指针的结构时,一种简单的方法是使用 calloc() 而不是 malloc() 来创建结构,因为 calloc() 返回的内存总是为零.

If you write this way, then you'll need to also initialise all pointers to NULL when you create them. When you create structs that have pointers in them, an easy way to do this is to use calloc() instead of malloc() to create the struct, since calloc() returns memory which is always zeroed.

当你的程序退出时,所有的内存都会被释放回操作系统.这意味着您在技术上 不需要free() 程序生命周期中存在的东西.不过,我建议养成释放所有分配的习惯,否则你会忘记重要的一天.

When your program exits, all memory is released back to the operating system. This means you don't technically need to free() things that are around for the lifetime of the program. However, I'd recommend getting into the habit of freeing everything you've malloced, because otherwise you'll forget one day when it's important.

<小时>

进一步改进

正如评论者在另一个答案中指出的那样,使用幻数(代码中硬编码的数字)通常是不好的做法.在我上面给你的例子中,我把10"硬编码到程序中作为字符串的大小.但是,最好执行以下操作:


Further improvements

As a commenter points out on another answer, using magic numbers (numbers hard coded in your code) is generally bad practice. In the examples I've given you above, I've hard coded "10" into the program as the size of the string. However, it's better to do something like:

#define FNAME_MAX_LENGTH 10

然后再去:

malloc(sizeof(char) * FNAME_MAX_LENGTH);

这样做的好处是,如果你需要在任何地方改变字符串的大小,你可以只在一个地方改变它.它还可以防止您不小心在一个地方输入 100 或 1,从而导致潜在的严重且难以发现的错误.

This has the advantage that if you need to change the size of the string anywhere, you can change it in just one place. It also prevents you accidentally typing 100 or 1 in one place, causing a potentially serious, hard-to-find bug.

当然,既然您已经获得了长度的 #define,那么您需要更新我们指定长度的 scanf() 调用.但是,由于 scanf() 需要长度 - 1,因此您将无法使用 #define 来指定长度(至少,不能以任何可读性好大大地).

Of course, now that you've got a #define for the length, you'll need to update the scanf() call where we specify the length. However, since scanf() needs the length - 1, you won't be able to use the #define to specify the length (at least, not in any nice readable way).

因此,您可能会对 fgets(),它读取到指定长度 -1(或直到行尾 - 以先到者为准).然后你可以这样做:

Consequently, you might be interested in fgets(), which reads up to a specified length -1 (or until the end of the line - whichever is first). You could then do:

fgets(contact->fName,FNAME_MAX_LENGTH,stdin);

而不是 scanf() 调用.进行此更改的另一个充分理由是 scanf() 可以是 种类痛苦.

instead of the scanf() call. Another good reason to make this change is that scanf() can be kind of a pain.

所以,除了上面的总结:

So, in addition to the summary above:

  1. 对字符串的长度使用#define 可以避免出现问题,并使以后更改代码更容易.
  2. fgets()scanf() 更容易使用,并且更适合使用 #define 作为字符串长度.
  1. Using a #define for the length of the string can avoid problems and make it easier to change your code later.
  2. fgets() is easier to use than scanf(), and is more compatible with using a #define for the string length.

这篇关于尝试在结构上使用 scanf 时出现分段错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆