0

我有一些代码,我想要一个通用函数,该函数从主代码中获取指针并在指针地址处操作变量。问题是指针的类型是 char、int 和 short。我希望能够在没有标志或跟踪传递的指针类型等的情况下做到这一点。我的猜测是可以使用指针的 typedef 联合,然后该函数将采用 int 指针(最大数据大小三者中的)

除了 char 指针之外,以下类型的作品。有一个更好的方法吗?

       #include <stdio.h>

    void pointerfunction(int *p);
    int a=10;
    short b=20;
    char f=4;
    typedef union 
    {
    int *ptr1;
    short *ptr2;
    char *ptr3;
    }pointers;

    int main()
    {
    pointers mypointers;

    mypointers.ptr1=&a;
    pointerfunction(mypointers.ptr1);
    printf("%d\n", *(mypointers.ptr1));
    mypointers.ptr2=&b;
    pointerfunction(mypointers.ptr1);
    printf("%d\n", *(mypointers.ptr2));
    mypointers.ptr3=&f;
    pointerfunction(mypointers.ptr1);
    printf("%d\n", *(mypointers.ptr3));
    }


    void pointerfunction(int *p)
    {
    *p=*p*10;  
    }
4

6 回答 6

3

您使用联合的想法是一个很好的想法,但是您将不得不在联合中添加一个额外的成员来指示它实际上是哪种指针。

同一台机器上的所有指针大小相同,无论它们是 int *、char * 还是 void *。

int 和 short 工作的原因是因为编译器将 int 和 short 转换为 int,所以printf()函数基本上看到两者相同。

首先,我将描述一个可能的实现。然而,这个特定的实现是丑陋的,并不是真正的方法,因为它有许多问题,尤其是您使用命令开关并真正降低内聚和增加耦合

第一次尝试将类似于以下内容。

#define  POINTER_UNION_TYPE_CHAR   1
#define  POINTER_UNION_TYPE_INT    2
#define  POINTER_UNION_TYPE_SHORT  3

typedef struct {
  int   iType;
  union {
    char *pChar;
    int  *pInt;
    short *pShort;
  } u;
} Pointers;

当您使用此结构时,您将执行以下操作:

int iValue = 1;
Pointers  PointerThing;

PointerThing.u.pInt = &iValue;  PointerThing.iType = POINTER_UNION_TYPE_INT;

然后在你的函数中使用这个你会做类似的事情:

void pointer_funct (Pointers *pPointers)
{
   switch (pPointers->iType) {
      case  POINTER_UNION_TYPE_CHAR:
           // do things with char pointer pPointers->u.pChar
           break;
      case  POINTER_UNION_TYPE_INT:
           // do things with char pointer pPointers->u.pInt
           break;
      case  POINTER_UNION_TYPE_SHORT:
           // do things with char pointer pPointers->u.pShort
           break;
      default:
           break;
    }
}

一个更好的方法是让单独的函数完成所有组合成一个函数的工作。因此,换句话说,您将拥有三个不同的函数,每个函数都将处理特定的指针类型。这样,知道类型是什么的功能就可以继续调用适当的函数。

另一种方法是使用一些面向对象的技术。看到这个帖子到另一个虽然类似的问题

于 2012-08-15T21:46:10.783 回答
2

正如其他人所说,这在 C 中是不可能的。你是正确的,它int是三种类型中最大的,但你似乎忽略了这个事实的含义。

为什么这在 C 中是不可能的?

在 C 中,数据直接存储在内存中,没有元数据开销。变量直接映射到内存中的数据。除非您创建它(违反您的要求,即没有标志或跟踪正在传递的指针类型),否则没有与变量一起存储的信息,例如:

  • 它是什么类型
  • 变量是否已初始化
  • 变量是否在范围内
  • 或(对于数组/字符串)使用的长度或可用大小

就像其他语言一样。相反,这个信息应该由程序员维护,要么通过创建一个struct来存储这些信息,要么让程序员记住发生了什么。

C 是一种系统编程语言,它适用于系统编程的部分原因是它不像 Java 或 C# 那样具有这种开销。

好的,但为什么它在工会中不起作用?

所指向的类型的各种大小的含义是什么?考虑以下内存图,其中每个字符为 4 位,int 为 32 位,short 为 16 位,char 为 8 位:

半字节:89ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
[other ][data ][int ][int ][int ][mo][re][ ][da][ta] // 整数
[other ][data ][sh][or][t ][sh][or][t ][mo][re][ ][da][ta] // 短裤
[其他][数据][][][][][][][][][][][][][mo][re][][da][ta] // 字符

请注意,这完全忽略了对齐和字节顺序问题;有一些平台(包括 ARM,我在您的其他一些问题中看到)对对齐做出了某些保证,可以帮助您。(†)

但是,对于静态内存或堆上的内存,问题仍然存在。考虑如果将字符串存储ABCDEFGHIJKL在字符数组中会发生什么。记住 ASCIIA是 0x41,这将在内存中变为以下内容:

[其他][数据]4142434445464748494A4B4C[mo][re][][da][ta]

现在假设您传递了一个指向C函数的指针,该函数将 this 取消引用为整数:

                    [int] // 指向 C 的 Int 指针
[其他][数据][][][][][][][][][][][][][mo][re][][da][ta] // 字符
[其他][数据]4142434445464748494A4B4C[mo][re][][da][ta]
                    ^-- C 在这里;0x43

在此处使用 int 指针将违反 C 规范。

如果这还不够,并且我们假设您的编译器在逻辑上运行,它将尝试跨字边界取消引用内存,这可能会引发总线错误或使用错误(我忘记了它在 ARMv7 上的实际作用,但其中任何一个错误都会终止您的程序)。

如果这仍然不够,并且它以某种方式完成了所要求的操作,则该操作将产生错误的答案,因为您正在使用值 0x43444546 而不是 0x43。


关于 ARM 处理器上的内存对齐的一些脚注

(†) 例如,在 ARM 上,ABI 指定堆栈在正常使用中必须是字对齐的 ( sp % 4 == 0),在这种情况下,您的代码可能会工作,如下图所示:

0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
[其他][数据][int][int][int][mo][re][][da][ta]
[其他][数据][sh] [or] [t] [sh] [or] [t] [mo] [re] ...
[其他][数据][][][][][][][][]...

堆栈还保证对公共接口是双字对齐的,并且在内部不必维护它,有关详细信息,请参见 AAPCS 中的 5.2.1。尽管如此,这不是您想要依赖的东西(在大多数情况下可移植代码更可取),或者甚至需要知道,除非您正在编写编译器或原始汇编代码

于 2012-08-15T22:31:05.653 回答
1

这是一个坏主意。看这里:

void pointerfunction(int *p);
short b=20;
short c=20;
typedef union
{
  int *ptr1;
  short *ptr2;
  char *ptr3;
} pointers;

int main()
{
  pointers mypointers;

  mypointers.ptr2=&b;
  pointerfunction(mypointers.ptr2);
  printf("b=%d,c=%d\n", b,c);

  mypointers.ptr2=&c;
  pointerfunction(mypointers.ptr2);
  printf("b=%d,c=%d\n", b,c);

}


void pointerfunction(int *p)
{
  *p=*p*10;
}

在我的系统上打印:

b=200,c=200
b=200,c=2000

那是你想要发生的吗?

于 2012-08-15T21:30:58.063 回答
1

看来您想要模板函数之类的东西。但是,C 不支持模板函数或函数重载。

由于您的三种类型具有不同的大小,您可以从指针推断类型。因此,您可以使用宏来创建重载函数的感觉。

#define pointerfunction(x) do { \
    switch (sizeof(*x)) { \
    case sizeof(int):   pointerfunction_int((void *)x);   break; \
    case sizeof(short): pointerfunction_short((void *)x); break; \
    case sizeof(char):  pointerfunction_char((void *)x);  break; \
    default:            fprintf(stderr, "unknown pointer type for %p\n", x); \
                        break; \
    } \
} while (0)

#define pointerfunction_template(T) \
    void pointerfunction_ ## T (T *x) { *x = *x * 10; }

pointerfunction_template(int);
pointerfunction_template(short);
pointerfunction_template(char);

然后,您可以像这样使用宏:

int a=10;
short b=20;
char f=4;

int main () {
    pointerfunction(&a);
    pointerfunction(&b);
    pointerfunction(&f);
    return 0;
}

但是,这种技术一般不会起作用。特别是,如果两种类型的大小相同,它就会失败。然后,您将被迫将类型本身嵌入到您的宏调用中。

#define pointerfunction_call(T, x) pointerfunction_ ## T(x)

pointerfunction_template(float);
pointerfunction_template(double);

float g = 2.2;
double h = 3.1;

pointerfunction_call(float, &g);
pointerfunction_call(double, &h);
于 2012-08-16T06:29:02.353 回答
0

这是行不通的。您无法知道您在联合中存储的指针的类型。

您可以定义一个结构,该结构将联合作为成员,另一个字段说明您正在使用哪种值。请记住,无论指针指向什么,指针的大小都是相同的。我没有测试过这个,但你明白了。

#include <stdio.h>

int a=10;
short b=20;
theChar f=4;

#define INT 1
#define SHORT 2
#define CHAR 4

typedef union 
{
int *ptr1;
short *ptr2;
char *ptr3;
}pointers;

typedef struct {
    pointers ps;
    type int;
} myStruct;


void pointerfunction(myStruct theStruct)
{
    switch (theStruct.type) {
        case INT:
            *(theStruct.ptr1) += 10;
            break;
        case SHORT:
            *(theStruct.ptr2) += 20;
            break;
        case CHAR:
            *(theStruct.ptr3) += 30;
            break;
    }
}


int main()
{
    // Example with a char
    myStruct theStruct;
    theStruct.type = CHAR;

    theStruct.ptr3=&theChar;

    pointerfunction(theStruct);
    printf("%d\n", *(theStruct.pointers.ptr3));
}
于 2012-08-15T21:30:30.960 回答
0

C不能做“通用”函数,至少不能像 C++ 模板和函数重载那样“做我的意思”:

template <typename T>
void pointerfunction(T *p)
{
  *p = *p * 10;
}
...
pointerfunction(&a);
pointerfunction(&b);
pointerfunction(&c);

在某些时候,您的函数必须知道指针的正确类型才能正常运行;将指针类型转换为int *将导致非整数类型出现问题。

可以做的一件事是编写一个不同的函数来处理每种类型的指针,但通过一个通用的“通用”接口调用这些函数,如下所示:

void intfunc(void *p)   { int   *lp = *p; *lp *= 10; }
void charfunc(void *p)  { char  *lp = *p; *lp *= 10; }
void shortfunc(void *p) { short *lp = *p; *lp *= 10; }

void callfunc(void *data, void (*ptrfunc)(void *))
{
  ptrfunc(data);
}
...
callfunc(&a, intfunc);
callfunc(&b, shortfunc);
callfunc(&c, charfunc);

它并不漂亮,而且与 C++ 模板不同,它会抛出任何类型安全的伪装(一旦你开始使用void *,你就失去了编译器的任何支持)。

但...

这种方法非常灵活,您不必做任何自然的事情就可以让它发挥作用。

于 2012-08-15T22:18:27.917 回答