如何正确地将数组指针传递给C

时间:2015-04-23 21:40:14

标签: c pointers computer-science user-defined-functions

关于这个程序可能有一个不同的问题,但在这个C代码中我有三个功能:一个用于打印记录,一个用于添加记录,一个用于删除记录。

我不明白为什么(添加)和(删除)不会在主函数中进行更改,所以当我使用print all records函数时,它会打印出来变化,但它没有显示变化,出了什么问题?

详细信息在评论中,请随意运行代码以查看问题。

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

/*The program is to store student record (firstname,lastname and score), it should not be more
than 15 records and the names should not be more than 20 chars, array pointersnare being used
instead of arrays, the (add_record) and (delete_record) functions must
do thsi change in the main function, so when you print all records, the changes are shown*/

void print_records(char **firstname,char **lastname,float *score,int *the_size)
{
    int i;
    printf("Printing All record(s)...\n");

    for (i=0;i<*the_size;i++) /*loop to print records of all arrays in correct format*/
    {
        printf("Firstname : %s, Lastname : %s, Score : %f\n",firstname[i],lastname[i],score[i]);

    }
}
void add_new_record (char **firstname,char **lastname,float *score,int the_size)
{
    printf("Add new record in the format :\nFirstname Lastname Score\n");
    /*the strategy here is to check if all 15 elemts are used, if they are, use realloc
    to add one more, if not add the record after the last record*/
    if (the_size == 15)
    {
        firstname=realloc(firstname,16*sizeof(char*));
        firstname[15]=malloc((20+1)*sizeof(char));
        lastname=realloc(lastname,16*sizeof(char*));
        lastname[15]=malloc((20+1)*sizeof(char));
        score=realloc(score,16*sizeof(float));
        scanf("%s %s %f",firstname[15],lastname[15],&score[15]);
        printf("New Record Added Successfully !\n");
        printf("Firstname : %s, Lastname : %s, Score : %f\n",firstname[15],lastname[15],score[15]);
    }
    else if (the_size<15)
    {
        scanf("%s %s %f",firstname[the_size],lastname[the_size],&score[the_size]);
        printf("New Record Added Successfully !\n");
        printf("Firstname : %s, Lastname : %s, Score : %f\n",firstname[the_size],lastname[the_size],score[the_size]);
    }
}
void delete_record (char **firstname,char **lastname,float *score,int the_size)
{
    char *str=malloc(20*sizeof(char)); /*String entered by user must be 20 or less chars*/
    int i,ctr=0;
    char *temp_first=malloc(20*sizeof(char));/*temp array to swap firstname must be 20 or less chars*/
    char *temp_last=malloc(20*sizeof(char)); /*temp array to swap lastname must be 20 or less chars*/
    float temp_score;/*ctr is the counter used to check if there are no matchs in the end*/
    printf("Enter the lastname of record(s) to delete : ");
    scanf("%s",str);
    /* the strategy here is to move the element to be deleted to the last index and use
    relloc to shrink the size by 1 (-1) */
    for (i=0;i< the_size;i++)
    {
        if (strcmp(str,lastname[i])==0)
        {
            printf("Deleting Record for %s %s...\n",firstname[i],lastname[i]);
            temp_score=score[i];
            score[i]=score[the_size-1];
            score[the_size-1]=temp_score;
            strcpy(temp_first, firstname[i]);  /*using strcpy function to swap strings*/
            strcpy(firstname[i], firstname[the_size-1]);
            strcpy(firstname[the_size-1], temp_first);
            strcpy(temp_last, lastname[i]);
            strcpy(lastname[i], lastname[the_size-1]);
            strcpy(lastname[the_size-1], temp_last);
            score=realloc(score,(the_size-1)*sizeof(float));
            firstname=realloc(firstname,(the_size-1)*sizeof(char*));
            lastname=realloc(lastname,(the_size-1)*sizeof(char*));
            ctr++;
            the_size--;


            }

        }
    if (!ctr) /*if ctr=0 (no increment), then print,there is no match*/
    {
        printf ("Sorry, no available record for %s",str);
    }
        free(temp_first);
        free(temp_last);
        free(str);
}
void main()
{
    char **firstname;
    char **lastname;
    float *score;
    int number_of_records,i,j=-1,ctr=1,row=15,col=20;
    /*ctr is to keep track of the student's number (makes it easier to
      the user), it starts with (1)*/

    firstname=malloc(row*sizeof(char*));
    for(i=0;i<row;i++)
    {
        firstname[i]=malloc((col+1)*sizeof(char));
    }
     lastname=malloc(row*sizeof(char*));
    for(i=0;i<row;i++)
    {
        lastname[i]=malloc((col+1)*sizeof(char));
    }
    printf("\nPlease indicate number of records you want to enter (min 2, max 15): ");
    scanf("%d",&number_of_records);
    score=malloc(row*sizeof(float));

    printf("\nPlease input records of students\n(enter a new line after"
           "each record), with following format:\nfirst name last name score ");
    for (i=0;i<number_of_records;i++)
    {
        printf("\nEnter record for student %d : ",ctr);
        scanf("%s %s %f",firstname[i],lastname[i],&score[i]);

        ctr++; /*ctr is to keep track of student number
                 (makes it easy to the user) */

    }


    while (j!=0) /*Main menu will keep looping after using a function as long as j is not 0
           When the user enters 0 (zero) the loop will stop and therefore the program will terminate*/
    {
        printf("\nSelect desired function by pressing the corresponding key number\n");

        printf("\n********** Main Menu **********\n");

        printf("\n>>> Print records (press 1)\n");

        printf("\n>>> Add a new Record (press 2 )\n");

        printf("\n>>> delete record (press 3)\n");


        printf("\n>>> Exit the program (press 0)\n");

        scanf("%d",&j); /*getting j from the user (j is used for selection and for the while loop)*/
        if (j==1)
        {
            print_records(firstname,lastname,score,&number_of_records);
        }
        else if (j==2)
        {
        add_new_record(firstname,lastname,score,number_of_records);
        }
        else if (j==3)
        {
    delete_record(firstname,lastname,score,number_of_records);
        }
    else if (j==0)
    {
    printf("Exitting program ...\n");
        }
    }
}

2 个答案:

答案 0 :(得分:0)

首先,您将add_的参数声明为指针(可能是因为您希望函数更改您传递地址的变量的值):

add_new_record (char **firstname, char **lastname, float *score, int the_size) { 

但是你只需要分配当地人,而不是他们指向的东西:

    firstname = realloc(firstname,16*sizeof(char*));
    score = realloc(score,16*sizeof(float));

如果要更改这些指向的变量,则必须这样做 取消引用它们进行分配:

    *firstname = malloc(16);
    *score = 1.0;

现在,原始的firstname指针指向一个有效的内存块(可能你想要strcpy()那里有一些实际名称),而得分点的原始float变量现在为1.0。

还有其他问题,但这是此功能不会改变您认为应该改变的主要原因,因为您没有告诉它。

答案 1 :(得分:0)

正如其他答案所观察到的,在C中,所有参数都是按值传递的。这意味着函数获取调用者值的副本,因此调用者看不到对该值的更改。换句话说,给定

void f(any_type arg) {
    arg = any_value;
}

无论any_type是什么类型或any_value是什么值,调用者都不会检测到任何更改。但请注意,它之间的差异,以及:

void f(any_type *arg) {
    *arg = any_value;
}

在这种情况下,它不是被修改的函数参数(指针),而是指向的东西。参数是调用者值的副本,因此两者指向同一个事物。调用者无法检测到参数的更改,但在调用之后,可以检测对其指向的事物的更改。

您的代码会出现一些此类问题,其中一些问题会导致您的主要问题。最重要的是,这些与您的记录保持在列表中的元素数量(number_of_records中的变量main())有关。您的添加和删除功能或多或少都可以正常工作,除非他们无法将修改后的列表大小传回main。

当已有15条记录时,add_new_record()还有其他问题;如果可以,我会拒绝这种情况。如果你必须支持它,那么你有很多东西需要清理。其中一些与传值问题有关,另一些则与列表最初包含 16 或更多记录时代码应该执行的操作有关。

<强>更新: 由于你在解决这个问题上遇到了很多麻烦,所以这里是delete_record()的修订版。它实现的内容远远超过了在删除记录时获得所需输出所需的最小变化,因为事实上只要我遇到麻烦,我可能会提出很多其他问题。查看新的和修改过的评论。

/*
 * Can realloc() *firstname, *lastname, and *score and return the updated
 * values to the caller.  Most importantly, can update *the_size and have
 * the caller see the result.
 */
void delete_record (char ***firstname, char ***lastname, float **score, int *the_size)
{
    int i;
    int initial_size = *the_size;
    char str[21];  /* no need to malloc a fixed-length local array */

    printf("Enter the lastname of record(s) to delete : ");
    fflush(stdout);  /* The prompt might not appear if you don't flush */
    /*
     * Note the field width in the format below.  Without it, a user can
     * easily cause a buffer overflow.
     */
    scanf("%20s", str);

    /*
     * The strategy for each element to delete (there may be more than one)
     * is to free the element's name components (else their allocated memory
     * leaks), copy the last (at that time) element's components into
     * place (for the name components, just the pointers), and later
     * realloc to shrink the overall size to exactly fit the remaining
     * elements (once we know how many that is).
     */
    for (i = 0; i < *the_size; )
    {
        if (strcmp(str, (*lastname)[i]) == 0)
        {
            printf("Deleting Record for %s %s...\n", (*firstname)[i], (*lastname)[i]);
            free((*firstname)[i]);
            free((*lastname)[i]);
            (*firstname)[i] = (*firstname)[*the_size - 1];
            (*lastname)[i] = (*lastname)[*the_size - 1];
            (*score)[i] = (*score)[*the_size - 1];
            *the_size -= 1; /* not the same as *the_size-- */
            /* don't increment i, else we miss testing the new i'th element */
        } else {
            i += 1;
        }
    }
    if (*the_size != initial_size)
    {
        void *temp;

        /*
         * Always check the return value of realloc(), even when you're
         * shrinking the allocation.  Usually, though, you'd want at least
         * some kind of diagnostic in the event of failure.
         */
        temp = realloc(*firstname, sizeof(char *) * (*the_size));
        if (temp)
        {
            *firstname = temp;
        }
        temp = realloc(*lastname, sizeof(char *) * (*the_size));
        if (temp)
        {
            *lastname = temp;
        }
        temp = realloc(*score, sizeof(float) * (*the_size));
        if (temp)
        {
            *score = temp;
        }
    }
    else  /* there is no match */
    {
        printf ("Sorry, no available record for %s",str);
    }
}

您的main()会这样称呼:

delete_record(&firstname, &lastname, &score, &number_of_records);

add_record()需要进行类似的更改,尽管你确实有一个单独的问题我已经在那里调用了16个以上的条目数。

此外,您通过使用名字,姓氏和分数的单独数组为自己做额外的工作。定义包含所有三个的struct要容易得多,并且只使用一个动态数组,其元素是struct的实例。