使用/分段错误将字符串分配给C中的结构

Assigning a string to a structure in C w/ Segemntation fault

目前,我正在尝试解决考试中的练习以备考。研究到这里后,我更改了

的结构定义
char nr[10];
char name[25];

char *nr;
char *name;

将字符串(指向字符串的指针)传递到结构中(请参阅下面的完整代码)。

当我使用 Netbeans 8.2 和 Cygwin GCC 7.4.0 执行代码时出现运行时错误。有时代码无休止地运行 w/o 终止,有时我得到一个分段错误。调试后,编译器尝试执行时出现错误

new->item->nr = orderno;

我通过另一个带有交换函数的冒泡排序练习意识到了什么。我习惯像这样交换引用值:

void swap(int *px, int *py) {
  int temp = *px;
  *px = *py;
  *py = temp;
}

我想做的是使用这样的地址进行交换:

void swap(int *px, int *py) {
  int *temp = px;
  px = py;
  py = temp;
}

Netbeans 通过 运行 无休止地 w/o 终止交换函数或我为其创建主题的代码显示相同的行为。有人知道我做错了什么吗?

#include <stdio.h>
#include <stdlib.h>

typedef struct litem_t {
    char *nr;   // was char nr[10];
    char *name; // was char name[25];
    float price;
    int cnt;
    float sum;
} litem;
typedef struct llist_t {
    struct litem_t *item;
    struct llist_t *next;
} llist;

llist* addItem(llist *alist, char *orderno, char *name, float price, int count, float sum);
void dumpList(llist *alist);

void L_3_a_KL_17(int argc, char **argv) {
    llist *alist;
    alist = addItem(NULL, "AT 1001 C", "Pear jPhone Quad 64GB", 499.00, 1, 499.00);
    alist = addItem(alist, "ZT 1012 D", "Pear jPhone Octa 128GB", 799.00, 1, 799.00);
    
    dumpList(alist);
}

llist* addItem(llist *alist, char *orderno, char *name, float price, int count, float sum) {
    llist *new = (llist *)malloc(sizeof(llist));
    if(new == NULL)
        return NULL;
    new->item->nr = orderno; // runtime error here
    new->item->name = name;
    new->item->price = price;
    new->item->cnt = count;
    new->item->sum = sum;
    new->next = alist;
    return new;
}

void dumpList(llist *alist) {
    llist *temp;
    int j = 1;
    while(alist != NULL) {
        printf("\n%d. Article:\nNumber:\t%s\nName:\t%s\nPrice:\t%.2f\nCnt:\t%d\nSum:\t%.2f",                                     
            j, alist->item->nr, alist->item->name, alist->item->price, alist->item->cnt,
            alist->item->summe);
        temp = alist;
        alist = alist->next;
        free(temp);
        j++;
    }
}

基本上,代码应该读取文章,将它们按顺序存储在一个串联列表中,然后将它们打印在屏幕上。文章的顺序无关紧要。

感谢您的帮助。

韩国

你可能想要这个:

void swap(int **px, int **py) {
  int *temp = *px;
  *px = *py;
  *py = temp;
}
...
int *p1, *p2;
...
swap(&p1, &p2);

这没有任何用处:

void swap(int *px, int *py) {
  int *temp = px;
  px = py;
  py = temp;
}

就像这没有做任何有用的事情一样:

void swap(int x, int x) {
  int temp = x;
  x = y;
  y = temp;
}

您的列表源中有一些错误

ptr->item->nr = orderno;

您将指向字符串的指针复制到结构中的指针。这仅在字符串已存储在堆上并且您将指针作为函数调用中的参数传递时才有效。

在您的示例中,您只复制了字符串地址。如果函数返回时该字符串不再可用,则指针不会指向预期的字符串。您应该按照在第一个版本中定义的方式将字符串复制到缓冲区,或者确保只要数据结构中的指针具有其值,字符串就存在。

第二个问题是您为列表条目而不是项目本身分配了内存。这会导致您提到的错误。列表条目仅包含指向该项目的指针!因此,您还必须为该项目分配内存。如果失败,您必须错误处理您已成功创建列表元素但未创建数据项的问题。

为了回答您评论中的问题,请让我详细介绍一下

您为数据定义了一个结构类型

typedef struct litem_t {
    char nr[10];   // was char nr[10];
    char name[25]; // was char name[25];
    float price;
    int cnt;
    float sum;
} litem;

和一个带有链接项目的指针的结构:

typedef struct llist_t {
    struct litem_t *item;
    struct llist_t *next;
} llist;

请看这张图片。您可以看到您需要为每个新数据的两个结构创建一个实例

   Chained Instances               data instances 
     of type llist                   of type litem
     
     Size of each                    Size of each element 
     instance is 8 byte              is 47 byte
         
    [alist]
     |
     |                                 
     '--> +--------+                   last created objects  
          | item --|--------------->  +-----------+               
          | next --|---.              | nr[10]    |   10 byte
          +--------+   |              | name[25]  |   25 byte 
                       |              | price     |    4 byte     
     .-----------------'              | cnt       |    4 byte   
     |                                | sum       |    4 byte     
     |                                +-----------+
     '--> +--------+     
          | item --|--------------->  +-----------+                        
          | next --|---.              | nr[10]    |               
          +--------+   |              | name[25]  |                 
                       |              | price     |                 
     .-----------------'              | cnt       |                 
     |                                | sum       |          
     |                                +-----------+           
     '--> +--------+                                         
          | item --|---------------> +-----------+          
          | next = NULL              | nr[10]    |                    
          +--------+                 | name[25]  |                     
                                     | price     |           
                                     | cnt       |           
                                     | sum       |           
                                     +-----------+           
                                     First created objects
                                     
                                     

图片显示首先需要为llist项分配内存。比指针“item”存在,您可以为数据项分配内存。必须分别释放这两个项目,例如这样。

llist * next;
if (alist)
{
    do
    {
        next = alist->next;  // temp. copy because pointer will be deleted
        if (alist->item) free(alist->item);  // free memory for litem
        free (alist);                        // free memory for llist
        alist = next;                        // set alist to next item  
    } while (alist != NULL)                  /7 very first entry has no "next"  
}

更好的方法是创建一个释放单个项目并更正前一个项目的“下一个”指针的函数。有了这个,您可以删除列表中的一个项目。恕我直言,这就是使用链表的原因之一。

代码(不含免费)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct litem_t {
    char nr[10];   // was char nr[10];
    char name[25]; // was char name[25];
    float price;
    int cnt;
    float sum;
} litem;
typedef struct llist_t {
    struct litem_t *item;
    struct llist_t *next;
} llist;

llist* addItem(llist *alist, char const *orderno, char const *name, float price, int count, float sum);
void dumpList(llist *alist);

void L_3_a_KL_17(int argc, char **argv) {
    llist *alist;
    alist = addItem(NULL, "AT 1001 C", "Pear jPhone Quad 64GB", 499.00, 1, 499.00);
    alist = addItem(alist, "ZT 1012 D", "Pear jPhone Octa 128GB", 799.00, 1, 799.00);
    
    dumpList(alist);
}

llist* addItem(llist *alist, char const *orderno, char const *name, float price, int count, float sum) {
    // Alloccate space for new list entry 
    llist *newlistentry_ptr = (llist *)malloc(sizeof(llist));
    if(newlistentry_ptr == NULL)
        return NULL;
    
    // Alloc space for new data item 
    newlistentry_ptr->item = (litem *)malloc(sizeof(litem));   
    if(newlistentry_ptr->item == NULL) {
        // add here some error handling
        // caller will not expect that item is NULL
        // maybe free newlistentry_ptr for error detection
        newlistentry_ptr->next = alist;
        return newlistentry_ptr;
    }

    // Copy strings into a buffer where they are retained
    strncpy (newlistentry_ptr->item->nr, orderno, sizeof(newlistentry_ptr->item->nr)); 
    strncpy (newlistentry_ptr->item->name, name, sizeof(newlistentry_ptr->item->name));
    newlistentry_ptr->item->price = price;
    newlistentry_ptr->item->cnt = count;
    newlistentry_ptr->item->sum = sum;
    newlistentry_ptr->next = alist;
    return newlistentry_ptr;
}

void dumpList(llist *alist) {
    llist *temp;
    int j = 1;
    while(alist != NULL) {
        printf("\n%d. Article:\nNumber:\t%s\nName:\t%s\nPrice:\t%.2f\nCnt:\t%d\nSum:\t%.2f",                                     
            j, alist->item->nr, alist->item->name, alist->item->price, alist->item->cnt,
            alist->item->sum);
        temp = alist;
        alist = alist->next;
        free(temp);
        j++;
    }
}
#include <stdio.h>

void swap(int *px, int *py) {
    int *temp = px;
    px = py;
    py = temp;
}

void bsort(int *v, int n) {
    typedef char boolean;
    boolean switch;
    const boolean
        FALSE = 0,
        TRUE = 1;
    do {
        switch = FALSE;
        for(int *i = v; i-v < n; i++)
            if(*i > *(i+1)) {
                swap(i, i+1)
                switch = TRUE;
            }
    } while(switch);
}

void main(void) {
    int
        v[] = {42, 51, 13, 48, 92, 16, 9, 68},
        n = (int)sizeof(v)/(int)sizeof(int);
    bsort(v, n-1);

    for(int *i = v; i-v < n; i++)
        printf("%d "; *i);
}

当我在调试模式下 运行 时,swap 函数应该可以切换工作正常的地址。离开函数时指针被破坏并且引用丢失时是否存在相同的问题?我认为指针只是一个参考,无论如何都应该在交换函数中进行更改。你明白我的意思吗?

如果和前一个问题是同一个问题,static 应该可以解决,不是吗?

注意:你问了第二个问题。 Whosebug 好像不是这样的。如果还不清楚,您应该将新问题作为新主题提出。

你想交换什么?我猜你想交换值。

在这种情况下,调用和函数签名是正确的,但交换实现不是。您在 swap() 中交换指针,但这不会影响 i 或 i+1 或指针指向的数据。

您应该取消对指针的引用,然后交换数据

// px points to data cell 1 (as i)
// py pounts to data cell 2 (as i+1)
void swap (int * px, int * py)
{
  int temp = *px;  // copy data cell 1 to temp
  *px = *py;       // copy data cell 2 to data cell 1
  *py = temp;      // copy temp to data cell 2
}

这个实际交换数据*i和*(i+1)