【问题标题】:Program prints result I want except in one place I get the memory address instead of the value of the address.程序打印我想要的结果,除了在一个地方我得到内存地址而不是地址的值。
【发布时间】:2012-03-25 10:37:00
【问题描述】:

基本上,练习说“创建一个函数,该函数将数组及其大小作为参数,并返回一个新数组,其中包含作为参数给出的数组的三个最大值。”所以我想我会拿数组并在函数中对其进行排序,然后将 3 个最高值赋给一个新数组并返回它。这是我的代码。

int *function(int *A, int k){
    int B[3],i,j,temp;

    //Sorting
    for(i=k-1;i>0;i--){
        for(j=1;j<=i;j++){
            if(A[j-1] > A[j])
            {
                temp = A[j-1];
                A[j-1] = A[j];
                A[j] = temp;
            }
        }
    }
    i = 0;
    while(i < 3){
        B[i]= A[k-1];
        i++;
        k--;
    }
    return B;   
} 

int main (int argc, const char * argv[]) {
   int A[5] = {1,8,7,4,6};
    int size = 5;
    int *func,i;

    func = function(A,size);
    for(i=0;i<3;i++)
        printf("%d ",func[i]); 
    return 0;
}

结果我应该得到 8 7 6 但我得到 8 -1073743756 6。我找不到错误。当我想返回 B 时,我也会收到警告。它显示“函数返回局部变量的地址”。也许这与这个问题有关。有什么想法吗?

【问题讨论】:

    标签: c arrays sorting pointers


    【解决方案1】:

    函数返回后,自动存储对象(如int B[3])被销毁,引用它们是非法的。实际上,由于向堆栈写入了各种内容,您会得到意想不到的值:

    你可以:

    • 给 B 静态存储
    • 使用malloc(以及后来的free
    • 完全使用不同的方法,例如让调用者负责传递B

    【讨论】:

    • 所以我做了一些改变。我做了 int *B 然后 B = (int *)malloc(3*sizeof(int)) 并且程序工作并且警告也不再存在了。但是写 B[3] 和使用 malloc 写数组 B 有什么区别呢?当我们从一开始就不知道数组的大小时,我们不使用 malloc 吗?在这个例子中,我从一开始就知道数组 B 的大小为 3。
    • 这些建议不按可取性排序;我会把清单倒过来。
    【解决方案2】:

    变量 B 的“生命周期”是在声明它的函数的持续时间内。当它超出范围时,它不再存在,并且它的内存可能会被重用于其他目的。您应该将所有编译器警告视为错误——它们通常是语义错误(与语法错误相反);也就是说,编译器理解代码,但它不太可能是你想要的,或者可能有意外或未定义的行为。

    在这种情况下,编译器会准确地告诉您问题所在。您可能需要问的是如何最好地解决错误。

    当调用者需要由函数填充数组时,通常使用的模式是调用者将数组提供给函数。因为数组在传递给函数时会“衰减”到指针,所以也传递长度是正常的,这对于避免缓冲区溢出很有用。您可以让函数“知道”在您的情况下缓冲区长度始终为 3,但它的可维护性和安全性较差。

    通常这样的函数会返回一个指向调用者缓冲区的指针,或者在失败时返回 NULL,因此它可以用于错误检查或用作另一个函数的参数。

    例子:

    int* fn( int* caller_buffer, int caller_buffer_length )
    {
        int i ;
        for( i = 0; i < caller_buffer_length; i++ )
        {
            caller_buffer[i] = ... ;
        }
    }
    

    然后

    int my_buffer[3] = {0} ;
    int my_buffer_length = sizeof(my_buffer) / sizeof(*my_buffer) ;
    int* error_check = 0 ;
    
    ...
    
    error_check = fn( my_buffer, my_buffer_length ) ;
    if( error_check != 0 )
    {
        ...
    }
    

    可以通过静态、全局或动态分配 B 来解决问题。所有这些解决方案我都会归类为“快速而肮脏的”。它们可能会解决这个特定问题,但不是一种能够很好地扩展到更复杂和更大的应用程序的通用模式。

    function() 内的本地动态内存分配引发了一个问题,即谁负责释放分配的内存?

    本地静态分配有效,但如果您再次调用该函数,之前的数据将会丢失,这可能并不总是可以接受的。它也不能重入,导致多线程应用程序或递归算法中的潜在问题。

    全局数据是静态分配的,但作为全局可见的附加问题,不再由一个函数单独控制。

    【讨论】:

      【解决方案3】:

      B 是一个局部变量,在函数退出时消失。

      在函数外声明B,并通过函数指针传递。

      【讨论】:

        【解决方案4】:

        B[] 数组仅在function() 运行时存在(它是本地)。返回后它是未引用的。

        如果您的任务文本被精确引用,那么您似乎必须深入研究 malloc。然后我现在说:

        • 不要强制转换 malloc
        • 完成后释放()

        使用malloc的原因,如下正文

        1. “返回一个数组”

        2. “只取一个数组和计数器”,而不是第三个参数或重用A

        不要被使用全局数组的诱惑;),你可能被允许使用静态? -(编辑:@Clifford 在为什么不这样做上有很好的观点)

        祝你好运!


        编辑:
        另外:您没有“获取内存地址而不是值”

        function() 被调用时,它会给出一个返回地址。其次,该函数中 local 变量的内存是保留的,并且它们的名称已给出地址。 IE。类似:

        /* assuming 4 byte int */
        [ 0xA4 ... 0xAF ] <-- int B[3]  -- 3 * 4 = 12 bytes
        [ 0xB0 ... 0xB4 ] <-- int i     -- 4 bytes
        [ 0xB5 ... 0xB9 ] <-- int j     -- 4 bytes
        [ 0xBA ... 0xBE ] <-- int temp  -- 4 bytes
        

        当函数返回时,这些区域被释放。由于没有使用电源来擦除数据,因此它保留在那里,但可以随时在另一个操作使用该区域时被覆盖。

        即从您的代码可能是printf() 或者它可能是完全不同的东西。重点是它不是保留的,可以用于“whatever”。 (不做煎饼,但我想你明白了)。

        因此,当您打印 func 的值并得到 2 个正确和一个错误时,这意味着前两个尚未被覆盖,而最后一个已被另一个调用覆盖。它甚至可能正在使用中。


        最后一个值不是地址,而是另一个解释为整数的值。它也不必是 int。如果另一个函数向该区域写入了一个短整数或一个字符(很可能是 4 个字节),那么您所做的就是读取 4 个字节并将这些位解释为一个整数。

        说:

        B[2] == 6 (on a 4 bytes, int 32, system)
        
        Address: 0xAC 0xAD 0xAE 0xAF       As bit sequence
        Value  :    6    0    0    0  ==> [0000 0110 0000 0000 0000 0000]
        

        返回mainfun[2] -&gt; [0xC 0xD 0xE 0xF]

        函数在其他地方定义了char(1 字节)并获得现在空闲的地址0xD
        它将值 123 写入该地址。

        现在我们有了:

        fun[2]:
        
        Address: 0xAC 0xAD 0xAE 0xAF       As bit sequence
        Value  :    6  123    0    0  ==> [0000 0110 0111 1011 0000 0000]
        

        或者,如果我们在 little-endian 系统(如我的系统)上将其转换为整数,我们会得到

                  +--------- Least significant byte
                  |
        Address: 0xAC 0xAD 0xAE 0xAF
        Value  :    6  123    0    0  ( == 0x06 0x7B 0x00 0x00 )
        
        Sort to "human Anglo left right order"
        0 0 123 6 => 0x00007B06 == 31494 (int, "normal human number"), and the number
                                               you print when doing the for() loop in 
                                               main.
        

        也可以像这样使用相关字节

                           +----- bytes of fun[2]
                           |
                  _________+_________
                 [                   ]
        char foo 0xAC                |
        int  bar       0xAD 0xAE 0xAF  0xB0
                       [____+______________]
                            |
                            +---- bytes of bar
        

        数字 1 和 2 是正确的事实并不能保证字节没有改变。可以将相同的值写入该位置。 0 可能已写入 int 等的字节 2。

        胡佛展示:

        *作为一个半注释,只是为了进一步混淆,它可能是一个地址,但然后是该地址的整数表示。事实上,这不太可能......它可能是某些操作的返回地址,您将其解释为 int。*

        希望您能从中获得至少 1% 的收益。我正在积极学习自己,解释往往是在学习:)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-20
          • 1970-01-01
          • 2011-04-10
          • 1970-01-01
          • 1970-01-01
          • 2014-05-17
          相关资源
          最近更新 更多