【问题标题】:Modifying a string inside a function by passing by reference In C通过在 C 中的引用传递来修改函数内部的字符串
【发布时间】:2021-12-04 23:15:51
【问题描述】:

所以,我遇到了一个我不太了解的问题。请善待我正在尝试自学C!

我有一个名为 secureInput() 的函数,它接受一个指向字符串的指针和一个 size,这样,当用户必须输入一些输入我可以确定没有缓冲区溢出。现在,问题是我想修改字符串而不复制它,而是直接通过它的内存地址修改它,但是当用户输入中的第二个字符被分配时它就会崩溃。查看 cmets 以了解它在哪里崩溃。

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

int secureInput(char **str, int size);

int main(int argc, const char * argv[]) {
    char *mystring = NULL; // Declaring it a null so that I use malloc later
    secureInput(&mystring, 10);
    printf("%s\n", mystring);

}


int secureInput(char **str, int size)
{
    *str = (char*)malloc(sizeof(char) *size);  // Because **str is a null pointer, I use malloc to allocate memory.
    if (*str == NULL)
        return -1;

    int c = 0;
    unsigned int count = 0;
    while((c = getchar()) != '\n' && count < size)
        /* Here is where it crashes.
         * But changing the bellow line as : *str[0][count++] = c;
         * works as expected. Also, using a temporary pointer
         * and later using it to replace *str, is also working
         */
    
        *str[count++] = c;
    *str[count] = '\0';

    return 0;
}

【问题讨论】:

  • *str[count++] 更改为(*str)[count++] 和另一个。
  • 您的str[count] 从传递给函数的指针获取偏移量,而不是从分配的指针获取。然后* 取消引用非法指针。通过返回指针(或NULL)而不是通过参数来编写函数会更容易。
  • @Liwinux 都是关于优先规则的!您可以将其与数学优先级进行比较。在 C 中,数组下标优先于指针的取消引用(请参阅此处的列表:en.cppreference.com/w/c/language/operator_precedence)。
  • 这可以通过使用局部变量 (char *s = malloc(); *str = s;) 来简化一点,然后在函数的其余部分使用 s。这也会使错误处理更容易一些(直到最后才分配给*str),因为在您知道自己完成之前不会对调用者的数据进行任何更改。
  • 另请注意,(*str)[count] 可能会访问超过分配空间的末尾。您应该分配一个额外的字节或少读一个字符。

标签: c malloc pass-by-reference


【解决方案1】:

至少这个问题:

减一

str[count] = '\0'; 可以在数组边界外写入导致 OP 的麻烦。建议count &lt; size --> count + 1 &lt; size.

并非总是读取整行

读取部分行会导致麻烦。

阅读整行并报告结果如何?让调用代码提供固定大小的缓冲区。

区分读取空行和文件结尾。

优雅地处理size == 0

// EOF: end-of-file with no input
// EOF: input error
// 0: input, but too much
// 1: Success
int secureInput(char *str, size_t size) {
  if (str == NULL) {
    size = 0;
  }

  bool too_many = false;
  size_t count = 0;
  int c;
  while((c = getchar()) != '\n') {
    if (c == EOF) {
      if (feof(stdin) && count > 0) {
        break;
      }
      if (size > 0) {
        str[0] = '\0';
      }
      return EOF;
    }

    if (count + 1 < size) {
      str[count++] = c;
    } else {
      too_many = true;
    }
  }

  if (count < size) {
    str[count] = '\0';
  } 
  return count < size && !too_many;
}

【讨论】:

    【解决方案2】:

    首先,您可以将字符串作为char* 传递,而不需要char**。当作为参数传递时,这通常用于字符串数组。然后,如果你想使用一个固定大小的数组,一个缓冲区,它有一个恒定的、预定义的大小,不要使用 malloc。动态内存分配总是低效且有风险的,所以只有在绝对必要时才使用它。

    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    #include <string.h>
    
    #define BUFFER_SIZE 10
    
    int secureInput(char *str, int size);
    
    int main(int argc, const char * argv[]) {
        char mystring[BUFFER_SIZE]; // Declaring it a null so that I use malloc later
        memset(mystring, 0, BUFFER_SIZE);
        secureInput(mystring, BUFFER_SIZE);
        printf("%s\n", mystring);
    }
    
    int secureInput(char *str, int size) {
    
        char c = 0;
        unsigned int count = 0;
        
        c = getchar();
        while(c != '\n' && count < size - 1) {
            str[count++] = c;
            c = getchar();
        }
        str[count] = '\0';
    
        return 0;
    }
    

    编辑:

    我可以看到指针算法仍然存在一些混淆。下面是一些地址打印和一个小图,希望对你有帮助:

    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    
    int secureInput(char **str, int size);
    
    int main(int argc, const char * argv[]) {
        char *mystring = NULL; // Declaring it a null so that I use malloc later
        secureInput(&mystring, 10);
    }
    
    
    int secureInput(char **str, int size) {
        *str = (char*)malloc(sizeof(char) *size);  // Because **str is a null pointer, I use malloc to allocate memory.
        (*str)[0] = 'a';
        (*str)[1] = 'b';
        (*str)[2] = 'c';
        (*str)[3] = 0;
        printf("address of the pointer that points to a pointer that points the first char of the array : %p\n", &str);
        printf("value of the pointer that points to a pointer that points to the first char of the array : %p\n", str);
        printf("address of the pointer that points to the first char of the array : %p\n", &(*str));
        printf("value of the pointer that points to the first char of the array : %p\n", *str);
        printf("address of the first char of the array: %p\n", &(**str));
        printf("address of the seconds char of the array: %p\n", &((*str)[1]));
        printf("value of the first char of the array : %c\n", **str);
        printf("value of the second char of the array : %c\n", *(*str + 1));
        printf("value of the second char of the array : %c\n", (*str)[1]);
        printf("*str[1] is the same as *(str[1]), which runs to a segmentation fault\n");
        return 0;
    }
    

    输出:

    address of the pointer that points to a pointer that points the first char of the array : 0x7ffce24333f8
    value of the pointer that points to a pointer that points to the first char of the array : 0x7ffce2433430
    address of the pointer that points to the first char of the array : 0x7ffce2433430
    value of the pointer that points to the first char of the array : 0x55a91985a2a0
    address of the first char of the array: 0x55a91985a2a0
    address of the seconds char of the array: 0x55a91985a2a1
    value of the first char of the array : a
    value of the second char of the array : b
    value of the second char of the array : b
    *str[1] is the same as *(str[1]), which runs to a segmentation fault
    
    
         0x7ffce24333f8              0x7ffce2433430              
       +----------------+          +----------------+          +----------------+
       | 0x7ffce2433430 | -------> | 0x55a91985a2a0 | -------> |       a        | 0x55a91985a2a0
       +----------------+          +----------------+          +----------------+
       
                                                               +----------------+
                                                               |       b        | 0x55a91985a2a1
                                                               +----------------+
       
                                                               +----------------+
                                                               |       c        | 0x55a91985a2a2
                                                               +----------------+
    

    关键是你取消引用哪个指针很重要。

    【讨论】:

    • 感谢您的解决方案,我应该说我的目标是在函数中初始化我的字符串,然后在 SecureInput() 中使用 malloc 函数。所以如果你记住这一点,这个函数的第一个参数应该是一个双指针,一个字符串是一个指向第一个字符的指针,所以如果我想要一个指向这个的指针,我们必须使用一个双指针对吗?
    • 我刚刚看到,你编辑的答案,这真的很有帮助,谢谢这个人!
    • 不客气。没错,如果要修改函数外部的值,则将指针传递给它。如果要修改在函数外部使用的指针,则将指针传递给它,因此是双指针。
    • 你说得对,我没有注意到计数与尺寸的问题。我更正了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-24
    • 2014-08-11
    • 2017-07-25
    • 2016-07-05
    • 2018-09-01
    • 1970-01-01
    • 2011-07-08
    相关资源
    最近更新 更多