【问题标题】:Assign an array to a pointer with complex cases将数组分配给具有复杂情况的指针
【发布时间】:2018-07-12 01:32:26
【问题描述】:

如果我们将一个指针分配给另一个指针,则称为“交换指针”。例如,

float *temp, *ptr1, *ptr2;
temp = ptr1;
ptr1 = ptr2;
ptr2 = temp;

但是,如果我们将数组分配给指针,这是非法的,因为数组并不是真正的指针类型。但是看看接下来的 3 个例子:

float arr[]={1.2, 1.9, 3.1};
float *ptr;
int i;
ptr = (float *)calloc(3,sizeof(float));
ptr = arr;
for (i=0;i<3;i++){
    printf("ptr[%d]=%f\n",i,ptr[i]);
}

这段代码sn-p通过编译并正确运行(非法代码得到正确答案?):

ptr[0]=1.200000
ptr[1]=1.900000
ptr[2]=3.100000

如果我在最后一行之后添加free(ptr),即

float arr[]={1.2, 1.9, 3.1};
float *ptr;
int i;
ptr = (float *)calloc(3,sizeof(float));
ptr = arr;
for (i=0;i<3;i++){
    printf("ptr[%d]=%f\n",i,ptr[i]);
}
free(ptr);  /*add free here*/

这次出现了警告:

warning: attempt to free a non-heap object ‘arr’ [-Wfree-nonheap-object]

int i;
int flag=0;
float arr1[3]={1.07,3.01,5.02};
float arr2[3]={2.07,6.01,9.02};
float arr3[3]={3.07,8.01,0.02};
float *ptr;
ptr = (float *)calloc(3,sizeof(float));
if(flag==0){
    ptr = arr1;
}
else if(flag==1){ 
    ptr = arr2;
}
else{ 
    ptr = arr3;
}
for (i=0;i<3;i++){
    printf("ptr[%d]=%f\n",i,ptr[i]);
}

使用不同的flag 可以正常运行,但是如果我在最后一行添加free(ptr),它仍然会像之前一样出现警告。

有人帮忙分析一下原因吗?

【问题讨论】:

  • 在一行上分配足够的字节来保存三个浮点数,并设置ptr 的值来保存该内存位置。在下一行更改ptr 的值以保存arr 数组的地址,从而孤立您之前分配的内存。由于ptr 指向的内存实际上是三个浮点数的文字数组,因此您无法释放它。你应该释放的内存已经泄露。
  • OT:当调用任何堆分配函数时:malloc calloc realloc 1) 返回的类型是void*,可以分配给任何指针。强制转换只会使代码混乱,使其更难以理解、调试等。2) 始终检查 (!=NULL) 返回值以确保操作成功
  • 关于:ptr = arr1; 和类似声明。这会通过调用calloc() 覆盖在指针中设置的值,从而导致内存泄漏。也许您的意思是:memcpy( ptr, arr1, sizeof( arr1) ); 注意:在 C 中,引用数组的名称会降级为数组第一个字节的地址。
  • @user3629249 - 我知道你的意思,但从技术上讲,数组是“转换为类型为''pointer to type''的表达式,它指向数组对象的初始元素” degrades 有点奇怪,但你会看到 decays ——它本身不在标准中,但通常理解)见:@ 987654321@.

标签: c arrays pointers


【解决方案1】:
ptr = (float *)calloc(3,sizeof(float));

在这里,您将 ptr 设置为一个足够大的 malloc 内存块,可容纳 3 个浮点数。然后在下一行:

ptr = arr;

您用arr 覆盖ptr 的值,导致内存泄漏。这是合法的,因为在这样的赋值中,数组衰减 变成指向其第一个元素的指针。

当您打印 ptr 指向的值时,您会得到 arr 中的值,因为这是 ptr 指向的位置。

这也是为什么在 ptr 上调用 free 是无效的,因为它不再指向分配的内存。

至于最后一段代码,这是一个无效的初始化器:

float arr1[3]={{1.07,3.01,5.02}};

当我运行此代码时,flag=0 显示第二个和第三个值的 0,而 flag=1flag=2 显示所有预期值。

你只需要一套大括号:

float arr1[3]={1.07,3.01,5.02};

【讨论】:

  • 对于最后一个代码,如果我在最后一行添加free(ptr);,它仍然像之前一样有警告。
  • @coco 是的,出于同样的原因:你用本地数组的地址覆盖了 malloc 的内存。
  • 所以这段代码 sn-p 仍然没有用,因为我们必须在使用它们后取消分配内存。这是否意味着我必须找到另一种将数组分配给指针的方法,例如“for”循环?
  • @coco 您要么必须将本地数组复制到分配的内存(使用memcpy 或循环),要么完全摆脱分配。
【解决方案2】:

没有错误的解决方案

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

int main()
{
    float arr[]={1.2, 1.9, 3.1};
    float *ptr;
    int i;
    ptr = (float *)calloc(3,sizeof(float*));

    if(ptr == NULL){
     perror("calloc");
     return -ENOMEM;
    }
    //ptr = arr;

    // here you need to do something like this
    ptr[0] = arr[0];
    ptr[1] = arr[1];
    ptr[2] = arr[2];

    for (i=0;i<3;i++){
       printf("ptr[%d]=%f\n",i,ptr[i]);

           }
        free(ptr);
}

【讨论】:

  • 不是报错,但是malloc(或callocrealloc)的返回不需要强制转换,没必要。见:Do I cast the result of malloc?——你肯定有一种有趣的大括号格式...
  • 是的,你是对的,但是为了其他语言之间的可移植性,比如 c++,我喜欢这样做。
  • 嗯,这就是为什么有一个叫做 C 标准的东西和一个叫做 C++ 标准的单独文档。此外,您应该验证calloc 的返回,例如(if (ptr != NULL)) 在使用内存之前。分配函数在失败时返回NULL(它们确实失败了)。回想一下,在回答问题时,您会进入老师的名单。您正在帮助一名新学生学习编程。确保他记得你是一位好老师:)
  • 完成了!!谢谢,从下次开始,我会确保一切都清楚.. :)
【解决方案3】:

当您将指针指向数组时,您的代码中绝对没有任何违法行为。它甚至不会给你一个警告。

指针是一个指针,它可以指向任何东西。有时你可能需要强制转换它,但指向相同基类型的数组不仅完全有效,甚至很常见。

但是,您只能在堆上分配的内存(malloc、calloc 等)上使用 free。一个数组是在栈上分配的,不需要释放,正如你已经看到的,如果你这样做,你会得到一个运行时错误。

【讨论】:

  • 但是对于“交换指针”(就像第一个示例一样),我们可以“释放”指针。是不是因为动态分配的内存仍然存在,而数组可以用常量指针覆盖它,使指针不再动态分配?
  • @coco 在第一个示例中,您根本没有分配任何内存,所以不,您不能释放它们。
【解决方案4】:

分配一个数组

C 中的数组不能赋值。

您需要逐个元素分配。

这里发生了什么

float arr[]={1.2, 1.9, 3.1};
float *ptr;

ptr = arr;

是你给ptr分配了arr[0]的地址。这称为“The array is decayed to a pointer(地址)它的第一个元素

没有数组元素被复制!

为了证明这一点,就这样做

arr[0] = 42.;

下一步然后运行打印循环

for (int i = 0; i < 3; ++i) 
{
  printf("ptr[%d] = %f\n", i, ptr[i]);
}

你会得到(类似的东西):

ptr[0] = 42.000
ptr[1] = 1.9000
...

【讨论】:

  • 其实可以复制。看第二个代码sn -p,它可以返回正确的值。
  • @coco:您是否阅读并尝试了我的提议,以证明在您展示的三种情况下都没有“复制”任何元素?
猜你喜欢
  • 1970-01-01
  • 2018-08-24
  • 1970-01-01
  • 2011-04-02
  • 1970-01-01
  • 2018-10-06
  • 1970-01-01
  • 1970-01-01
  • 2015-07-05
相关资源
最近更新 更多