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

How to properly pass array pointers to function in C

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

我不明白的是为什么(add)和(delete)不在main函数中做修改,所以当我使用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");
        }
    }
}

首先,您将 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。

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

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

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

无论 any_type 是什么类型或 any_value 是什么值,调用者永远不会检测到任何变化。但是,请仔细注意那个和这个之间的区别:

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

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

您的代码出现了一些此类问题,其中一些问题与您的主要问题有关。最重要的是,这些与您对列表中元素数量的记录有关(main() 中的变量 number_of_records)。您的添加和删除功能或多或少可以正常工作,除了它们无法将修改后的列表大小传回 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.

的实例会容易得多