Previous Next Contents Index Doc Set Home


The C-Pascal Interface

6


This chapter describes how to mix C and Pascal modules in the same program. It contains the following sections:

Compilation of Mixed-Language Programs

page 93

Compatibility of Types for C and Pascal

page 94

General Parameter Passing in C and Pascal

page 97

Procedure Calls: C-Pascal

page 97

Procedure Calls: Pascal-C

page 121

Procedures and Functions as Parameters

page 137

Global Variables in C and Pascal

page 138

File-Passing Between Pascal and C

page 139

The examples in this chapter assume that you are using the ANSI C compiler. To invoke ANSI C:


Compilation of Mixed-Language Programs

You must use the compiler option -lpc when you compile a C main routine that calls Pascal. -lpc includes the Pascal object library libpc. For example:

hostname% pc -c -calign my_pascal.p
hostname% cc my_pascal.o my_c.c -lpc
The -c option produces an unlinked object file. The -calign option causes pc to use C-like data formats for aggregate objects.

When you compile a Pascal main routine that calls C, you don't have to use any special options, but the -calign option is again useful. The C object library, libc, is automatically brought in for every Pascal compilation.

For example:

hostname% cc -c my_c.c
hostname% pc -calign my_c.o my_pascal.p


Compatibility of Types for C and Pascal

Table 6-1 and Table 6-2 list the default sizes and alignments of compatible types for C and Pascal.

Table  6-1 C and Pascal Size and Alignment of Compatible Types  

Pascal Type
C Type
Size (bytes)
Alignment (bytes)

double

double

8

8

longreal

double

8

8

real

double

8

8

single

float

4

4

shortreal

float

4

4

integer16

short int

2

2

integer32

int

4

4

integer

int

4

4

-128..127

char

1

1

boolean

char

1

1

alfa

char a[10]

10

1

char

char

1

1

string

char a[80]

80

1

varying[n] of char

struct{int, char[n]}

-

4

record

struct/union

-

Same as element type

array

array

-

Same as element type

variant record

struct/union

-

-

fields in packed record

bit field

-

-

Table  6-2 C and Pascal Size and Alignment of Compatible Types with -xl -

Pascal Type
C Type
Size (bytes)
Alignment (bytes)

real

float

4

4

integer

short int

2

2

Precautions with Compatible Types

This section describes the precautions you should take when working with compatible types.

The shortreal Type

The Pascal shortreal and C float compatibility works if you pass by reference. See "Value Parameters" on page 116 for examples that show you how to pass by value.

Character Strings

C has several assumptions about strings. All C strings are:

You can satisfy these assumptions as follows:

Array Indexes

Pascal array indexes can start at any integer; C array indexes always start at zero.

Aggregate Types

Aggregate types include arrays, varying arrays, sets, strings, alphas, records, and variant records.

Pascal aggregate types may require alignment and layout adjustment to match C unions, structures, and arrays. Pascal aggregate types can be any of the following: arrays, varying arrays, sets, strings, alphas, records, or variant records.

However, you can use the -calign option to eliminate some of these differences. With -calign, the following assumptions are made:

Incompatibilities

This section describes the incompatibilities between C and Pascal types.

Enumerated Types

C enumerated types are incompatible with Pascal enumerated types. Pascal enumerated types are represented internally by sequences of integral values starting with 0. Storage is allocated for a variable of an enumerated type as if the type was a subrange of integer. For example, an enumerated type of fewer than 128 elements is treated as 0..127, which, according to the rules above, is equivalent to a char in C.

C enumerated types are allocated a full word and can take on arbitrary integer values.

Pascal Set Types

In Pascal, a set type is implemented as a bit vector, which is similar to a C short-word array, where each short-word contains two bytes. Thus, sets are bit-vectors, and they are allocated in multiples of 16 bits. To find out the size of a set, enter the following code:

ceiling( ord( highest_element ) / 16 )

Direct access to individual elements of a set is highly machine-dependent and should be avoided.


General Parameter Passing in C and Pascal

A few general rules apply to parameter passing:


Procedure Calls: C-Pascal

Here are examples of how a C main program calls a Pascal procedure:

The Pascal procedure, Samp, in the file Samp.p. Note the procedure definition.

procedure Samp(var i: integer; var r: real);
begin
    i := 9;
    r := 9.9
end; { Samp }

The C main program, SampMain.c. Note the procedure definition and call.

#include <stdio.h>

extern void Samp(int *, double *);

int main (void)
{
   int     i ;
   double  d ;

   Samp(&i, &d) ;
   printf ("%d  %3.1f \n", i, d) ;
}

The commands to compile and execute Samp.p and SampMain.c

hostname% pc -c Samp.p
hostname% cc Samp.o SampMain.c
hostname% a.out 
 9 9.9

Variable Parameters

Pascal passes all variable parameters by reference, which C can do, too.

Simple Types without -xl

Without -xl, simple types match, as in the following example:

The Pascal procedure, SimVar.p

procedure SimVar(
var t, f: boolean;
var c: char;
var si: integer16;
var i: integer;
var sr: shortreal;
var r: real);
begin
    t := true;
f := false;
c := 'z';
si := 9;
i := 9;
sr := 9.9;
r := 9.9;
end; { SimVar }

The C main program, SimVarMain.c

#include <stdio.h>

extern void SimVar(char *, char *, char *, short *,
int *, float *, double *);

int main(void)
{
char t, f, c;
short si;
int i;
float sr;
double r;

    SimVar(&t, &f, &c, &si, &i, &sr, &r);
printf(" %08o %08o %c %d %d %3.1f %3.1f \n",
		t, f, c, si, i, sr, r);
}

The commands to compile and execute SimVar.p and SimVarMain.c

hostname% pc -c SimVar.p
hostname% cc SimVar.o SimVarMain.c
hostname% a.out
 00000001 00000000 z 9 9 9.9 9.9

Simple Types with -xl

With the -xl option, the Pascal real must be paired with a C float, and the Pascal integer must be paired with a C short int.

Strings of Characters

The C counterpart to the Pascal alfa and string types are arrays; C passes all arrays by reference. The C counterpart to the Pascal varying is a structure; C passes structures by value.

Before you call Pascal with a null varying string, set the byte count to zero because that is what Pascal assumes about such strings.

C can pass a structure consisting of a four-byte integer and an array of characters to a Pascal procedure, expecting a var parameter that is a variable-length string.

See the following example:

The Pascal procedure, StrVar.p

type
TVarStr = varying [25] of char;

procedure StrVar(
var a: alfa;
var s: string;
var v: TVarStr);
begin
a := 'abcdefghi' + chr(0);
s := 'abcdefghijklmnopqrstuvwxyz' + chr(0);
v := 'varstr' + chr(0);
end;  { StrVar }

The C main program, StrVarMain.c

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

struct TVarLenStr {
int nbytes;
char a[25];
};

extern void StrVar(char *, char *, struct TVarLenStr *);

int main(void)
{
struct TVarLenStr vls;
char s10[10], s80[80], s25[25];

    vls.nbytes = 0;
StrVar(s10, s80, &vls);
strncpy(s25, vls.a, vls.nbytes);
printf(" s10 = '%s' \n s80 = '%s' \n s25 = '%s' \n",
s10, s80, s25);
printf(" strlen(s25) = %d \n", strlen(s25));
}

The commands to compile and execute StrVar.p and StrVarMain.c

hostname% pc -c StrVar.p
hostname% cc StrVar.o StrVarMain.c -lpc
hostname% a.out
 s10='abcdefghi'
 s80='abcdefghijklmnopqrtstuvwxyz'
 s25='varstr'
 strlen(s25)=6

Fixed Arrays

For a fixed array parameter, pass the same type and size by reference, as shown in the following example:

The Pascal procedure, FixVec.p

type
    VecTyp = array [0..8] of integer;

procedure FixVec(var V: TVec; var Sum: integer);
var
    i: integer;
begin
    Sum := 0;
    for i := 0 to 8 do 
        Sum := Sum + V[i]
end; { FixVec }

The C main program, FixVecMain.c

#include <stdio.h>

extern void FixVec(int [], int *);

int main(void)
{
    int Sum;
static int a[] = {0,1,2,3,4,5,6,7,8};

    FixVec(a, &Sum);
printf(" %d \n", Sum);
}

The commands to compile and execute FixVec.p and FixVecMain.c

hostname% pc -c -calign FixVec.p 
hostname% cc FixVec.o FixVecMain.c -lpc
hostname% a.out
 36

Although it does not apply in this example, arrays of aggregates in Pascal have, by default, a size that is always a multiple of four bytes. When you use the -calign option to compile the Pascal code, that difference with C is eliminated.

The following example illustrates this point. The string 'Sunday' only gets through to the C main program when you compile the Pascal routine using
-calign.

The Pascal procedure, DaysOfWeek.p

type
	TDay= array [0..8] of char;
	TWeek = array [0..6] of day;
	TYear = array [0..51] of week;

procedure DaysOfWeek(var Y: TYear);
begin
	v[1][1] := 'Sunday';
end;

The C main program, DaysOfWeekMain.c

#include <stdio.h>

extern void DaysOfWeek(char [][7][9]);

int main(void)
{
    char Year[52][7][9];

    DaysOfWeek(Year);
printf(" Day = '%s' \n", Year[1][1]);
}

The commands to compile and execute DaysOfWeek.p and DaysOfWeekMain.c without
-calign

hostname% pc -c DaysOfWeek.p
hostname% cc DaysOfWeek.o DaysOfWeekMain.c -lpc
hostname% a.out
 Day = ''

The commands to compile and execute DaysOfWeek.p and DaysOfWeekMain.c with -calign

hostname% pc -c -calign DaysOfWeek.p
hostname% cc DaysOfWeek.o DaysOfWeekMain.c -lpc
hostname% a.out
day = 'Sunday '

The univ Arrays

You can pass any size array to a Pascal procedure expecting a univ array, although there is no special gain in doing so, because there is no type or size checking for separate compilations. However, if you want to use an existing Pascal procedure that has a univ array, you can do so. All univ arrays that are in, out, in out, or var parameters pass by reference.

The Pascal procedure, UniVec.p, which defines a 10-element array

type
TVec = array [0..9] of integer;

procedure UniVec(
var V: univ TVec;
in Last: integer;
var Sum: integer);
var
i: integer;
begin
Sum := 0;
for i := 0 to Last do
Sum := Sum + V[i];
end; { UniVec }

The C main program, UniVecMain.c, which passes a 3-element array to the Pascal procedure written to do a 10-element array

#include <stdio.h>

extern void UniVec(int *, int, int *);

int main(void)
{
int Sum;
static int a[] = {7, 8, 9};
	
    UniVec(a, 2, &Sum);
printf(" %d \n", Sum);
}

The commands to compile and execute UniVec.p and UniVecMain.c with -calign

hostname% pc -c -calign UniVec.p
hostname% cc UniVec.o UniVecMain.c -lpc
hostname% a.out
 24

Conformant Arrays

For single-dimension conformant arrays, pass upper and lower bounds, placed after the declared parameter list. If the array is multidimensional, pass element widths as well, one element width for each dimension, except the last one.

See this example:

function ip(var x: array [lb..ub: integer] of real): real;

extern double ip(double [], int, int);
...
    double v1[10];
double z;
z = ip(v1, 0, 9);
...

One bounds pair may apply to several arrays if they are declared in the same parameter group:

function ip(var x,y:array[lb..ub:integer] of real):real;
	 ...
	 double v1[10], v2[10] ;
	 extern double ip() ;
	 double z ;
	 z = ip ( v1, v2, 0, 9 ) ;
	 ...

With multidimensional arrays, for all dimensions but the last one, pass the low bound, high bound, and element width.

Examples of single-dimension, multidimension, and array-of-character conformant arrays follow. Conformant arrays are included here only because they are a relatively standard feature; there are usually more efficient and simpler ways to do the same thing.

Example 1: Single-Dimension Array

The Pascal procedure, IntCA.p. Pascal passes the bounds pair.

procedure IntCA(var a: array [lb..ub: integer] of integer);
begin
    a[1] := 1;
    a[2] := 2
end; { IntCA }

The C main program, IntCAMain.c

#include <stdio.h>

extern void IntCA(int [], int, int);

int main(void)
{
    int k ;
    static int s[] = { 0, 0, 0 };

    IntCA (s, 0, sizeof(s)-1);
    for (k=0 ; k < 3 ; k++)
        printf(" %d \n", s[k]);
}

The commands to compile and execute IntCA.p and IntCAMain.c with -calign

hostname% pc -c -calign IntCA.p
hostname% cc IntCA.o IntCAMain.c -lpc
hostname% a.out
	 0
	 1
	 2

Example 2: Multi-Dimension Array

The Pascal procedure, RealCA.p. Pascal passes low bound, high bound, and element width.

procedure RealCA(var A: array [r1..r2: integer] of
                        array [c1..c2: integer] of real);
var
    col, row: integer;
begin
    for row := r1 to r2 do
        for col := c1 to c2 do
            if row = col then
                A[row, col] := 1.0
            else
                A[row, col] := 0.0
end; { RealCA }

The C main program, RealCAMain.c. Array M has 2 rows of 3 columns each. c1 and c2 are the first and last columns. r1 and r2 are the first and last rows. wc is the width of a column element (smallest) and is equal to sizeof( M[0][0] ). wr is the width of a row element (next largest) and is equal to
(c2-c1+1) * wc.

#include <stdio.h>
#define NC 3
#define NR 2
extern void RealCA(double [][NC], int, int, int, int, int);
int main(void)
{
    double        M[NR][NC];
    int           col, c1, c2, row, r1, r2, wc, wr;
    
    c1 = 0;
r1 = 0;
c2 = NC - 1;
r2 = NR - 1;
wc = sizeof(M[0][0]);
wr = (c2 - c1 + 1) * wc;
    RealCA(M, r1, r2, wr, c1, c2);
for (row = r1; row <= r2; row++) {
printf("\n");
for (col = c1; col <= c2; col++)
printf("%4.1f", M[row][col]);
};
printf("\n");
}

The commands to compile and execute RealCA.p and RealCAMain.c with -calign

hostname% pc -c -calign RealCA.p
hostname% cc RealCA.o RealCAMain.c -lpc
hostname% a.out

    1.0    0.0    0.0
    0.0    1.0    0.0

If wc is the width of the smallest element, as determined by sizeof(), then the width of the next largest element is the number of those smaller elements in the next larger element multiplied by wc.

width of next largest element = (ub - lb + 1) * wc
In general, (lb, ub, wc) are the bounds and element width of the next lower dimension of the array. This definition is recursive.

Example 3: Array of Characters

The Pascal procedure, ChrCAVar.p

procedure ChrCAVar(var a: array [lb..ub: integer] of char);

begin
    a[0]  := 'T';
    a[13] := 'o';
end; { ChrCAVar }

The C main program, ChrCAVarMain.c. For C, the lower bound is always 0.

#include <stdio.h>

extern void ChrCAVar(char [], int, int);

int main(void)
{
    static char s[] = "this is a string" ;

    ChrCAVar( s, 0, sizeof(s)-1) ; /*(s, lower, upper)*/
    printf("%11s \n", s) ;
}

The commands to compile and execute ChrCAVar.p and ChrCAVarMain.c

hostname% pc -c -calign ChrCAVar.p
hostname% cc ChrCAVar.o ChrCAVarMain.c -lpc
hostname% a.out
This is a string

Records and Structures

In most cases, a Pascal record describes the same objects as its C structure equivalent, provided that the components have compatible types and are declared in the same order. The compatibility of the types depends mostly on size and alignment. For more information on size and alignments of simple components, see "Compatibility of Types for C and Pascal" on page 94.

By default, the alignment of a record is always four bytes and the size of a record is always a multiple of four bytes. However, when you use -calign in compiling the Pascal code, the size and alignment of the Pascal record matches the size and alignment of the equivalent C structure.

A Pascal record of an integer and a character string matches a C structure of the same constructs, as follows:

The Pascal procedure, StruChr.p. It is safer for the Pascal procedure to explicitly provide the null byte and include it in the count before the string is passed to C.

type
    TLenStr = record
          nbytes: integer;
          chrstr: array [0..24] of char
      end;

procedure StruChr(var v: TLenStr);
begin
    v.NBytes := 14;
    v.ChrStr := 'St. Petersburg' + chr(0);
end; { StruChr }

The C main program, StruChrMain.c

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

struct TVarLenStr {
int NBytes;
char a[25];
};

extern void StruChr(struct TVarLenStr *);

int main(void)
{
    struct TVarLenStr vls;
char s25[25];

    vls.NBytes = 0;
StruChr(&vls);
strncpy(s25, vls.a, vls.NBytes);
printf(" s25 = '%s' \n", s25);
printf(" strlen(s25) = %d \n", strlen(s25));
}

The commands to compile and execute StruChr.p and StruChrMain.c

hostname% pc -c StruChr.p
hostname% cc StruChr.o StruChrMain.c -lpc
hostname% a.out
 s25='St. Petersburg'
 strlen(s25) = 13

The record in the example above has, by default, the same size and alignment as the equivalent C record. Some records, though, are laid out differently unless you use the -calign option.

Consider this example:

The Pascal routine, DayWeather.p

type
    TDayWeather = record
TDay: array [0..8] of char;
TWeather: array [0..20] of char;
end;
	
    TDayWeatherArray = array [0..1] of TDayWeather;

procedure DayWeather(var W: TDayWeatherArray;
var WeatherSize: integer);
begin
W[1].TDay := 'Sunday' + chr(0);
W[1].TWeather := 'Sunny' + chr(0);
WeatherSize := 5;
end; { StruChr }

The C main program, DayWeatherMain.c

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

struct TDayRec {
char TDay[9];
char TWeather[21];
};

extern void DayWeather(struct TDayRec [], int *);

int main(void)
{
char s25[25];
char t25[25];
struct TDayRec dr[2];
int nbytes = 0;

    DayWeather(dr, &nbytes);
strncpy(s25, dr[1].TDay, 6);
printf(" day = '%s' \n", s25);
strncpy(t25, dr[1].TWeather, nbytes);
printf(" weather = '%s' \n", t25);
}

When you compile the Pascal routine without using the -calign option, the program does not work correctly.

The commands to compile and execute DayWeather.p and DayWeatherMain.c without -calign

hostname% pc -c DayWeather.p
hostname% cc DayWeather.o DayWeatherMain.c -lpc
hostname% a.out
 day = ''
 weather = ' sun'

The commands to compile and execute DayWeather.p and DayWeatherMain.c with -calign

hostname% pc -calign -c DayWeather.p
hostname% cc DayWeather.o DayWeatherMain.c -lpc
hostname% a.out
 day = 'Sunday'
 weather = 'sunny'

Variant Records

C equivalents of variant records can sometimes be constructed, although there is some variation with architecture and sometimes a need to adjust alignment. You can avoid the need to adjust alignment by using the -calign option.

The Pascal procedure, VarRec.p

type
    vr = record
          case tag: char of
              'a': (ch1, ch2: char);
              'b': (flag: boolean);
              'K': (ALIGN: integer);
      end;

procedure VarRec(var x: vr);
begin
    if x.ch1 = 'a' then 
        x.ch2 := 'Z'
end; { VarRec }

The C main program, VarRecMain.c

#include <stdio.h>

    struct vlr {
        char tag;
        union {
            struct {
                char ch1, ch2;
            }a_var;
            struct {
                char flag;
            }b_var;
            struct {
                int ALIGN;
            }c_var;
        }var_part;
};

extern void VarRec(struct vlr *);

int main(void)
{
    struct vlr *x;
    
    x = (struct vlr *)malloc(sizeof(struct vlr));
x->tag = 'a';
x->var_part.a_var.ch1 = 'a';
x->var_part.a_var.ch2 = 'b';
VarRec(x);
printf(" %c \n", x->var_part.a_var.ch2);
}

The commands to compile and execute VarRec.p and VarRecMain.c

hostname% pc -c -calign VarRec.p
hostname% cc VarRec.o VarRecMain.c -lpc
hostname% a.out
 Z

Pascal Set Type

In Pascal, a set type is implemented as a bit vector, which is similar to a C short-word array. Direct access to individual elements of a set is highly machine-dependent and should be avoided.

In Pascal, bits are numbered within a byte from the most significant to least, as shown in Table 6-3.

Table  6-3 Set Implementation

Set
Bit Numbering

set+3:

31, 30, 29, 28, 27, 26, 25, 24

set+2:

23, 22, 21, 20, 19, 18, 17, 16

set+1:

15, 14, 13, 12, 11, 10, 9, 8

set+0:

7, 6, 5, 4, 3, 2, 1, 0

In C, a set could be described as a short-word array beginning at an even address. With the current set representation, it does not matter what the lower-bound value is.

The nth element in a set [lower...upper] can be tested as follows:

#define LG2BITSLONG 5 /* log2( bits in long word) */
#define LG2BITSWORD 4 /* log2( bits in short word) */
#define MSKBITSLONG 0x1f
#define MSKBITSHORT 0x0

	short *setptr; /* set as array of shorts */
	int upper;     /* upper bound of the set */
	int elem;      /* ordinal value of set element */
	int i;

	if  ( setptr[elem >> LG2BITSWORD]  &
	(1 <<  (elem & MSKBITSWORD))      )  {
	 		  /* elem is in set */
	}

Pascal intset Type

The Pascal intset type is predefined as set of [0..127]. A variable of this type takes 16 bytes of storage.

The Pascal procedure, IntSetVar.p, which has an intset of the elements
[1, 3, 7, 8]

procedure IntSetVar(var s: intset);
begin
    s := [1, 3, 7, 8]
end; { IntSetVar }

The C main program, IntSetVarMain.c

#include <stdio.h>

extern void IntSetVar(unsigned int *);

int main(void)
{
    int  w ;
    unsigned int  *p, *s ;

    s = (unsigned int *) malloc(16);
    IntSetVar(s) ;
    for (w = 0, p = s ; w < 4 ; w++, p++)
        printf("%012o %3d \n", *p, w);
    printf(" 110 001 010 (binary, word 4) \n");
    printf(" 876 543 210 (bits, word 4)" \n");
}

The commands to compile and execute IntSetVar.p and IntSetVarMain.c. The output of this example depends on the architecture of your machine.

hostname% pc -c IntSetVar.p
hostname% cc IntSetVar.o IntSetVarMain.c -lpc
hostname% a.out 
 000000000000 0
 000000000000 1
 000000000000 2
 000000000612 3
 110 001 010 (binary, word 4)
 876 543 210 (bits, word 4)

Value Parameters

There are three types of value parameters in Pascal.

Simple Types without -xl

Without -xl, simple types match, as in the following example:

The Pascal procedure, SimVal. p. t, f, c, i, r, and s are value parameters.

procedure SimVal(
t, f: boolean;
	c:  char;
	si: integer16;
	i:  integer;
	sr: shortreal;
	r:  real;
	var reply: integer);

begin
Reply := 0;
if t then
Reply := Reply + 1;
if not f then
Reply := Reply + 8;
if c='z' then
Reply := Reply + 64;
if si=9 then
Reply := Reply + 512;
if i=9 then
Reply := Reply + 4096;
if sr=shortreal(9.9) then
Reply := Reply + 32768;
if r=9.9 then
Reply := Reply + 262144;
end; { SimVal }

The C main program, SimValMain.c

#include <stdio.h>

extern void SimVal(
char, char, char,
short,
int,
float,
double,
int *);

int main(void)
{
char t = 1, f = 0;
char c = 'z';
short si = 9;
int i = 9;
float sr = 9.9;
double r = 9.9;
int args;

    SimVal(t, f, c, si, i, sr, r, &args);
printf(" args = %06o \n", args);

The commands to compile and execute SimVal.p and SimValMain.c

hostname% pc -c SimVal.p
hostname% cc SimVal.o SimValMain.c -lpc
hostname% a.out
 args=111111

If no function prototype is provided for SimVal in SimValMain.c, then sr:shortreal must be changed to sr:real in SimVal.p. This change is necessary because in C, a float is promoted to double in the absence of function prototypes. In -xl mode, change sr:shortreal to sr:longreal.

Simple Types with -xl

With -xl, the Pascal real must be paired with a C float, and the Pascal integer must be paired with a C short int.

Arrays

Since C cannot pass arrays by value, it cannot pass strings of characters, fixed arrays, or univ arrays by value.

Conformant Arrays

Pascal passes all value parameters on the stack or in registers, except for value conformant array parameters, which are handled by creating a copy in the caller environment and passing a pointer to the copy. In addition, the bounds of the array must be passed (see "Conformant Arrays" on page 105).

This example is the same as the single-dimension example in "Conformant Arrays," except that the var prefix is deleted.

The Pascal procedure, ChrCAVal.p

procedure ChrCAVal(a: array [lb..ub: integer] of char);
begin
    a[0] := 'T';
    a[13] := 'o';
end; { ChrCAVal }

The C main program, ChrCAValMain.c

#include <stdio.h>

extern void ChrCAVal(char [], int, int);

int main(void)
{
    static char s[] = "This is a string";
	
    ChrCAVal(s, 0, sizeof(s) -1);
printf(" %11s \n", s);
}

The commands to compile and execute ChrCAVal.p and ChrCAValMain.c with
-calign

hostname% pc -c -calign ChrCAVal.p
hostname% cc ChrCAVal.o ChrCAValMain.c -lpc
hostname% a.out
This is a string

Function Return Values

Function return values match types the same as with parameters, and they pass in much the same way.

Simple Types

The simple types pass in a straightforward way, as follows:

The Pascal function, RetReal.p

function RetReal(x: real): real;
begin
    RetReal := x + 1.0
end; { RetReal }

The C main program, RetRealMain.c

#include <stdio.h>

extern double RetReal(double);

int main(void)
{
double r, s;

    r = 2.0;
s = RetReal(r);
printf(" %f \n", s);
}

The commands to compile and execute RetReal.p and RetRealMain.c

hostname% pc -c RetReal.p
hostname% cc RetReal.o RetRealMain.c
hostname% a.out
 3.000000

Input and Output

If your C main program calls a Pascal procedure that does I/O, then include the following code before you call the Pascal procedure:

PASCAL_IO_INIT();

Also, in the C main program just before exit, add the following line:

PASCAL_IO_DONE();

See this example:

The Pascal procedure, pasc_read.p

procedure pasc_read;
var
    Tfile : text;
    data : integer;

begin
  writeln ('In Pascal procedure');
  reset(Tfile, 'data.txt');

  while not eof(Tfile)
    do
      begin
        readln (Tfile,data)
      end;

  writeln ('At end of Pascal procedure',data)
end;

The C main program, main.c

#include <stdio.h>

extern void pasc_read();

int main(void)
{
	FILE *ptr;
printf ("Calling Pascal routine\n");
PASCAL_IO_INIT();
pasc_read();
PASCAL_IO_DONE();
printf ("After Pascal routine\n");
}

The commands to compile and execute pasc_read.p and main.c

hostname% pc pasc_read.p -c 
hostname% cc -g main.c pasc_read.o -lpc
hostname% a.out
Calling Pascal routine
In Pascal procedure
At end of Pascal procedure	1
After Pascal routine


Procedure Calls: Pascal-C

This section parallels the section, "Procedure Calls: C-Pascal" on page 97. Earlier comments and restrictions also apply here.

Variable Parameters

Pascal passes all variable parameters by reference, which C can do, too.

Simple Types

Simple types pass in a straightforward manner, as follows:

The C function, SimRef.c

void SimRef(
char *t,
char *f,
char *c,
short *si,
int *i,
float *sr,
double *r)
{
    *t = 1;
*f = 0;
*c = 'z';
*si = 9;
*i = 9;
*sr = 9.9;
*r = 9.9;
}

The Pascal main program, SimRefMain.p

program SimRefMain(output);
var
t, f: boolean;
c: char;
si: integer16;
i: integer;
sr: shortreal;
r: real;

procedure SimRef(
var t, f: boolean;
var c: char;
var si: integer16;
var i: integer;
var sr: shortreal;
var r: real);
external c;
begin
SimRef(t, f, c, si, i, sr, r);
writeln(t, f: 6, c: 2, si: 2, i: 2, sr :4:1, r :4:1);
end. { SimRefMain }

The commands to compile and execute SimRef.c and SimRefMain.p

hostname% cc -c SimRef.c
hostname% pc SimRef.o SimRefMain.p
hostname% a.out
true false z 9 9 9.9 9.9

Strings of Characters

The alfa and string types pass simply; varying strings are more complicated. All pass by reference.

The C function, StrVar.c

#include <string.h>

struct TVarLenStr {
int nbytes;
char a[26];
};

void StrVar(char *s10, char *s80, struct TVarLenStr *vls)
{
    static char ax[11] = "abcdefghij";
static char sx[81] = "abcdefghijklmnopqrstuvwxyz";
static char vx[6] = "varstr";
    
    strncpy(s10, ax, 11);
strncpy(s80, sx, 80);
strncpy(vls->a, vx, 6);
vls->nbytes = 6;
}

The Pascal main program, StrVarMain.p

program StrVarMain(output);
type
    TVarStr = varying[26] of char;

var
    a: alfa;
    s: string;
    v: TVarstr;

procedure StrVar(var a: alfa; var s: string; var v: TVarStr);
		external c;

begin
    StrVar(a, s, v);
writeln(a);
writeln(s);
writeln(v);
writeln(' length(v) = ', length(v) :2);
end. { StrVarMain }

The commands to compile and execute StrVar.c and StrVarMain.p

hostname% cc -c StrVar.c
hostname% pc StrVar.o StrVarMain.p
hostname% a.out
abcdefghij
abcdefghijklmnopqrtstuvwxyz
varstr
 length(v) = 6

Avoid constructs that rely on strings being in static variable storage. For example, you could use mktemp(3) in Pascal as follows:

Incorrect use of string in static variable storage

tmp := mktemp('/tmp/eph.xxxxxx')

This use is incorrect, since mktemp()modifies its argument. Instead, use the C library routine strncpy() (see string(3)) to copy the string constant to a declared char array variable, as in:

Correct solution using the C library routine strncpy()

program Use_mktemp ;

procedure strncpy( var dest: univ string ;
 	var srce: univ string ;
	length: integer) ; 	 external c ;

procedure mktemp(var dest: univ string); external c;
    ...
var path: string ;
begin
    ...
strncpy( path, '/tmp/eph.xxxxxx', sizeof(path)) ;
mktemp( path ) ;
    ...
end .

Fixed Arrays

For a fixed-array parameter, pass the same type and size, as in this example:

The C function, FixVec.c

void FixVec(int V[9], int *Sum)
{
    int i;
    
    *Sum = 0;
for (i = 0; i <= 8; i++)
*Sum = *Sum + V[i];
}

The Pascal main program, FixVecMain.p

program FixVecMain(output);
type
    TVec = array [0..8] of integer;
var
    V: TVec := [0, 1, 2, 3, 4, 5, 6, 7, 8];
    Sum: integer;

procedure FixVec(var XV: TVec; var XSum: integer); external c;

begin
    FixVec(V, Sum);
    writeln(Sum: 3);
end. { FixVecMain }

The commands to compile and execute FixVec.c and FixVecMain.p

hostname% cc -c FixVec.c
hostname% pc -calign FixVec.o FixVecMain.p
hostname% a.out
 36

The -calign option is not needed for this example, but may be necessary if the array parameter is an array of aggregates.

The univ Arrays

The univ arrays that are in, out, in out, or var parameters pass by reference.

Here is an example:

The C function, UniVec.c

void UniVec(int V[3], int Last, int *Sum)
{
int i;
    
    *Sum = 0;
for (i = 0; i <= Last; i++)
*Sum += V[i];
}

The Pascal main program, UniVecMain.p

program UniVecMain(output);
type
TVec = array [0..9] of integer;
var
Sum: integer;
    V: array [0..2] of integer;

procedure UniVec(var V: univ TVec; in Last: integer;
var Sum: integer);
external c;

begin
    V[0] := 7;
V[1] := 8;
V[2] := 9;
UniVec(V, 2, Sum);
writeln(Sum);
end. { UniVecMain }

The commands to compile and execute UniVec.c and UniVecMain.p

hostname% cc -c UniVec.c
hostname% pc -calign UniVec.o UniVecMain.p
hostname% a.out
	        24

The -calign option is not needed for this example, but may be necessary if the array parameter is an array of aggregates.

Conformant Arrays

For single-dimension conformant arrays, pass upper and lower bounds placed after the declared parameter list. If the array is multidimensional, pass element widths as well, one element width for each dimension, except the last one. Chapter 8, "The FORTRAN-Pascal Interface," has an example of multidimensional conformant array passing.

The following example uses a single-dimension array:

The C function, IntCA.c

void IntCA(int a[], int lb, int ub)
{
    	int k;
    
    for (k=0; k <= ub - lb; k++)
a[k] = 4;
}

The Pascal main program, IntCAMain.p. Note that what Pascal passes as s, is received in C as a, lb, ub.

program IntCAMain(output);

var
    s: array [1..3] of integer;
    i: integer;

procedure IntCA(var a: array [lb..ub: integer] of integer);
external c;

begin
    IntCA(s);
    for i := 1 to 3 do
      write(s[i]);
    writeln
end. { IntCAMain }

The commands to compile and execute IntCA.c and IntCAMain.p

hostname% cc -c IntCA.c
hostname% pc -calign IntCA.o IntCAMain.p 
hostname% a.out
            4          4         4

The -calign option is not needed for this example, but may be necessary if the array parameter is an array of aggregates.

Records and Structures

In most cases, a Pascal record describes the same objects as its C structure equivalent, provided that the components have compatible types and are declared in the same order. For more information, see "Compatibility of Types for C and Pascal" on page 94.

Records that contain aggregates may differ because aggregates in C and Pascal sometimes have different sizes and alignments. If you compile the Pascal code with the -calign option, the differences are eliminated.

A Pascal record of an integer and a character string matches a C structure of an integer and an array of char values, as follows:

The C function, StruChr.c

#include <string.h>

struct TVarLenStr {
int nbytes;
char a[26];
};

void StruChr(struct TVarLenStr *v)
{
    strncpy(v->a, "strvar", 6);
v->nbytes = 6;
}

The Pascal main program, StruChrMain.p

program StruChrMain(output);
type
    TVarLenStr = record
          nbytes: integer;
          a: array [0..25] of char
    end;
var
    vls: TVarLenStr;
    i: integer;

procedure StruChr(var vls: TVarLenStr); external c;

begin
    StruChr(vls);
    write(' string=''');
    for i := 0 to vls.nbytes - 1 do
        write(vls.a[i]);
    writeln('''');
    writeln(' length = ', vls.nbytes)
end. { StruChrMain }

The commands to compile and execute StruChr.c and StruChrMain.p

hostname% cc -c StruChr.c
hostname% pc -calign StruChr.o StruChrMain.p
hostname% a.out
 string=' strvar'
 length=          6

Variant Records

C equivalents of variant records can sometimes be constructed, although there is some variation with the architecture, and sometimes you have to adjust the alignment.

Following are some examples:

The C function, VarRec.c

struct vlr {
char tag;
union {
struct {
char ch1, ch2;
} a_var;
struct {
char flag;
} b_var;
struct {
int ALIGN;
} c_var;
} var_part;
};

void VarRec(struct vlr *x)
{
if (x->var_part.a_var.ch1 == 'a')
x->var_part.a_var.ch2 = 'Z';
}

The Pascal main program, VarRecMain.p

program VarRecMain;
type
    vr = record
        case tag: char of
            'a': (ch1, ch2: char);
            'b': (flag: boolean);
            'K': (ALIGN: integer)
      end;
var
    x: vr;

procedure VarRec(var d: vr); external c;

begin
    x.tag := 'a';
    x.ch1 := 'a';
    x.ch2 := 'b';
    VarRec(x);
    writeln(x.ch2)
end. { VarRecMain }

The commands to compile and execute VarRec.c and VarRecMain.p

hostname% cc -c VarRec.c
hostname% pc -calign VarRec.o VarRecMain.p
hostname% a.out
Z

The -calign option is not needed in the previous example, but may be necessary if the record contains aggregates.

Non-Pascal Procedures

When you use the -xl option in compiling Pascal code, you can use the nonpascal keyword to declare that an external procedure is written in another language. This keyword generally causes everything to be passed by reference.

See this example:

The C function, NonPas.c. In the function for_C, s is a pointer (declared var in the procedure declaration), and len is not a pointer (not declared var in the procedure declaration). In the function for_nonpascal, s is still a pointer (though not declared var in the procedure declaration), and len is now a pointer (though not declared var).

#include <stdio.h>

void for_C(char *s, int len)
{
int i;
for (i = 0; i < len; i++)
putchar(s[i]);
putchar('\n');
}

void for_NonPascal(char *s, int *len)
{
int i;
for (i = 0; i < *len; i++)
putchar(s[i]);
putchar('\n');
}

The Pascal main program, NonPasMain.p

program NonPasMain;
var
    s: string;

procedure for_C(var s: string; len: integer); external c;
procedure for_NonPascal(var s: string; len: integer); nonpascal;

begin
    s :='Hello from Pascal';
    for_C(s, 18);
    for_NonPascal(s, 18);
end.  { NonPasMain }

The commands to compile and execute NonPas.c and NonPasMain.p

hostname% cc -c NonPas.c
hostname% pc NonPas.o NonPasMain.p
hostname% a.out
 Hello from Pascal
 Hello from Pascal

Value Parameters

In general, Pascal passes value parameters in registers or on the stack, widening to a full word if necessary.

Simple Types

With value parameters, simple types match, as in the following example:

The C function, SimVal.c

void SimVal(
char t,
char f,
char c,
short si,
int i,
float sr,
double r,
int *reply)
{
*reply = 0;
if (t) *reply += 01;
if (!f) *reply += 010;
if (c == 'z') *reply += 0100;
if (si == 9) *reply += 01000;
if (i == 9) *reply += 010000;
if (sr ==(float)9.9) *reply += 0100000;
if (r == 9.9) *reply +=01000000;
}

The Pascal main program, SimValMain.p

program SimVal(output);

var
    t: boolean					:= true;
    f: boolean					:= false;
    c: char 					:= 'z';
     si: integer16					:= 9;
i: integer := 9;
sr: shortreal := 9.9;
r: double := 9.9;
args: integer;

procedure SimVal(
t, f: boolean;
c: char;
si: integer16;
i: integer;
sr: shortreal;
r: double;
var Reply: integer);
external c;
begin
SimVal(t, f, c, si, i, sr, r, args);
writeln(' args = ', args :6 oct);
end.  { SimVal }

The commands to compile and execute SimVal.c and SimValMain.p

hostname% cc -c SimVal.c 
hostname% pc SimVal.o SimValMain.p 
hostname% a.out
 args=111111

Function Return Values

Function return values match types in the same manner as with parameters, and they pass in much the same way. See "Variable Parameters" on page 98. The following example shows how to pass simple types.

The C function, RetReal.c

double RetReal(double *x)
{
return(*x + 1.0);
}

The Pascal main program, RetRealMain.p

program RetRealMain;
var
    r, s: real;

function RetReal(var x: real): real; external c;

begin
    r := 2.0;
    s := RetReal(r);
    writeln(r: 4: 1, s: 4: 1)
end. { RetRealMain }

The commands to compile and execute RetReal.c and RetRealMain.p

hostname% cc -c RetReal.c
hostname% pc RetReal.o RetRealMain.p
hostname% a.out
 2.0 3.0

Parameters That Are Pointers to Procedures

Pascal has a special type that is a pointer to a procedure. A variable of this type can be used as a parameter, as follows:

The C function, ProcPar.c

#include <string.h>

void proc_c (void (*p)())  /* a pointer to procedure argument */
{
    char *s ;
    s = "Called from C";
    (*p)( s, strlen(s));  /* Call the Pascal routine */
}

The Pascal main program, ProcParMain.p, which calls the C procedure, proc_c, passing it the address of the Pascal procedure, proc_pas. The C procedure assigns a value to the string s, and calls the procedure whose pointer it just received. Then the Pascal procedure, proc_pas, writes a literal constant and the string it just received.

program ProcParMain;
type
    { Declare a procedure pointer type. }
    proc_ptr = ^procedure(var s: string; i: integer);

{Declare an external C procedure which takes a procedure argument.}

procedure proc_c(p: proc_ptr); external c;

procedure proc_pas(var cstr: string; strlen: integer);
var
    i: integer;
begin
    write('Hello from PROC_PASCAL: ');
    for i := 1 to strlen do
        write(cstr[i])
    writeln;
end; { proc_pas }

begin
    { Call the C routine. }
    proc_c(addr(proc_pas))
end. { ProcParMain }

The commands to compile and execute ProcPar.c and ProcParMain.p

hostname% cc -c ProcPar.c
hostname% pc ProcPar.o ProcParMain.p
hostname% a.out
 Hello from PROC_PASCAL: Called from C


Procedures and Functions as Parameters

It is probably clearer to pass a pointer to a procedure than to pass the procedure name itself. See "Procedure Calls: Pascal-C" on page 121.

A procedure or function passed as an argument is associated with a static link to its lexical parent's activation record. When an outer block procedure or function is passed as an argument, Pascal passes a null pointer in the position normally occupied by the passed routine's static link. So that procedures and functions can be passed to other languages as arguments, the static links for all procedure or function arguments are placed after the end of the conformant array bounds pairs, if any.

Routines in other languages can be passed to Pascal; a dummy argument must be passed in the position normally occupied by the passed routine's static link. If the passed routine is not a Pascal routine, the argument is used only as a placeholder.


Global Variables in C and Pascal

If the types are compatible, a global variable can be shared between C and Pascal.

An example:

The Pascal procedure, GloVar.p

var
    Year: integer;

procedure GloVar;
begin
    Year := 2001
end; { GloVar }

The C main program, GloVarMain.c

#include <stdio.h>

extern void GloVar();

int Year;

int main(void)
{
    Year = 2042;
    GloVar();
    printf( " %d \n", Year ) ;
}

The commands to compile and execute GloVar.p and GloVarMain.c without -xl. With -xl, the Pascal integer must be paired with a C short int and declared public since the default visibility is private.

hostname% pc -c GloVar.p
hostname% cc GloVar.o GloVarMain.c
hostname% a.out
 2001


File-Passing Between Pascal and C

You can pass a file pointer from Pascal to C, then have C do the I/O, as in:

The C procedure, UseFilePtr.c

#include <stdio.h>

void UseFilePtr (FILE *ptr)
{
    { /* Write to the file: */
    fprintf( ptr, "[1] Passing the file descriptor \n") ;
    fprintf( ptr, "[2] and writing information \n") ;
    fprintf( ptr, "[3] to a file \n") ;
}

The Pascal main program, UseFilePtrMain.p

program UseFilePtrMain;
var
    f: text;
    cfile: univ_ptr;

procedure UseFilePtr(cf: univ_ptr); external c;

begin
    rewrite(f, 'myfile.data'); { Make the file. }
    cfile := getfile(f);       { Get a file pointer. }
    UseFilePtr(cfile);         { Call the C function. }
end. { UseFilePtrMain }

The commands to compile and execute UseFilePtc.c and UseFilePtrMain.p

hostname% cc -c UseFilePtr.c
hostname% pc UseFilePtr.o UseFilePtrMain.p
hostname% a.out
hostname% cat myfile.data
[1] Passing the file descriptor
[2] and writing information
[3] to a file


Previous Next Contents Index Doc Set Home