【问题标题】:Creating and initializing an array of user defined type in C在 C 中创建和初始化用户定义类型的数组
【发布时间】:2012-11-29 13:41:25
【问题描述】:

我最近开始学习 C,如果我的问题非常基本或不清楚,敬请见谅。

我将 3 个命令行值传递给我的程序。 (类型,长度,VAL)。使用这些值,我想在 TYPE 类型和长度 LENGTH 的堆上创建一个数组,并将所有元素初始化为 VAL。比如

float 8 4 : 将创建一个包含 8 个元素的浮点数组,每个元素的值为 4。

char 4 65 : 将创建一个包含 4 个元素的 char 数组,每个元素的值都为 'A'。

确定长度是直截了当的。但我正在努力确定如何初始化指向数组和 VAL 的指针。

以下是我迄今为止尝试过的(未成功)。我正在初始化一个 char 指针,但是我知道这是不正确的,因为我可能需要一个 float 或 int 指针。我还使用基于 TYPE 的第一个字符的 switch 语句,对我来说这似乎很 hacky。

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

        int LENGTH;
        char *ptr;

        LENGTH = atoi ( argv[2] );


        switch( *argv[1] ){
                case 'f':
                        printf("Type is float\n");
                        ptr = (float *)malloc(LENGTH * sizeof(float));
                        break;
                case 'c':
                        printf("Type is char\n");
                        ptr = (char *)malloc(LENGTH * sizeof(char));
                        break;
                case 'i':
                        printf("Type is int\n");
                        ptr = (int *)malloc(LENGTH * sizeof(int));
                        break;
                default:
                        printf("Unrecognized type");
                        break;
        }

        while( i < arrayLength ){
                *ptr[i] = *argv[3];
                i++;
        }

        free ( ptr );

        return 0;
}

我还可以看到问题的初始化元素存在问题。初始值取决于类型,因此需要进行转换。

我想知道的是,在事先不知道 TYPE 的情况下,如何创建指针或初始化值?我可能完全从错误的方向看待这个问题,所以任何建议都将不胜感激。

谢谢

【问题讨论】:

  • 为什么?您真正想做什么?创建未知类型的数组不是很有用。您需要先了解类型,然后才能知道哪些操作可用。
  • 这是我在课程中发现的一个问题。我认为创建未知类型的数组没有用,这意味着我处理问题的方式本身是不正确的。

标签: c arrays pointers malloc command-line-arguments


【解决方案1】:

这是修改后的代码以执行您所要求的操作(请参阅下面的注释):

#include <stdio.h>
#include <stdlib.h>
#include <string.h> // for memset()

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

        int LENGTH;
        int i;

        void *ptr = NULL;

        if(argc < 4)
        {
            fprintf(stderr, "Usage: %s type len data\n", argv[0]);
            return 1;
        }

        LENGTH = atoi ( argv[2] );


        switch( *argv[1] ){
                case 'f':
                        printf("Type is float\n");
                        ptr = (float *)malloc(LENGTH * sizeof(float));
                        for(i = 0; i < LENGTH; i++)   // Cannot use memset in that case: memset(3) only works with integers.
                            ((float *)ptr)[i] = (float)atof(argv[3]);
                        for(i = 0; i < LENGTH; i++)
                            printf("[%d]: %lf\n", i, ((float *)ptr)[i]);
                        break;
                case 'c':
                        printf("Type is char\n");
                        ptr = (char *)malloc(LENGTH * sizeof(char));
                        memset(ptr, (char)atoi(argv[3]), LENGTH);
                        for(i = 0; i < LENGTH; i++)
                            printf("[%d]: %c\n", i, ((char *)ptr)[i]);
                        break;
                case 'i':
                        printf("Type is int\n");
                        ptr = (int *)malloc(LENGTH * sizeof(int));
                        memset(ptr, (int)atoi(argv[3]), LENGTH);
                        for(i = 0; i < LENGTH; i++)
                            printf("[%d]: %d\n", i, ((int *)ptr)[i]);
                        break;
                default:
                        printf("Unrecognized type\n");
                        break;
        }

        if(ptr == NULL)
            return 1;

        free ( ptr );

        return 0;
}

不过,正如其他人指出的那样,这样做毫无意义。之所以使用类型,是为了知道两件事:

  1. 数据大小
  2. 如何解释它

您在这里尝试实现的是笨拙地尝试完成操作系统的工作。

需要明确的是:内存是一个连续的存储,操作系统将其标记为“已分配”(通过 malloc(3) 之类的函数)。当它这样做时,程序必须知道数据的类型才能知道要读取什么(指针只是一个地址)以及如何解释它(ints 的存储方式与floats 不同是)。

如果你真的有兴趣学习这些东西(数据是如何存储的,计算机是如何工作的,以及比 C# 更低的水平),我建议你阅读:

  1. MIPS assembly tutorial(谷歌的第一个相关答案,任何 MIPS asm 课程都可以)

  2. x86 Assembly, 6th ed. 如果你有钱的话。

另外,如果你想测试 MIPS 程序集,mars 是一个 MIPS 程序集模拟器,可以正常工作(并且是免费的)。

我很高兴看到人们仍在努力学习 C。

【讨论】:

    【解决方案2】:

    当需要 C 中的变量可以容纳不同的类型时,您需要使用联合,例如:

    typedef union {
        float f;
        int i;
        char c;
    } arg_type;
    

    然后,声明为arg_type argument; 的变量将大到足以容纳最大的联合成员。它们像结构成员一样被访问,例如:

    argument.f = 0.0;
    argument.c = '\n';
    argument.i = 12;
    

    但是,您需要跟踪您在其中存储的类型,因为编译器无法知道您使用的是哪种类型。一旦定义了联合,你就可以做

    char var_type = 'f';
    arg_type *p = mallocLENGTH * sizeof(arg_type);
    p[0].f = atof(argv[i])
    

    不过,要确保您的操作正确,这有点痛苦。例如:

    argument.f = 1234.56;
    putchar(argument.c);
    

    将编译但其行为未定义。将内容保留为 argv 字符形式并保存解析通常更容易,直到您想要使用特定类型。

    【讨论】:

    • 不是类型双关语通过联合实现定义,而不是未定义
    • 我不打算深入研究标准,但是当从存储为浮点数的联合中读取 int 成员时,给定机器上的给定编译器很可能会给出确定性结果。如果这就是“定义的实现”的意思,那很好,但是如果有任何有意义的方式可以被认为是“有用的定义”,我看不到它。
    • “实现定义”是指标准所说的:标准未指定的行为,但实现必须实现和记录。所以它不仅仅是“最可能确定的”,它的行为会记录在编译器手册的某个地方。话虽如此,我同意一般来说这不是一件有用的事情。
    • 这个解决方案似乎比我建议的要优雅得多。谢谢!但是我遇到了一些问题。指针似乎创建得很好,但使用 p[0].f = atof(argv[i]) 初始化任何值会导致“未定义类型的无效使用”错误。再次感谢您的帮助!
    【解决方案3】:

    用 C 编写这样通用的东西很难。C++ 更合适。但是如果你在 C 中需要它,那么我要做的是声明 3 个不同的指针,float*、int*、char*。我会分配并初始化与用户输入相对应的那个。但请记住,您需要携带一些变量来标记整个程序中所选类型的含义,并在需要使用数组时使用if 语句。

    【讨论】:

    • 我不确定 C++ 是否值得推荐。特别是对于开始学习编程的人。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-02
    • 2013-05-04
    相关资源
    最近更新 更多