如何在C结构体中处理字符串字段?

31

我在用C语言基于单链表创建数据库时遇到了问题,不是因为链表概念,而是因为结构体中的字符串字段。

这是一个关于C语言的作业,据我所知(我是个新手),C语言没有'字符串'这种数据类型。

这是我的结构体代码:

typedef struct 
{
  int number;
  string name;
  string address;
  string birthdate;
  char gender;
} patient;

typedef struct llist
{
  patient num;
  struct llist *next;
} list;

我在考虑为字符串本身创建一个结构体,以便我可以像这样在结构体中使用它们:

typedef struct string 
{ 
  char *text;
} *string;

当需要创建新的字符串类型(char数组)的数据时,我将使用 malloc() 为它们中的每一个分配内存。

typedef struct string
{
  char *text;
} *string;

int main()
{
    int length = 50;
    string s = (string) malloc(sizeof string);
    s->text = (char *) malloc(len * sizeof char);
    strcpy(s->text, patient.name->text);
}

有人能帮我解决这个问题吗?
谢谢。

5个回答

67

关于字符串和内存分配:

C语言中的字符串只是一系列的char字符,因此你可以使用char *或者char数组来代替字符串数据类型:

typedef struct     {
  int number;
  char *name;
  char *address;
  char *birthdate;
  char gender;
} patient;

然后你需要为该结构体本身和每个字符串分配内存:

patient *createPatient(int number, char *name, 
  char *addr, char *bd, char sex) {

  // Allocate memory for the pointers themselves and other elements
  // in the struct.
  patient *p = malloc(sizeof(struct patient));

  p->number = number; // Scalars (int, char, etc) can simply be copied

  // Must allocate memory for contents of pointers.  Here, strdup()
  // creates a new copy of name.  Another option:
  // p->name = malloc(strlen(name)+1);
  // strcpy(p->name, name);
  p->name = strdup(name);
  p->address = strdup(addr);
  p->birthdate = strdup(bd);
  p->gender = sex;
  return p;
}

如果你只需要一些patient,你可以避免内存管理的代价是分配比你实际需要的更多的内存:

typedef struct     {
  int number;
  char name[50];       // Declaring an array will allocate the specified
  char address[200];   // amount of memory when the struct is created,
  char birthdate[50];  // but pre-determines the max length and may
  char gender;         // allocate more than you need.
} patient;

关于链表:

一般来说,链表的目的是提供对一个有序元素集合的快速访问。如果你的llist包含一个名为num的元素(可能包含患者编号),你需要另外一个数据结构来保存实际的patient,每次都需要查找患者编号。

相反,如果你声明一个

typedef struct llist
{
  patient *p;
  struct llist *next;
} list;

然后,每个元素都包含一个指向 patient 结构的直接指针,您可以像这样访问数据:

patient *getPatient(list *patients, int num) {
  list *l = patients;
  while (l != NULL) {
    if (l->p->num == num) {
      return l->p;
    }
    l = l->next;
  }
  return NULL;
}

啊,所以基本上如果我想将字符串类型用作char指针,我不需要直接使用字符串类型并将其修改为指针。谢谢,这真的很有启发性。但是我有几个问题:(a)假设我想为新患者插入记录,我是否需要创建一个新函数,例如void insert()?我对上面的typedef struct中的struct patient有点困惑。(b)还是我必须在列表结构中插入一个包含患者结构的新节点,通过插入一个新节点到列表中来实现?这样清楚吗?非常感谢。 - fleuracia
链表是一种基本的通用数据结构,一旦你理解了它们,它们会为你提供很好的服务。找到比在SO上得到的更全面的解释是值得投资的。有很多好的网站和书籍可供选择。 - Adam Liss

6

我认为这种解决方案使用的代码更少,即使对于新手也很容易理解。

对于结构体中的字符串字段,您可以使用指针,将字符串重新分配给该指针将更加直接和简单。

定义结构体的定义:

typedef struct {
  int number;
  char *name;
  char *address;
  char *birthdate;
  char gender;
} Patient;

使用该结构体类型初始化变量:

Patient patient;
patient.number = 12345;
patient.address = "123/123 some road Rd.";
patient.birthdate = "2020/12/12";
patient.gender = 'M';

就是这么简单。希望这个答案能帮助许多开发人员。


这帮助我解决了问题。当定义字符串时,我遇到了错误,例如:struct Student { char name[24]; } student; - user8581607
2
患者的性别应该是patient.gender = 'M',而不是"M",因为它是一个字符。请进行更正。虽然编译不会报错,但结果并不如预期。 - Kapil
作为一名新手,这个答案对我非常有帮助。与(看起来更复杂的)被接受的答案相比,它有什么缺点吗? - Dave
1
@Dave 我认为使用这种方法没有什么缺点,但是如果你能理解被接受的答案中代码的风格,那么这也是一个加分项,因为有很多开发者使用该风格。 - Pakpoom Tiwakornkit
@Kapil 谢谢你指出这个问题。答案已经被更正了。 - Pakpoom Tiwakornkit

1

如果你确实想使用typedef,那么Richard的方法是你想要的。但在这种情况下,我建议不要使用typedef,因为你会失去指针的视野,而且没有任何好处。

如果你正在处理计数字符串或具有其他功能的东西,那可能会有所不同,但在这种情况下,我真的建议你熟悉“标准”C字符串实现即“char *”...


0

这个不起作用:

string s = (string)malloc(sizeof string); 

string 指的是指针,你需要结构体本身的大小:

string s = malloc(sizeof (*string)); 

请注意,也没有任何转换(从 malloc 的返回类型 void * 隐式执行转换)。
此外,在您的 main 中,您有一个全局声明的未初始化的 patient 。 请尝试:
 patient.number = 3;     
 patient.name = "John";     
 patient.address = "Baker street";     
 patient.birthdate = "4/15/2012";     
 patient.gender = 'M';     

在读取其任何成员之前

此外,strcpy 本质上是不安全的,因为它没有边界检查(如果源太长,则会复制直到遇到第一个 '\0',从而写入分配的内存之外)。请改用 strncpy,其中您至少可以指定要复制的最大字符数--阅读文档以确保传递正确的值,很容易犯一个偏移量错误。


1
在使用malloc时,您不应该进行类型转换。 - Alok Save
不,patient并没有全局声明,它是一个typedef。但即使它是全局的,你也不能像这样初始化它。 - kralyk

0

你可以使用一个更简单的typedef

typedef char *string;

那么,你的malloc看起来就像普通的malloc:

string s = malloc(maxStringLength);

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