【问题标题】:C: complicated use of, union of structures & unions as arguments in function producing undefined behaviorC:复杂使用,结构和联合的联合作为函数中的参数产生未定义的行为
【发布时间】:2011-12-08 01:47:14
【问题描述】:

我目前正在尝试开发一个小规模的程序来模拟一种使用联合作为参数的方法,然后再使用相同的想法开发一个更大规模的程序。联合,由两个结构组成。

在我的程序中,第一个结构有一个 unsigned int,第二个结构有 81 个元素的 char 数组。在下文中,联合具有两种类型的成员结构。接下来是函数ufuncx(); 的原型,它有两个参数,一个联合指针和一个int。

我正在尝试实现一种方法,我可以将它传递给一个函数,一个指向在联合之外定义的两个结构地址之一的联合指针,这两个结构地址也是联合结构成员的一部分。

源代码清单:

/*
   this is a program test to see if different types of union members can `enter code as
   function arguments of the same union type.
*/

#include <stdio.h>
#include <string.h>

struct id_num
{
   unsigned short int idnumber;
};

struct namedisc
{
   char name[81];
};

union combo_x
{
   struct id_num numtest;
   struct namedisc note;
};

int ufuncx(union combo_x *, int);

int main(void)
{

//step 1:

    union combo_x *a, *b;

    //struct data

    struct namedisc text;
    struct id_num age;

//step 2a:

    //pointer *a - uses the id_num struct

    age.idnumber = 25;
    a->numtest = age;

//step 2b:

    //pointer *b - uses the namedisc struct

    strcpy(text.name,"John Doe\0");
    b->note = text;

//step 3:

      /*=- if you comment this, a pointer error in run time occurs -=*/
      printf(".... %s\n",b->note.name);

//step 4:

    //show output

    ufuncx(a,1);
    ufuncx(b,2);

    return 0;
}

int ufuncx(union combo_x *data,int part)
{

    if(part < 1 || part > 2)
    {
        printf("we dont have that part...\n");
        return -1;
    }

    if(part == 1)
    {
        printf(" age = %d ",data->numtest.idnumber);
    }

    if(part == 2)
    {
        printf("name = %s ",data->note.name);
    }

    printf("(it works!)\n");

    return 0;
}

这是此源代码的运行时输出:

.... John Doe
 age = 25 (it works!) 
name = John Doe (it works!)

这看起来不错,而且似乎可以工作,但是有一个问题。 在我的程序的第 3 步中,如果我注释掉 printf(); 语句,我会得到:

 age = 25 (it works!)
name = (null) (it works!)
!$h *tl J#@^&

name = (null)(它有效!),在此更改中紧随其后的是随机垃圾,表示错误的指针地址。

注释掉的printf(); 语句不应该在运行时产生影响,但在这种情况下它会产生影响。我的第一个想法是,我有一个指针问题,特别是当我尝试使用间接运算符将 struct text 的地址分配给指针 b 时。

我看不出第 2b 步出了什么问题。我尝试以多种方式对其进行修改,但出现编译器错误。这就是我卡住的地方——因为我遇到了一个逻辑错误。

基于这个程序思想的更大规模的模型,将有两个由许多结构组成的联合作为成员,因为每个结构将有许多不同数据类型的成员(即短整数、字符数组等),这些成员将被传递作为函数的参数。相同的目标,只是扩展了。

提前致谢。

【问题讨论】:

  • 容易出错的体现:)

标签: c function pointers struct unions


【解决方案1】:

如果您的编译器没有对您大喊大叫,那么您要么没有打开足够多的警告选项,要么您需要获得更好的编译器。你不能传递(指向)一个工会的成员,就好像它是工会的一部分一样;除非您忽略编译器的警告,否则不允许作弊 - 这几乎总是一个坏主意。

请注意,您的指针 a 和 b 未初始化,因此取消引用它们会立即导致未定义的行为 - 如果您幸运,则会出现核心转储,如果您不幸运,谁知道呢。

union combo_x *a, *b;
struct namedisc text;
struct id_num age;

age.idnumber = 25;
a->numtest = age;   // a is uninitialized - undefined behaviour

strcpy(text.name,"John Doe\0");
b->note = text;     // b is uninitialized - undefined behaviour

你可能在想:

struct namedisc text;
struct id_num age;   
union combo_x *a = (union combo_x *)&text;
union combo_x *b = (union combo_x *)&age;

a->numtest.idnumber = age;
strcpy(b->note.name, "John Doe");

但是强制转换对于关闭编译器的合理抱怨是必要的。

你可以合法地做的是:

union combo_x a;
union combo_x b;

a.numtest.idnumber = age;
strcpy(b.note.name, "John Doe");

【讨论】:

  • 这就是解决方案,演员打字。
  • 我不能推荐类型转换解决方案;你在欺骗你的编译器,当你这样做时,编译器往往会得到自己的回报。我同意我的 cmets 以前没有完全清楚地说明这一点。话虽如此,事实上它可能会很好地工作。对你有利的一个因素是你有一个结构的联合。如果你在联合中有一个普通的char *,理论上你可能会遇到问题。
  • FWIW,我在计算机 (ICL Perq) 上学习了 C,其中内存位置地址的 char * 值与指向同一位置的任何其他指针类型不同。这台机器是字寻址的,奇数字节和偶数字节之间的差异被编码在地址的高位,而不是低位。在这样的环境下你要非常小心!
  • 好吧,我的编译器在抱怨“无法将“int *”转换为“char *”——其他时候我使用了正确的类型但产生了分段错误(空指针分配). 程序成功了。
【解决方案2】:

首先,您通过使用未初始化的a 和b 并且没有将它们指向任何有效的union combo_x(通过malloc 向它们发送或将它们指向现有的地址)导致未定义的行为union combo_x。如果您的编译器没有告诉您,请打开警告。请参阅:

union combo_x *a, *b;

struct namedisc text;
struct id_num age;

age.idnumber = 25;
a->numtest = age; /* dereferencing a...where is a pointing? */

【讨论】:

    【解决方案3】:

    在此代码中,您的变量 a 和 b 永远不会像应有的那样指向内存中的有效区域。在您描述的情况下,它是偶然起作用的。

    您应该将a 设置为您的age 变量的地址,而不是像您那样尝试修改a->numtest。 b->note;

    换句话说,请确保您的代码中包含以下内容:

    a = &age;
    b = &text;
    

    这样,a 和 b 都指向一个有效且已分配的内存区域。

    【讨论】:

    • 顺便说一句,根据您对这里工作原理的理解程度,我不建议您使用纯 C 语言进行“大型项目”来尝试执行这种魔法。检查您的项目需求,也许使用支持动态类型或多态的语言会更可取。 (想想 Python,Javascript Java)。有一些方法可以用高级语言创建大部分程序,并只用 C 编写性能关键部分。
    • 实际上是我的编译器出了问题——我正在安装一个新的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-11
    • 1970-01-01
    • 1970-01-01
    • 2013-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多