【问题标题】:Initialize User defined parameters value for Array in C在 C 中为数组初始化用户定义的参数值
【发布时间】:2020-11-02 06:55:07
【问题描述】:

我在教科书中了解到,初始化数组的方法只有几种。

方法一:

int ary[5] = {1,2,3,4,5};

方法二:

int ary[] = {1,2,3,4,5};

但是如果我想将用户定义的值输入到数组中怎么办。这段代码可以吗?谁能教我一个更好的方法来做到这一点?

#include<stdio.h>
#include<conio.h>

int main()
{
    int i, n;
    printf("Enter array parameter: \n");
    scanf("%d",&n);
    int a[n]; 
    int b[n];
    
    for(i=0;i<n;i++)
    {
        printf("Enter value for array a[%d]: \n",i+1);
        scanf("%d",&a[i]);
    }
    n -= 1; 
    for(i=0;i<=n;i++)
    {
        b[i]=a[n-i];
    }
    printf("Value of array b[] is: \n");
    for(i=0;i<=n;i++)
    {
        printf("%d ",b[i]);
    }
    for(i=0;i<=n;i++)
    {
        printf("\narray b[%d] = %d ",i,b[i]);
    }
    
    getch();
}

【问题讨论】:

  • int ary[5] {1,2,3,4,5}; - 你不能在 C 中做到这一点。它在 C++11 中有效
  • 您的代码很脆弱。您不能使用任何用户输入功能,例如scanf("%d",&amp;n) 正确,除非您检查返回,例如if (scanf("%d",&amp;n) != 1) { fputs ("error: invalid integer input.\n", stderr); return 1; } 否则,请理解您使用的是可变长度数组 (VLA),并非所有从 C11 开始的编译器都支持它。不要包含conio.h,那个古老的 DOS 标头使您的代码 100% 不可移植到窗口以外的任何地方。 stdio.h 提供您所需的一切。只需使用getchar() 来保持窗口打开,而不是使用getch()
  • 我理解了,但 C11 是什么?我使用 Dev C++ 5.11。人们一直在谈论的编译器版本是什么?
  • 您的输出只是纯文本。无需添加屏幕截图,而只需将其作为文本复制并粘贴到您的问题中。
  • C11 是 C 标准的 2011 版本。但要注意,第一条评论没有提到 C11,而是 C++11,它是 2011 版本的 C++。众所周知,C 和 C++ 是非常不同的语言。

标签: arrays c initialization declaration


【解决方案1】:
int ary[5] {1,2,3,4,5};

这不是有效的数组初始化。应该是

int ary[5] = {1,2,3,4,5};

注意等号。

否则,您的数组初始化对 C99 及更高版本有效。普通 c 不允许使用变量来初始化数组大小。

【讨论】:

  • 即 ISO/IEC 9899:1999[9] C 标准。有关 C 标准的详细信息,请参阅链接:link
【解决方案2】:

你有很多问题,未定义的行为是最关键的:

n -= 1; 
for(i=0;i<=n;i++)
{
    b[i]=a[n-i];
}

i == n 时,您索引一个超出b[] 数组末尾的位置。 C 中的所有数组都是零索引的,所以你的循环限制是0 &lt;= i &lt; n。通过使用i&lt;=n,您循环了太多次。

您未能检查任何用户输入的返回。尝试输入'"one"',或者不小心点击'r' 而不是4,看看会发生什么。您必须为每个用户输入检查返回并处理错误。如果用户输入1, 2, 'r', 5 会发生什么?您还必须清空 stdin 中的任何无关字符,否则它们会在您下次输入时再次咬您。

不要使用conio.h。这使您的代码 100% 不可移植。您可以根据需要使用stdio.h 中的功能。

也就是说,当您需要执行重复性任务(例如接受整数输入)时,请编写一个简短的函数来执行此操作。您可以编写一个指针,该指针指向要填充的整数,以及一个指向提示的指针以显示并在成功时返回0,如果用户通过使用 Ctrl 生成手动 EOF 来取消输入,则返回 -1 +d 在 Linux 上或 Ctrl+z 在 Windows 上。您可以添加一个辅助函数来清空stdin,例如

void empty_stdin (void)
{
    int c = getchar();
    
    while (c != '\n' && c != EOF)
        c = getchar();
}

int getint (int *n, const char *prompt)
{
    int rtn;
    
    for (;;) {
        fputs (prompt, stdout);
        rtn = scanf ("%d", n);
        if (rtn == EOF) {
            fputs ("  (user canceled input)\n", stderr);
            return -1;
        }
        else if (rtn == 0) {
            fputs ("  error: invalid integer input.\n", stderr);
            empty_stdin();
        }
        else
            break;
    }
    empty_stdin();
    
    return 0;
}

a[] 中读取数组并在b[] 中反转它的程序的其余部分很简单:

int main (void) {
    
    int i = 0, n;
    
    if (getint (&n, "Enter array parameter: ") == -1)
        return 0;
    
    int a[n], b[n];
    
    for (i = 0; i < n; i++) {
        char buf[128];
        sprintf (buf, "enter value for a[%d]: ", i+1);
        if (getint (&a[i], buf) == -1)
            return 0;
    }
    for (i = 0; i < n; i++)
        b[i] = a[n - 1 - i];
    
    puts ("\nvalue of array b[]:");
    for (i = 0; i < n; i++)
        printf (" %d", b[i]);
    putchar ('\n');

#if defined (_WIN32) || defined (_WIN64)
    getchar();  /* hold terminal open on windows - type any char, hit return */
#endif
}

使用/输出示例

所有有效输入:

$ ./bin/getarray
Enter array parameter: 5
enter value for a[1]: 5
enter value for a[2]: 4
enter value for a[3]: 3
enter value for a[4]: 2
enter value for a[5]: 1

value of array b[]:
 1 2 3 4 5

输入错误:

$ ./bin/getarray
Enter array parameter: foo
  error: invalid integer input.
Enter array parameter: 5
enter value for a[1]: 5
enter value for a[2]: four
  error: invalid integer input.
enter value for a[2]: 4
enter value for a[3]: 3
enter value for a[4]: I'm getting tired of this game...
  error: invalid integer input.
enter value for a[4]: 2
enter value for a[5]: 1

value of array b[]:
 1 2 3 4 5

用户取消输入:

$ ./bin/getarray
Enter array parameter: 5
enter value for a[1]: 5
enter value for a[2]: 4
enter value for a[3]: 3
enter value for a[4]:   (user canceled input)

查看一下,如果您还有其他问题,请告诉我。

【讨论】:

    【解决方案3】:

    正如有人指出的,第一种方法在 C 中不起作用。但它是有效的 C++。

    int ary[] = {1,2,3,4,5}; 的第二种工作方式,数组ary 在编译时初始化为 5 项(甚至在您运行程序之前)。内存是在栈上分配的。

    您也可以像int ary[6] = {1,2,3,4,5}; 那样做,您可以在其中指定您希望数组的长度。然后您可以访问 ary[5] 并将其更改为您想要的任何值(默认为 0)。

    注意ary[6] 超出范围,因为数组的元素从 0 变为 5。

    在您的代码中,您使用了一种称为可变长度数组 (VLA) 的东西,正如 Wikipedia 所指出的,它们在堆栈上分配有自动存储持续时间。这意味着当您声明它们的函数结束时,它们的生命周期也结束(它们在您声明它们的函数范围内是本地的)。要了解为什么这很重要,请考虑以下代码:

    int *func(int n) {
       int v[n]; // variable length array
    
       v[0] = 1;
       v[1] = 2;
       v[2] = 3;
    
       return v;   // this will not work, variable length arrays will be destroyed
                   // when the function they belong to goes out of scope
    }
    
    int main() {
       int *v;
       v = func(3);  // when you will try to access the memory in v, it will be undefined 
                     // behaviour (can seg fault, the memory is not yours anymore)
       // print values in v
       return 0;
    }
    

    要解决此问题,您需要使用在stdlib.h 中定义的malloc

    int *func(int n) {
       int *v = malloc(sizeof(int) * n); // allocate memory for n elements
    
       v[0] = 1;
       v[1] = 2;
       v[2] = 3;
    
       return v;   // this will now work
    }
    
    int main() {
       int *v;
       v = func(3); // this will work
       // print values in v
       free(v);
       return 0;
    }
    

    之所以可行,是因为使用malloc 分配的内存是在堆上分配的,这意味着它在程序的整个持续时间内都存在,除非您自己解除分配(这意味着您需要在最后使用释放内存函数free)。

    【讨论】:

      【解决方案4】:

      C中的数组初始化:

      int ary[5] {1,2,3,4,5};
      

      这是 c 中的无效语法(在 c++ 中有效)。

      初始化 c 数组有 3 种方法:

      1. 用大括号 {} 像你一样 (int ary[] = {1,2,3,4,5};)
      2. 在 for 循环中逐一迭代数组元素并初始化每个元素。
      3. via : memset(void *str, int c, size_t n);,用于将整个数组填充为相同的值。例如如果我想用0x0 初始化数组,那么memset 调用会像:memset(arr, 0x0, sizeof(arr));

      关于你的代码:

      1. 对于代码中的每个scanf(..),您必须在代码中取得任何进展之前检查和验证scanf("%d",&amp;n);、返回/错误和n 中的用户输入。你怎么查?稍后见。
      2. 在您的情况下,最好限制用户输入 n 并在定义的范围内验证 n (N_min &lt;= n &lt;= N_max),而不是在 n 设置为的情况下导致在堆栈上分配巨大的数组用户的最大值!
      if(scanf("%d",&n) == EOF){
          /*errors ocuured*/
          f_invoke_error_handling_rotuine(); 
      }
      

      f_invoke_error_handling_rotuine(..) 中,您可以执行代码需要执行的任何操作,可能是带有错误消息的abort(),可能会将默认值设置为n...等等。

      请参阅 man7.org 中的 memsetscanf 参考: scanf, memset

      【讨论】:

        【解决方案5】:

        “这段代码可以吗?谁能教我一个更好的方法?”

        从句法的角度来看,这段代码是可以的(您纠正了错误的初始化),但由于您使用了可能不受任何实现支持的 可变长度数组 (VLA),因此不可移植(它们由我提供编译器扩展)并且仅完全符合 C99 标准。

        更好的方法是使用malloc() 并为堆上的数组分配内存。

        这还有一个优点,即您可以根据需要调整数组的大小(使其更大 f.e.),并在不再需要时free() 数组的内存。

        相关内容:


        int i, n;
        
        printf("Enter amount of array elements: \n");
        if (scanf("%d",&n) != 1)
        { 
            fputs("Error at input!", stderr);
            return EXIT_FAILURE;
        }
        
        int* a = malloc ( sizeof(*a) * n );
        if (!a)
        { 
            fputs("Error at allocation for a!", stderr);
            return EXIT_FAILURE;
        }
        
        int* b = malloc ( sizeof(*b) * n );
        if (!b)
        { 
            fputs("Error at allocation for b!", stderr);
            return EXIT_FAILURE;
        }
        
        for (i = 0; i < n; i++)
        {
            printf("Enter value for array a[%d]: \n", i);
            if (scanf("%d", &a[i]) != 1)
            { 
               fprintf(stderr, "Error at input for a[%d]!", i);
               return EXIT_FAILURE;
            }
        }
        
        ...
        
        free(a);
        free(b);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-11-29
          • 2019-05-23
          • 2013-05-04
          • 1970-01-01
          • 2020-06-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多