C程序使用结构重新排列列表

时间:2016-05-08 08:37:57

标签: c function pointers data-structures structure

第一个C编程课并开始学习编程。我目前正在学习如何在C中使用结构,这是一个帮助理解这个过程的学习任务。

程序应接受地址输入,并通过最低邮政编码到最高邮政编码的邮政编码进行组织。

下面的程序和输入/输出是我用来学习这门课程的程序之一。我想使用此程序使用文件重定向将数据转储到文件中。例如:

ConsoleApplication1.exe < input.txt > testout.txt

现在所有被转储到测试文件中的是:

Processed 18 sets of data.

显然,由于我在“删除”中的printf声明。用于测试过程的功能。

我的问题是,获取输入,处理它,然后将输出以zipcode顺序转储到文件中的最佳方法是什么?有人可以推荐一个可以添加的功能/声明来实现这个目标吗?

非常感谢您提供帮助,时间和指导,让您的计划有效!

//header file initiating other headers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Person {
    char name[50];
    char address[50];
    char citystate[30];
    char zip[10];
} Person;

int takedata (Person * arr[]);
void sortdata (Person * arr[], int noElements);

int main (void) {

    //Intiate program
    int noElements = 0;
    Person *arr[50];

    //call input function
    //input(work, &count);
    takedata (arr);

    //sort files
    //call function
    //swap(work, &count);
    sortdata (arr, &noElements);

    //call output function
    //output(work, &count);

    //end program
    return 0;
}

void sortdata (Person * arr[], int noElements)
{
    /* temporary pointer to Person data type to aid with swapping */
    //Person * tempptr = (Person *)malloc(sizeof(Person));

    int i, j, compare;
    Person *tempptr = NULL;

    for (i = 0; i <= (noElements - 1); i++); {
        for (j = (i + 1); j <= noElements; j++) {
            compare = strcmp (arr[i]->zip, arr[j]->zip);
            if (compare > 0) {
                printf ("attempted sort %d times.\n", j);
                /* stores value in index i for array inside of temporary pointer  */
                tempptr = arr[i];
                arr[i] = arr[j];
                arr[j] = tempptr;
            }
        }
    }
}

int takedata (Person * arr[])
{
    /* counter variable */
    int i = 0;
    char teststring[25];

    while ((gets (teststring)) != NULL && i < 50) {

        /* dynamically allocates memory for each index of the array */
        arr[i] = (Person *) malloc (sizeof (Person));

        /* takes in data from user/ input file */
        strcpy (arr[i]->name, teststring);
        gets (arr[i]->address);
        gets (arr[i]->citystate);
        gets (arr[i]->zip);

        i++;
    }

    printf ("Processed %d sets of data.\n\n", i);

    return (i - 1);
}

正在使用的输入数据:

A1, A220294 Lorenzana Dr
Woodland Hills, CA
91364
B1, B2
19831 Henshaw St
Culver City, CA
94023
C1, C2
5142 Dumont Pl
Azusa, CA
91112
D1, D2
20636 De Forest St
Woodland Hills, CA
91364
A1, A2
20294 Lorenzana Dr
Woodland Hills, CA
91364
E1, E2
4851 Poe Ave
Woodland Hills, CA
91364
F1, F2
20225 Lorenzana Dr
Los Angeles, CA
91111
G1, G2
20253 Lorenzana Dr
Los Angeles, CA
90005
H1, H2
5241 Del Moreno Dr
Los Angeles, CA
91110
I1, I2
5332 Felice Pl
Stevenson Ranch, CA
94135
J1, J2
5135 Quakertown Ave
Thousand Oaks, CA
91362
K1, K2
720 Eucalyptus Ave 105
Inglewood, CA
89030
L1, L2
5021 Dumont Pl
Woodland Hills, CA
91364
M1, M2
4819 Quedo Pl
Westlake Village, CA
91362
I1, I2
5332 Felice Pl
Stevenson Ranch, CA
94135
I1, I2
5332 Felice Pl
Stevenson Ranch, CA
94135
N1, N2
20044 Wells Dr
Beverly Hills, CA
90210
O1, O2
7659 Mckinley Ave
Los Angeles, CA
90001

2 个答案:

答案 0 :(得分:1)

如评论中所述,您的代码中存在大量错误。从安全角度来看,永远不会使用gets。鉴于存在安全风险,它已从C11 中的C标准中删除而没有弃用

虽然不是错误,但C的标准编码样式避免使用CamelCase变量来支持所有小写。参见例如NASA - C Style Guide, 1994

在C中,qsort是库提供的实际排序例程。除非你只是想折磨自己,否则你应该使用qsortzip上的指针数组进行排序,并且可以完全删除sortdata例程。使用qsort编写代码时唯一需要的是整数比较函数,以便qsort知道如何对指针进行排序。由于您要对指针列表进行排序,因此需要认识到compare函数的每个输入都是指向struct person 指针的指针,这意味着您将退出两次。 qsortzip进行排序的简短比较函数可以是:

/** comparison function for qsort of pointers on zip */
int cmpzip (const void *a, const void *b)
{
    person *ap = *(person * const *)a;
    person *bp = *(person * const *)b;

    return strcmp (ap->zip, bp->zip);
}

注意:虽然(person * const *)强制转换可能看起来很不稳定,但它只是反映您正在处理其值不会改变的常量指针。您可以忽略{{1修饰符并将强制转换为const,这在视觉上更有意义。)

基于(person **)的整个指针数组被简化为:

zip

(使用C库提供的排序例程比滚动自己的...更不容易出错...)

接下来,您的代码可能会在50个不同的地方之一失败,您将毫无头绪。你只会盲目地阅读和写作,直到发生 SEGFAULT (或其他错误)。如果您对 qsort (arr, nelem, sizeof *arr, cmpzip); 的通话失败怎么办?学习验证后续步骤所依赖的每个步骤,并验证所有用户输入。 (我会在这里提出关于没有套malloc的说明 - 完全没必要)验证的例子:

malloc致电main

takedata

if (!(nelem = takedata (arr, fp))) { /* read addresses */ fprintf (stderr, "error: no elements read.\n"); return 1; } 验证分配中:

takedata

验证每次阅读:

        /* allocate/validate memory for struct */
        if (!(arr[i] = malloc (sizeof *arr[i]))) {
            fprintf (stderr, "error: virtual memory exhausted.\n");
            exit (EXIT_FAILURE);
        }

尽可能添加其他验证,例如检查确保您的 /* read/validate address from stdin */ if (!fgets (buf, MAXC, fp)) { fprintf (stderr, "error: read failure, arr[%d]->address.\n", i); exit (EXIT_FAILURE); } 是全部数字,例如

zip

我想指出其他一些问题,但老实说,我失去了轨道。查看所有评论,它们涵盖了大部分问题。

最后,将所有部分放在一个从文件读取数据的示例中(在修复第一行之后),您可以执行类似于以下操作的操作。确保您了解代码的每一行,每个字符的作用,如果没有,请询​​问。代码将从命令行中指定为第一个参数的文件中读取数据(如果没有给出参数,则默认从 if (!fgets (buf, MAXC, fp)) { /* zip */ fprintf (stderr, "error: read failure, arr[%d]->zip.\n", i); exit (EXIT_FAILURE); } len = (size_t)rmcrlf (buf); /* trims newline and returns length */ char *p = buf; for (; *p; p++) if (*p < '0' || '9' < *p) { /* validate zip all numeric */ fprintf (stderr, "error: invalid zip, arr[%d]->zip '%s'.\n", i, buf); exit (EXIT_FAILURE); } 读取)。

stdin

输入文件

您的输入第一行已修复,例如

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

/* constants for max zip, citystate, (structs, name, addr), buf */
enum { MAXZ = 10, MAXCS = 30, MAXS = 50, MAXC = 128 };

typedef struct person {
    char name[MAXS];
    char address[MAXS];
    char citystate[MAXCS];
    char zip[MAXZ];
} person;

size_t takedata (person **arr, FILE *fp);
int rmcrlf (char *s);
int cmpzip (const void *a, const void *b);

int main (int argc, char **argv) {

    size_t i, nelem = 0;    /* nelem cannot be negative */
    person *arr[MAXS];      /* C-style avoids mixed case */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    if (!(nelem = takedata (arr, fp))) { /* read addresses */
        fprintf (stderr, "error: no elements read.\n");
        return 1;
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    printf ("Processed %zu sets of data.\n\n", nelem);

    /* sort array of pointers on zip */
    qsort (arr, nelem, sizeof *arr, cmpzip);

    for (i = 0; i < nelem; i++) { /* print output free memory */
        printf (" name : %s  (%s)\n", arr[i]->name, arr[i]->zip);
        free (arr[i]);
    }

    return 0;
}

size_t takedata (person **arr, FILE *fp)
{
    if (!arr || !fp) {  /* validate arr and fp are non-NULL */
        fprintf (stderr, "takedata() error: invalid parameter.\n");
        return 0;
    }
    int i = 0;
    char buf[MAXC] = "";

    while (i < MAXS && fgets (buf, MAXC, fp)) {
        /* remove newline get length */
        size_t len = (size_t)rmcrlf (buf);

        /* allocate/validate memory for struct */
        if (!(arr[i] = malloc (sizeof *arr[i]))) {
            fprintf (stderr, "error: virtual memory exhausted.\n");
            exit (EXIT_FAILURE);
        }
        strncpy (arr[i]->name, buf, len + 1); /* copy buf to name */

        /* read/validate address from stdin */
        if (!fgets (buf, MAXC, fp)) {
            fprintf (stderr, "error: read failure, arr[%d]->address.\n", i);
            exit (EXIT_FAILURE);
        }
        len = (size_t)rmcrlf (buf);
        strncpy (arr[i]->address, buf, len + 1);

        if (!fgets (buf, MAXC, fp)) { /* citystate */
            fprintf (stderr, "error: read failure, arr[%d]->citystate.\n", i);
            exit (EXIT_FAILURE);
        }
        len = (size_t)rmcrlf (buf);
        strncpy (arr[i]->citystate, buf, len + 1);

        if (!fgets (buf, MAXC, fp)) { /* zip */
            fprintf (stderr, "error: read failure, arr[%d]->zip.\n", i);
            exit (EXIT_FAILURE);
        }
        len = (size_t)rmcrlf (buf);
        char *p = buf;
        for (; *p; p++)
            if (*p < '0' || '9' < *p) { /* validate zip all numeric */
                fprintf (stderr, "error: invalid zip, arr[%d]->zip '%s'.\n",
                        i, buf);
                exit (EXIT_FAILURE);            
            }
        strncpy (arr[i]->zip, buf, len + 1);

        i++;
    }

    return (i);
}

/** remove newline or carriage-return from 's'.
 *  returns new length, on success, -1 if 's' is NULL.
 */
int rmcrlf (char *s)
{
    if (!s) return -1;
    if (!*s) return 0;
    char *p = s;
    for (; *p && *p != '\n' && *p != '\r'; p++) {}
    *p = 0;

    return (int)(p - s);
}

/** comparison function for qsort of pointers on zip */
int cmpzip (const void *a, const void *b)
{
    person *ap = *(person * const *)a;
    person *bp = *(person * const *)b;

    return strcmp (ap->zip, bp->zip);
}

示例使用/输出

以下是按排序顺序显示$ cat dat/addr.txt A1, A2 20294 Lorenzana Dr Woodland Hills, CA 91364 B1, B2 19831 Henshaw St Culver City, CA 94023 C1, C2 ... name的排序指针数组的快速打印:

zip

内存使用/错误检查

在你动态分配内存的任何代码中,你有2 responsibilites 关于分配的任何内存块:(1)总是保留一个指向起始地址的指针内存块,(2)当不再需要时,它可以释放

必须使用内存错误检查程序,以确保您没有在已分配的内存块之外/之外写入,尝试读取或基于未初始化的值跳转,最后确认您已释放所有内存你分配的记忆。

$ ./bin/addrstruct <dat/addr.txt
Processed 18 sets of data.

 name : K1, K2  (89030)
 name : O1, O2  (90001)
 name : G1, G2  (90005)
 name : N1, N2  (90210)
 name : H1, H2  (91110)
 name : F1, F2  (91111)
 name : C1, C2  (91112)
 name : J1, J2  (91362)
 name : M1, M2  (91362)
 name : A1, A2  (91364)
 name : D1, D2  (91364)
 name : A1, A2  (91364)
 name : E1, E2  (91364)
 name : L1, L2  (91364)
 name : B1, B2  (94023)
 name : I1, I2  (94135)
 name : I1, I2  (94135)
 name : I1, I2  (94135)

始终确认释放所有堆块 - 不可能泄漏并且同样重要错误摘要:0个上下文中的0个错误

总有很多坚果&amp;语言的螺栓打包成看似简短的,对结构的一个指针数组,类型示例。这就是为什么教授们喜欢他们 - 以及为什么你应该确保你理解每一个难题。仔细看看,如果您有任何问题,请告诉我。

完全格式化输出

如果您想调整输出并添加结构的每个字段并整理格式,您可以执行类似以下操作。只需将上面代码中的输出替换为:

$ valgrind ./bin/addrstruct <dat/addr.txt
==28762== Memcheck, a memory error detector
==28762== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==28762== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==28762== Command: ./bin/addrstruct
==28762==
Processed 18 sets of data.

 name : K1, K2  (89030)
 name : O1, O2  (90001)
 ...
 name : I1, I2  (94135)
 name : I1, I2  (94135)
==28762==
==28762== HEAP SUMMARY:
==28762==     in use at exit: 0 bytes in 0 blocks
==28762==   total heap usage: 18 allocs, 18 frees, 2,520 bytes allocated
==28762==
==28762== All heap blocks were freed -- no leaks are possible
==28762==
==28762== For counts of detected and suppressed errors, rerun with: -v
==28762== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)

对于格式化输出,例如:

printf (" %-6s    %-22s    %-20s    %s\n", "Name",
        "Address", "City State", "Zip");
printf (" ------    ----------------------    "
        "--------------------    -----\n");
for (i = 0; i < nelem; i++) { /* print output free memory */
    printf (" %-6s    %-22s    %-20s    %s\n", arr[i]->name,
            arr[i]->address, arr[i]->citystate, arr[i]->zip);
    free (arr[i]);
}

答案 1 :(得分:0)

函数:main()有一个变量noElements,初始化为0

函数:takeData()为读取的项目数保留一个本地计数器,但该值永远不会返回到main()

中的变量

然后main()将该值0传递给sortData()作为要排序的元素数。当然,如果计数为0,则sortData()函数不执行任何操作。

为什么变量:noElements有&#39;地址传递给sortData()?函数:sortData()永远不会更改该变量。所以代码应该简单地传递noElements的内容,这是函数:sortData()所期望的内容,而不是地址。

在发布的代码中没有显示arr[] struct Person数组的内容。