【问题标题】:(int*) when dynamically allocating array of ints in c(int*) 在 c 中动态分配整数数组时
【发布时间】:2017-04-09 12:57:45
【问题描述】:

所以我有点困惑如何创建一个函数,该函数将返回一个指向C 中整数数组的指针。我知道你不能这样做:

int* myFunction() {
  int myInt[aDefinedSize];
  return myInt; }

因为这是返回一个指向局部变量的指针。 所以,我想到了这个:

int* myFunction(){
  int* myInt = (int) malloc(aDefinedSize * sizeof(int));
  return myInt; }

这给出了错误:警告从指针转换为不同大小的整数 这意味着使用它,它有效:

int* myFunction(){
  int* myInt = (int*) malloc(aDefinedSize * sizeof(int));
  return myInt; }

我感到困惑的是: 向我解释 malloc 之前的 (int*) 是为了这样做:它告诉编译器正在分配的内存的数据类型是什么。例如,当您逐步遍历数组并且编译器需要知道要增加多少字节时,就会使用它。 所以,如果我给出的这个解释是正确的,那么内存是不是分配给了 aDefinedSize 个指向整数的指针,而不是实际上的整数?因此,myInt 不是指向整数指针数组的指针吗? 理解这一点的一些帮助会很棒。谢谢!!

【问题讨论】:

  • 不要强制返回malloc!除此之外,您的代码还不错。
  • 请不要通过将} 附加到函数的最后一行来“保存行”。众所周知,它会导致资深程序员癫痫发作。
  • 如果你忘记包含 那么 malloc 被隐式声明为返回一个 int 并且如果你不强制转换它的返回值,那么编译器将打印一个警告,以便你知道你忘记了包括它。
  • 显然没有......

标签: c arrays pointers memory-management


【解决方案1】:

那么,如果我给出的这个解释是正确的,那么内存是不是为 aDefinedSize 个指向整数的指针分配的,而不是实际上的整数?

不,您向malloc 询问aDefinedSize * sizeof(int) 字节,不是 aDefinedSize * sizeof(int *) 字节。这就是你得到的内存大小,类型取决于用于访问内存的指针。

因此,myInt 不是指向整数指针数组的指针吗?

不,因为您将其定义为 int *,一个指向 int 的指针。

当然,指针不知道分配的内存有多大,而只是指向适合那里的第一个int。作为程序员,您需要跟踪大小。

请注意you shouldn't use that explicit typecastmalloc 返回一个 void *,可以静默分配给任何指针,如下所示:

int* myInt = malloc(aDefinedSize * sizeof(int));

指针的算术以指向类型的步幅工作,即int *pp[3]*(p+3)相同,大致意思是"转到p,前进三个次sizeof(int)(以字节为单位),然后访问该位置”int **q 将是一个指向 int 的指针,并且可能指向一个指针数组。

【讨论】:

  • 有一次,我在回答中对指针指向指针进行了扩展讨论,但将其编辑为切线。基本上,指针数组几乎从来都不是你想要的那种“二维数组”。不幸的是,初学者总是先了解argv并尝试模仿它。与数组数组相比,唯一的优势是您可以通过不存储整行来节省内存,但是有更好的数据结构来存储稀疏矩阵,例如压缩的稀疏行。
  • 谢谢。在指针到指针的主题中,如果我要使用int** myArr = malloc(numElements * sizeof(int*)) 初始化一个指针数组,那么我是否正确地指出使用myArr++ 对数组进行增量将按int 指针大小的块逐步遍历内存, 不是整数?
  • 不确定这是针对@ikkachu 还是我,或者谁先得到它,但你是对的。这将为您提供(指向开头的指针)指向int 的指针数组。
【解决方案2】:

malloc 分配一个字节数组并返回指向第一个字节的void*。或者 NULL 如果分配失败。

要将此数组视为不同数据类型的数组,必须将指针强制转换为该数据类型。

在 C 中,void* 隐式转换为任何数据指针类型,因此不需要显式转换:

int* allocateIntArray(unsigned number_of_elements) {
    int* int_array = malloc(number_of_elements * sizeof(int)); // <--- no cast is required here.
    return int_array;
}

【讨论】:

    【解决方案3】:

    C 中的数组

    在 C 中,您要记住数组只是内存中的一个地址,加上一个长度和一个对象类型。当您将其作为参数传递给函数或函数的返回值时,长度会被遗忘,并且会与第一个元素的地址互换处理。这导致程序在读取或写入超出缓冲区末尾时出现许多安全漏洞。

    在大多数情况下,数组的名称会自动转换为其第一个元素的地址,因此您可以将数组或指针传递给memmove(),但也有一些例外情况,即它也有长度很重要。数组上的sizeof() 运算符是数组中的字节数,但sizeof() 指针是指针变量的大小。所以如果我们声明int a[SIZE];sizeof(a)sizeof(int)*(size_t)(SIZE) 相同,而sizeof(&amp;a[0])sizeof(int*) 相同。另一个重要的一点是,编译器通常可以在编译时判断数组访问是否越界,但它不知道对指针的哪些访问是安全的。

    如何返回一个数组

    如果你想返回一个指向同一个静态数组的指针,并且每次调用函数都得到同一个数组,你可以这样做:

    #define ARRAY_SIZE 32U
    
    int* get_static_array(void)
    {
      static int the_array[ARRAY_SIZE];
      return the_array;
    }
    

    您必须在静态数组上调用free()

    如果你想创建一个动态数组,你可以这样做,虽然这是一个人为的例子:

    #include <stdlib.h>
    
    int* make_dynamic_array(size_t n)
    // Returns an array that you must free with free().
    {
      return calloc( n, sizeof(int) );
    }
    

    当您不再需要动态数组时,必须使用free() 释放它,否则程序会泄漏内存。

    实用建议

    对于任何简单的事情,你实际上会写:

    int * const p = calloc( n, sizeof(int) );
    

    除非由于某种原因数组指针会改变,例如:

    int* p = calloc( n, sizeof(int) );
    /* ... */
    p = realloc( p, new_size );
    

    我会推荐calloc() 而不是malloc() 作为一般规则,因为它将内存块初始化为零,而malloc() 未指定内容。这意味着,如果您有读取未初始化内存的错误,使用calloc() 将始终为您提供可预测、可重现的结果,而使用malloc() 每次都可能为您提供不同的未定义行为。特别是,如果您分配一个指针,然后在 0 是指针的陷阱值的实现上取消引用它(如典型的桌面 CPU),则由 calloc() 创建的指针总是会立即给您一个段错误,而垃圾malloc() 创建的指针可能看起来可以工作,但会损坏内存的任何部分。这种错误很难追踪。在调试器中也更容易看出内存是否被清零,而不是任意值是有效还是垃圾。

    进一步讨论

    在 cmets 中,有人反对我使用的一些术语。特别是,C++ 提供了几种不同的方法来返回对数组的引用,这些引用保留了有关其类型的更多信息,例如:

    #include <array>
    #include <cstdlib>
    
    using std::size_t;
    
    constexpr size_t size = 16U;
    using int_array = int[size];
    
    int_array& get_static_array()
    {
      static int the_array[size];
      return the_array;
    }
    
    std::array<int, size>& get_static_std_array()
    {
      static std::array<int, size> the_array;
      return the_array;
    }
    

    因此,一位评论者(如果我理解正确的话)反对“返回数组”这一短语应该指代这种函数。我使用的短语比这更广泛,但我希望能澄清当你在 C 中 return the_array; 时会发生什么。你会得到一个指针。与您相关的是您丢失了有关数组大小的信息,这使得在 C 中编写安全错误非常容易,这些错误会读取或写入超出为数组分配的内存块。

    还有一些反对意见,我不应该告诉你使用calloc() 而不是malloc() 动态分配包含指针的结构和数组,如果你之前取消引用这些指针,几乎所有现代 CPU 都会出现段错误你初始化它们。郑重声明:并非所有 CPU 都如此,因此它不是可移植的行为。有些 CPU 不会陷阱。一些旧的大型机将捕获一个非零的特殊指针值。但是,当我在台式机或工作站上编码时,它会派上用场。即使您在其中一个异常上运行,至少您的指针每次都会具有相同的值,这应该使错误更具重现性,并且当您调试并查看指针时,它会立即明显为零,而指针是垃圾不会立即显而易见。

    【讨论】:

    • 澄清:您不能将“数组”传入或传出函数。通常也不会返回指向数组的指针。而是传递了一个指向第一个元素的指针。
    • 当你给一个函数一个数组的名字作为参数,或者像第一个例子一样从你的函数返回一个数组的名字时,C所做的就是传递一个指向第一个元素的指针数组,是的。在这些情况下,您可以等效地编写 a&amp;a&amp;a[0]
    • 我已经编辑说“通过引用返回数组”,我认为反对 C 中“返回数组”措辞的人会接受。
    • 他不会。 C 不支持引用。指针是一等类型,引用不是。
    • 当你传递或返回一个数组的名字时会发生什么,这就是计算机科学家所说的传递引用。您甚至可以将您的函数声明为frotz_the_array( size_t n, int to_be_frotzed[n] ),该函数会将to_be_frotzed 视为输入输出数组参数。
    猜你喜欢
    • 2018-09-26
    • 1970-01-01
    • 2018-03-05
    • 2020-11-26
    • 1970-01-01
    • 2010-10-02
    • 1970-01-01
    • 2021-08-04
    • 1970-01-01
    相关资源
    最近更新 更多