关于struct:在C语言中实现COBOL REDEFINES

Implement COBOL REDEFINES in C

我正在尝试用C语言实现COBOL中使用的REDEFINES逻辑。

下面是COBOL程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
   IDENTIFICATION DIVISION.
   ENVIRONMENT DIVISION.
   DATA DIVISION.
   WORKING-STORAGE SECTION.

   01  DATE-MMDDYY.
       10  DATE-MM               PIC 9(02).
       10  DATE-DD               PIC 9(02).
       10  DATE-YY               PIC 9(02).
   01  SYSTEM-DATE-MMDDYY REDEFINES DATE-MMDDYY PIC X(6).

   PROCEDURE DIVISION.

       MOVE '011817' TO SYSTEM-DATE-MMDDYY.
       DISPLAY 'SYSTEM-DATE-MMDDYY: ' SYSTEM-DATE-MMDDYY.
       DISPLAY 'DATE-MM: ' DATE-MM.
       DISPLAY 'DATE-DD: ' DATE-DD.
       DISPLAY 'DATE-YY: ' DATE-YY.

       DISPLAY 'CHANGING DATE-YY = 18'
       MOVE '18' TO DATE-YY.
       DISPLAY 'New SYSTEM-DATE-MMDDYY: ' SYSTEM-DATE-MMDDYY.

       STOP RUN.

下面是上面程序的执行:

1
2
3
4
5
6
SYSTEM-DATE-MMDDYY: 011817
DATE-MM: 01
DATE-DD: 18
DATE-YY: 17
CHANGING DATE-YY = 18
New SYSTEM-DATE-MMDDYY: 011818

我知道C语言中的UNION可以用来实现类似的目的。但这对我不起作用。

下面是我编写的C程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

union redef
{
        struct date_mmddyy{
                char date_mm[2];
                char date_dd[2];
                char date_yy[2];
        }date_mmddyy;
        char system_date_mmddyy[6];
};
typedef union redef redef;
int main(){
        redef redef;
        strcpy(redef.date_mmddyy.date_mm,"01");
        strcpy(redef.date_mmddyy.date_dd,"18");
        strcpy(redef.date_mmddyy.date_yy,"17");
        printf("%s\
"
,redef.date_mmddyy.date_mm);
        printf("%s\
"
,redef.date_mmddyy.date_dd);
        printf("%s\
"
,redef.date_mmddyy.date_yy);
        printf("%s\
"
,redef.system_date_mmddyy);

        strcpy(redef.system_date_mmddyy,"021918");
        printf("%s\
"
,redef.date_mmddyy.date_mm);
        printf("%s\
"
,redef.date_mmddyy.date_dd);
        printf("%s\
"
,redef.date_mmddyy.date_yy);
        printf("%s\
"
,redef.system_date_mmddyy);

        return 0;
}

运行如下:

1
2
3
4
5
6
7
8
011817
1817
17
011817
021918
1918
18
021918

可以请您分享一些想法吗?

更新1:

我现在已经用\\0终止了所有char数组。下面是更改后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

union redef
{
        struct date_mmddyy{
                char date_mm[3];
                char date_dd[3];
                char date_yy[3];
        }date_mmddyy;
        char system_date_mmddyy[7];
};
typedef union redef redef;
int main(){
        redef redef;
        redef.date_mmddyy.date_mm[2] = '\\0';
        redef.date_mmddyy.date_dd[2] = '\\0';
        redef.date_mmddyy.date_yy[2] = '\\0';
        redef.system_date_mmddyy[6] = '\\0';

        strcpy(redef.date_mmddyy.date_mm,"01");
        strcpy(redef.date_mmddyy.date_dd,"18");
        strcpy(redef.date_mmddyy.date_yy,"17");
        printf("%s\
"
,redef.date_mmddyy.date_mm);
        printf("%s\
"
,redef.date_mmddyy.date_dd);
        printf("%s\
"
,redef.date_mmddyy.date_yy);
        printf("%s\
"
,redef.system_date_mmddyy);

        strcpy(redef.system_date_mmddyy,"021918");
        printf("%s\
"
,redef.date_mmddyy.date_mm);
        printf("%s\
"
,redef.date_mmddyy.date_dd);
        printf("%s\
"
,redef.date_mmddyy.date_yy);
        printf("%s\
"
,redef.system_date_mmddyy);

        return 0;
}

下面是执行:

1
2
3
4
5
6
7
8
01
18
17
01
021918
918

021918

输出与COBOL中的输出相去甚远。


如果要在C中使用COBOL中的REDEFINE逻辑,则只有一个选择:不要将C字符串用于COBOL存储,因为COBOL数据仅使用字符数组作为数据结构。
和:
注意结构填充(对于未声明为PIC X的任何项目)!

这将导致类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>

union redef
{
        struct date_mmddyy{
               char date_mm[2];
               char date_dd[2];
               char date_yy[2];
        }date_mmddyy;
        char system_date_mmddyy[6];
};
typedef union redef redef;
int main(){
        redef redef;
        redef.date_mmddyy.date_mm[0] = '0';
        redef.date_mmddyy.date_mm[1] = '1';
        redef.date_mmddyy.date_dd[0] = '1';
        redef.date_mmddyy.date_dd[1] = '8';
        redef.date_mmddyy.date_yy[0] = '1';
        redef.date_mmddyy.date_yy[1] = '7';
        // or:
        memcpy((void *)redef.date_mmddyy, (void *)"011817", 6);

        printf("%c%c\
"
,redef.date_mmddyy.date_mm[0],
                        redef.date_mmddyy.date_mm[1]);
        [...]

要使其实际可用,您可能需要使用两件事:

  • 除了包含数据的结构外,还添加一个字段结构,该字段结构至少包含一个指向存储,大小和类型的指针(在示例中," x"和" numeric-display"就足够了)
  • 添加辅助函数以设置/获取字段值

类似

的东西

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>

enum cob_type {
    T_DISPLAY = 0,
    T_NUMERIC_DISPLAY
};

struct cob_field{
       char *data;
       int size;
       enum cob_type;    
};

/* one for the actual storage - could be a simple unnamed char array */
struct date_mmddyy{
       char date_mm[2];
       char date_dd[2];
       char date_yy[2];
} date_mmddyy;

/* fields with pointers to the storage and the size */
cob_field date_mmddyy = {&date_mmddyy, 6, T_DISPLAY};
cob_field date_mm     = {&date_mmddyy.date_mm, 2, T_NUMERIC_DISPLAY};
cob_field date_dd     = {&date_mmddyy.date_dd, 2, T_NUMERIC_DISPLAY};
cob_field date_yy     = {&date_mmddyy.date_yy, 2, T_NUMERIC_DISPLAY};
cob_field system_date_mmddyy = {&date_mmddyy, 6, T_DISPLAY};

int main(){
        set_field_data(system_date_mmddyy,"011817");
        printf("SYSTEM-DATE-MMDDYY: %s\
', get_field_data(system_date_mmddyy));
        printf("
DATE-MM: %s\
', get_field_data(date_mm));
        printf("DATE-DD: %s\
'
, get_field_data(date_dd));
        printf("DATE-YY: %s\
', get_field_data(date_yy));

        puts("
CHANGING DATE-YY = 18");
        set_field_data(date_yy,"
18");
        printf("
SYSTEM-DATE-MMDDYY: %s\
', get_field_data(system_date_mmddyy));

        return 0;
}

鉴于COBOL具有许多不同的类型,您需要在辅助函数中编写很多逻辑-这里void set_field (cob_field *f, char *data)char *get_field (cob_field *f)所需的逻辑非常简单,当使用所有COBOL具有的类型时,这种变化会变得很多在文字中使用多个MOVE(添加隐式类型转换)和DISPLAY时,会更加复杂。

顺便说一句:您可能要检查GnuCOBOL-它会将COBOL转换为C ...


您的工会正在工作。问题来自显示结构字段时字符串中缺少NULL终止符。

如果您不想修改结构,也许可以修改格式限定符。

1
2
3
4
5
6
7
8
printf("%2s\
"
,redef.date_mmddyy.date_mm);
printf("%2s\
"
,redef.date_mmddyy.date_dd);
printf("%2s\
"
,redef.date_mmddyy.date_yy);
printf("%6s\
"
,redef.system_date_mmddyy);


问题在于输出本身。在C语言中,所有字符串(至少您要使用printf("%s",...)打印的字符串)最后都需要一个0字节。由于您的"字符串"应具有2或6个字节的固定长度,因此这将不起作用,因为2个字节的"字符串"中的每一个都需要第三个字节来完成字符串的

因此,实际上它是"可行的",但是结果是一个字符数组,这与C语言中的字符串有所不同,因此您不能使用常规的字符串操作。

这不是您想听到的,但我建议您不要使用字符串,而是使用整数并使用转换函数。例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct Date {
    int day, month, year;
};

void dateFromString(char* input, struct Date* output) {
    char tmp[3];
    tmp[2] = '\\0';
    memcpy(tmp, &input[0], 2);
    output->day = atoi(tmp);
    memcpy(tmp, &input[2], 2);
    output->month = atoi(tmp);
    memcpy(tmp, &input[4], 2);
    output->year = atoi(tmp);
}

void stringFromDate(struct Date* input, char* output) {
    sprintf(output,"%02i%02i%02i",input->day,input->month,input->year);
}

int main() {
    char text[] ="120456";
    struct Date d;
    dateFromString(text, &d);

    printf("%i\
"
,d.day);
    printf("%i\
"
,d.month);
    printf("%i\
"
,d.year);

    char buffer[7];
    stringFromDate(&d,buffer);
    printf("%s\
"
,buffer);
}