Char 数组指针(通过 'reference' 传递)未分配(sscanf 变量更改后)

标签 c string pointers scanf c89

在我通过 sscanf 更改其中一个字符串之前,这些函数工作正常。

即,如果我在(调用的)函数中创建了一个 char[] NAME,并将其初始化为字符串(例如“fred”;),然后将其分配给传递的参数 -> 参数更改它的父函数。

但是,如果我尝试使用 fgets -> sscanf 的输入更改字符串(NAME),然后将其分配给传递的数组,则父函数不会打印任何内容(“空白”)。

我正在尝试找出字符数组与指针的情况。我以为我已经拥有了;我可以用本地(初始化)字符串更改传递的字符串。但是,一旦我将一些“用户输入”分配给本地字符串,参数字符串就不会被正确更改。

这是我的功能...

  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;
  }

下面我将包含我的代码的其余部分(主、菜单等......)

  #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;
  }

如果有人可以向我指出我哪里出错了; 为什么使用 sscanf 更改变量会改变父变量受影响的方式? 我的困惑在于,它在“子字符串”预初始化时起作用(例如 char name[BUFFER] = {"Bob"}; ),但在我用 sscanf 更改它之后就不起作用了。

如果我的问题出在其他地方,也请告诉我。

任何方向的指针将不胜感激。

干杯。

最佳答案

问题出在这里:

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

这些是局部变量,这意味着字符数组位于堆栈帧中;当函数 get_name 完成时,该内存将被释放。向外界公开指向该内存的指针(即函数 enter_record)是错误的。

最简单的解决方案是在静态内存中分配字符串缓冲区:

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

这种方法确实有一些缺点。

  1. 字符串缓冲区在整个程序运行期间占用内存。
  2. 缓冲区大小必须是静态的;它不能是在运行时确定的动态字符数。

替代方案是:

  1. enter_record 而不是 get_name 中定义字符串缓冲区(作为局部变量),从而相应地延长其生命周期。让 enter_record 在调用 get_name 时将指针传递给缓冲区。
  2. 使用malloc。我必须说我不赞成让 get_name 分配内存,然后让 enter_record 释放它;这有点损害可维护性。

关于Char 数组指针(通过 'reference' 传递)未分配(sscanf 变量更改后),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24363370/

相关文章:

objective-c - 一般学习 Objective-C 和 Xcode 时注意到的好奇心

c - Win32 : Forward child message to parent - return value is different

指向地址的常量指针或指向值的指针

c++ - std::wstring:与 + 的连接无效

C++和多指针

c - 如何创建一个函数,为指针分配10个字节,然后检查指针是否已分配,返回 boolean 值?

c - 以下 C 代码的输出是 T T ,为什么不是 t t?

string - 将字符串拆分为字符数组?

java - 检查一个字符串是否有四个连续的字母升序或降序

c++ - 函数指针?