简体   繁体   中英

Char array pointer (pass by 'reference') not assigning (after sscanf variable change)

I've had these functions working fine, before I change one of the Strings via sscanf.

Ie, If I have a char[] NAME that I create in the (called) function, and initialize it to a string (eg "fred";), then assign that to the passed parameter -> The parameter changes in its parent function.

But if I try to change the string (NAME) with input from fgets -> sscanf, then assign it to the passed array, the parent function prints nothing ('blank').

I'm trying to figure out the char-array vs. pointer situation. And I thought I had it; I could change the passed String with a local (initialized) string. But once I assign some 'user input' to the local string, the parameter-string isn't being altered properly.

Here are my functions...

  int enter_record(FILE *fp, const char *prompt[]) {
     char *name[BUFFER] = {"wram"};
     int score = 12; /* REPLACE : Put 0 or something ! */

     fprintf(fp, "Enter_record()\n%s\n", *prompt); /* This is only here to use variables; so wil compile */

     printf("Score: %d\n", score);
     printf("Name:%s\n", *name);

     get_name(prompt, name);

     printf("Post-Change Name:%s\n", *name);

     return 1;
  }

  int get_name(const char *prompt[], char *fullName[]) {
     int n;   /* The number of strings scanned */

     char line[BUFFER];   /* The line that is scanned */
     char firstString[BUFFER];  /* First scanned string */
     char midString[BUFFER];    /* Second scanned string (',' ?) */
     char lastString[BUFFER];   /* Last string scanned */

     /* Function call validation */
     printf("get_name()\n%s\n", *prompt); 

     if( !fgets(line, BUFFER, stdin) ) {
        clearerr(stdin);
        return 0;   /* If *EOF* return FALSE */
     }

     if( !(n = sscanf(line, " %[a-zA-Z-] %[a-zA-Z-,] %[a-zA-Z-] %*s", firstString, midString, lastString)) ) {
        printf("Wrong format!!!!\n");
     }

     printf("n:%d\n", n);
     printf("firstString:%s\n", firstString);


     *fullName= firstString;

     return 1;
  }

Below I'll include the remainder of my code (main, menu, etc..)

  #include <stdio.h>

  #define BUFFER 512
  #define NAMELENGTH 14

  int menu(const char *choices[], const char *prompt[]);
  int enter_record(FILE *fp, const char *prompt[]);
  int get_name(const char *prompt[], char *fullName[]);

  int main(int argc, char *argv[]) {
     FILE *ofp;

     const char *menuOptions[] = {"Enter record", "Display records", "Quit", '\0'};   
     const char *prompt[2] = {"> "}; /* Prompt for user to enter input */
     int menuResult;

   /* Command line ARGS validation */
     /* 
        VALIDATES main() is called correctly from console.
     */
           /* Ensure program envoked correctly */
           if(argc != 2) {
              fprintf(stderr, "usage: %s [destination file]\n", argv[0]);
              return 1; /* Incorrect-envocation error */
           }

           /* Open file for student records */
           if( (ofp = fopen(argv[1], "wb+")) == 0) { /*If there's an error */
              perror("fopen");
              return 2;   /* File-open error */
           }   
     /*
        ///// END VALIDATION for main() call.
     */



     printf("main()\n");

     while(1) {
        menuResult = menu(menuOptions, prompt);

        switch(menuResult) {
           case 0: /* Menu returned FALSE;  */
              return 0;
           case 1: /* Enter Record; choice */ 
              enter_record(ofp, prompt);  
              break;
           default:
              break;
        }
     }

     return 0;
  }

  int menu(const char *choices[], const char *prompt[]) {
     int i; /* Used to print menu "choice" "Number" */
     char input[BUFFER]; /* The line scanned by fgets */
     int choice = 0;   /* What the user chooses */
     int choiceLen = -1;  /* Used to track how many elements in "choices[]"
                             This is used because (in my code) 'QUIT' is always last option
                          */

     /* Calculates # elements in *choices[] */
        while (choices[++choiceLen] != NULL) {};  
        printf("\n");

        for (i=0; choices[i] != '\0'; i++){
           printf("%1d. %s\n", i+1, choices[i]);
        }

     while(1) {
        printf("%s", *prompt);

        if(!fgets(input, BUFFER, stdin)) {
           clearerr(stdin);
           return 0;
        }
        sscanf(input, "%d %*s", &choice);
        if(choice == choiceLen) /* QUIT is always last option (choiceLen) */
           return 0;   /* Return QUIT */
        else if ((choice > 0) && (choice < choiceLen)) /* 0 is invalid choice */
           return choice; 
     }

     return 0;
  }

If some one could point out to me where I have gone wrong; Why would changing the variable with sscanf change the way the parent's-variable is affected? My confusion lies in the fact that it worked when the 'child-string' is pre-initialized (Eg char name[BUFFER] = {"Bob"}; ), but not after I'd altered it with sscanf.

If my problem lies elsewhere, please let me know as well.

A pointer in any direction would be appreciated.

Cheers.

The problem is here:

char line[BUFFER];
char firstString[BUFFER];
char midString[BUFFER];
char lastString[BUFFER];

These are local variables, which means the character arrays are in the stack frame; this memory is freed when function get_name finishes. It is wrong to expose a pointer to this memory to the outside world (ie function enter_record ).

The easiest solution is to allocate the string buffers in static memory:

static char line[BUFFER];
static char firstString[BUFFER];
static char midString[BUFFER];
static char lastString[BUFFER];

This approach does have a few disadvantages.

  1. The string buffers occupy memory for the entire duration of the program.
  2. The buffer size must be static; it cannot be a dynamic number of characters, determined in runtime.

Alternatives are:

  1. Define the string buffers (as local variables) in enter_record instead of get_name , which extends their lifetime accordingly. Let enter_record pass pointers to the buffers upon calling get_name .
  2. Use malloc . I must say I'm not in favor of having get_name allocate memory, then let enter_record free it; it kind of hurts maintainability.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM