【问题标题】:C pointer dereference errorC 指针取消引用错误
【发布时间】:2017-08-18 03:12:07
【问题描述】:

假设我有以下代码(示例):

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

void Ex(void *ret, int ret_len, int choice){
    if(choice==1){
        int *ret = ret;              
        for(int i=0; i<ret_len; i++){
            *(ret+i) = i;
        }
     } else {
        int **ret = ret;            
        for(int i=0; i<ret_len; i++){
            for(int j=0; j<ret_len; j++){
                *(*(ret+i)+j) = i*j;
            }
        }
     }
}

int main()
{
    int m[10];
    Ex(m,10,1);
    printf("%i",m[3]);
    return 0;
}

该函数的目标是获取一个指向一维数组的预分配内存的指针。

当编译和执行代码在choice==1时工作,否则,如果我运行

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

void Ex(void *ret, int ret_len, int choice){
    if(choice==1){
        int *ret = ret;              
        for(int i=0; i<ret_len; i++){
            *(ret+i) = i;
        }
     } else {
        int **ret = ret;            
        for(int i=0; i<ret_len; i++){
            for(int j=0; j<ret_len; j++){
                *(*(ret+i)+j) = i*j;
            }
        }
     }
}

int main()
{
    int m[100];
    Ex(m,10,2);
    printf("%i", m[3][5]);
    return 0;
}

没有产生任何输出,代码似乎永远运行(注意选择现在不是 1,m 是正确的大小)

我的印象是 Ex 中的强制转换应该改变数组的形状并允许 main 像 2d 一样索引它,但既不能像 1d 那样索引也不能像 2d 一样工作。

【问题讨论】:

  • *(ret+i) 的类型是int,所以*(int_value+j) 不能取消引用。
  • 是的,这行*(*(ret+i)+j)=i*j; 没有意义。
  • ret[i][j]*(*(ret+i)+j) 之间的唯一区别是一个比另一个更短、更容易阅读、更难出错 - if ret 的参数类型是int **ret(或int *ret[20] 的一些变体)。将ret 定义为int *ret,任何双重取消引用都是错误的。请注意,int **retint *ret[20] 类型非常不同,尽管您可以同时使用 ret[i][j]
  • @BLUEPIXY 说得有道理,但是如何以传统方式完成呢?
  • 根据您使用malloc 保护的方式,我认为有必要将一维数组计算为二维数组,这与您的解释相反。

标签: c arrays pointers compiler-errors


【解决方案1】:

查看了有关问题的 cmets 和一些答案,这是我为您的要求收集的内容。

您需要一个通用函数,该函数接受 choice 维度的数组并使用一些值对其进行初始化。

这可以满足您的目的。

void Ex(void *_ret, int ret_len, int choice){
    if(choice==1){
        int *ret = _ret;              
        int i;
        for(i=0; i<ret_len; i++){
            ret[i] = i;
        }
     } else {
        int (*ret)[ret_len] = _ret;            
        int i, j;
        for(i=0; i<ret_len; i++){
            for(j=0; j<ret_len; j++){
                ret[i][j] = i*j;
            }
        }
     }
}

现在你可以这样称呼它

int main(void) {
    int first[10];
    Ex(first, 10, 1);
    int second[20][20];
    Ex(second, 20, 2);
    printf("first[4] = %d\n", first[4]);
    printf("second[3][4] = %d\n", second[3][4]);
} 

你可以看到Demo here

【讨论】:

  • 您的回答确实有效。只要你不介意,我会接受的。 int *ret = _ret; 有必要吗? int (*ret)[ret_len] = _ret; 发生了什么?感谢您的帮助
  • int *ret = _ret 是必需的,因为_ret 的类型为void*,您不能直接取消引用void*。其他方法是在使用((int*)_ret)[i] 时进行转换,但这很难看,所以我将一次转换为一个单独的变量。
  • 对于int (*ret)[ret_len],它被定义为一个指针,指向一个大小为ret_len的int数组int[ret_len][ret_len]也将衰减到这个数组中。这是取消引用最初声明为 int[20][20] 的变量的正确方法。
  • 您可能已经看到int ret** 也在其他地方使用,但这是一个指向int 类型指针的指针,需要使用malloc(或非标准@ 987654338@) 在一个循环中。
  • 我必须再读几遍才能完全点击。谢谢。我接受了你的回答
【解决方案2】:

因为ret if 属于int * 类型,但 else 部分是:

    for(int i=0; i<ret_len; i++){
        for(int j=0; j<ret_len; j++){
            *(*(ret+i)+j)=i*j;
        }
    }

需要int ** 类型的ret。这会导致错误。


解决问题的方法是使用void *。这是在 C 中使用 泛型 的一种方式。

看看qsort in C.

void Ex(void *_ret, int ret_len, int choice){
    if(choice==1){
        int *ret = (int *)_ret;              // <---- type conversion
        for(int i=0; i<ret_len; i++){
            *(ret+i) = i;
        }
     } else {
        int **ret = (int **)_ret;            // <---- type conversion
        for(int i=0; i<ret_len; i++){
            for(int j=0; j<ret_len; j++){
                *(*(ret+i)+j) = i*j;
            }
        }
     }
}

【讨论】:

  • 我尝试了 void 指针 但我错过了演员表。这个答案肯定是编译的,对我来说很有意义。您是否真的需要声明一个新变量,或者您是否像显式转换一样为了清晰而这样做?如果您不强制转换,为什么编译器会担心?看来,如果我不强制转换并且拥有void * ret,那么它很可能仍然是指向指针的指针
  • @CircArgs 不,新变量声明不是必须的,只是为了清楚起见。
  • 我不必循环 malloc 所以我认为int **ret = (int **)_ret; 是不正确的。
  • @BLUEPIXY 你是说这个循环通过malloc?我没有透露我是如何分配内存的。我只是不想要建议在函数范围内进行动态分配的答案
  • @CircArgs 请告诉我你是如何保护调用方的内存的。
【解决方案3】:

我想出的对现有代码更改最少的最接近的事情是:

#include <stdio.h>
void Ex(int * ret, int ret_len, int choice){
    if(choice==1){
        for(int i=0; i<ret_len; i++){
            *(ret+i)=i;
        }
     }else{
        for(int i=0; i<ret_len; i++){
            for(int j=0; j<ret_len; j++){
                *(ret + i*ret_len + j) = i*j;
            }
        }
     }
}

int main() {
  int anint[10][10];
  Ex(anint, 10, 2);

  for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 10; j++) {printf("%d\n", anint[i][j]);}
  }
}

基本上,我在Ex 函数中手动操作偏移量。我假设两个维度的长度总是相同的。

更“传统的方式”是真正的二维数组,其中每个元素都是独立的 malloc。因此,第一级数组的元素可以强制转换为指针并在第二级取消引用。但是我不愿意调用 malloc 太多次。

我希望这可以解决您的问题。

【讨论】:

  • 你真的可以像在你的 else 语句中那样索引一个多维数组吗?我无法测试它,因为它似乎不起作用
  • 你可以。在 Mac 或 Linux 上,gcc aa.c 然后./a.out
猜你喜欢
  • 2020-03-17
  • 2019-05-20
  • 1970-01-01
  • 1970-01-01
  • 2018-12-22
  • 1970-01-01
  • 1970-01-01
  • 2011-06-17
  • 1970-01-01
相关资源
最近更新 更多