typedef union {
int ui;
char *up;
} u_t;
int foo(char *, ...);
Now if the function is called as:
int i;
char *p;
u_t u;
/* 1 */ foo("Int Now", i);
/* 2 */ foo("Int Now", u.ui); /* wrong */
/* 3 */ foo("Union Int Now", u.ui);
The alignment requirement have changed between invocation of foo 1 and foo 2 in each case.
Case 1:
stack: 4-byte align - return code
4-byte align - argument 1
4-byte align - integer i (argument 2)
Case 2:
stack: 4-byte align - return code
4-byte align - argument 1
8-byte padding
16-byte align - union u (argument 2)
If the code for foo() uses va_arg macro to try to pull off the u-t structure versus an int, alignment requirements may mean you get unexpected data.
The int will only have word alignment required on the stack, while inserting the int into the union will force 16-byte alignment and possibly padding on the stack. Bottom line: you need to know exactly which data type you are getting.
To save some storage to prevent padding _Packed qualifier can be used with structures. However, padding may still occur if there is a pointer in the structure, because of 16-byte alignment requirement of the pointers.
The above question uses va_arg macro, are there other considerations for use of variable arguments?
iSeries has a runtime difference during execution of va_arg. If the variable identified on va_start (the last right-most variable) is a short or char, va_arg does not position it's pointers correctly to pick up the variable length value. The ANSI C standard says that "The last argument must not have register storage class, and it must have a type that is not changed by the translator. (It cannot have an array type, a function type, type float, or any integer type that changes when promoted)." However, other platforms do allow shorts for the last fixed argument. The following example shows code that will cause a runtime exception on iSeries, but works fine on most UNIX platforms:
#include <stdio.h>
#include <stdarg.h>
int vout(short max, ...); /* on iSeries, short causes runtime error
This works O.K. on most UNIX */
int main(void)
{
vout(3, "Sat", "Sun", "Mon");
printf("\n");
vout(5, "Mon", "Tues", "Wed", "Thurs", "Fri");
}
int vout(short max, ...)
{
va_list arg_ptr;
int args = 0;
char *days[7];
va_start(arg_ptr, max);
while(args < max)
{
days[args] = va_arg(arg_ptr, char *);
printf("Day: %s \n", days[args++]);
}
va_end(arg_ptr);
}
The above example may be corrected by using the following on iSeries:
int vout(int max, ...);
Does iSeries has sys_nerr[] array for error message to be used in a C program?
sys_nerr[] is an array that exists on other systems that allows direct access to the strings that represent failure messages associated with errno values. The array is indexed on the errno value. On OS/400, errno message strings are translated along with most other readable text into many different languages. Because of this, the strings exist in a message file whose message ID's are indexed by the errno values. You can use function strerror() to retrieve a message text for an error number. Function strerror() gives the message text only in english, so if your job is using another language or if you want to retrieve message text in other languages you can use system API QMHRTVM. You can download an example which is a replacement for sys_nerr[].
Does iSeries support pointer arithmetic? Is it different from UNIX?
Yes, iSeries supports pointer arithmetic, but depending on the usage, there may be differences from UNIX. iSeries single-level storage assigns 16 megabyte segments on a per process basis. If, through pointer arithmetic, the 16 meg boundary is exceeded an exception occurs. The following example shows code that will cause a runtime exception on iSeries, but works fine on most UNIX platforms:
#include <stdio.h>
#include <string.h>
typedef struct {
char one[10];
char two[10];
int three;
int four;
} SomeStruct;
main()
{
SomeStruct *fp;
SomeStruct Fred[500];
int number1, number2;
number1 = 5000001;
number2 = 5000000;
fp = Fred+number1 - number2;
}
Fred+number1 causes 16 meg segment to be exceeded; resulting pointer value set to NULL.
With the following change, the code runs OK:
fp = Fred+(number1-number2);
(number1-number2) results in 1. Fred+1 is within 16 meg segment.
Is iSeries Big-Endian or Little-Endian machine?
iSeries is a Big-Endian machine.
I have heard that the iSeries uses 16 byte pointers. What are the porting considerations
One consideration is for data alignment, as mentioned above. Another consideration is casting between data types of integer and pointer. The result of integer-pointer casting will vary depending on the operating system release. In V4R2 and earlier releases any integer-pointer casting will result in a corrupted pointer and an runtime error. In V4R3 and later releases, a non-zero 4-byte integer value is preserved when converting to a pointer value and back to an integer. However, converting an integer to a pointer value does not yield a pointer value that can be used to call a function or access storage.
Does the iSeries support 64 bit integer data types?
Yes. In V4R3 and later releases, the iSeries supports the following data types in C and C++:
- long long
- long long int
- signed long long
- signed long long int
- unsigned long long
- unsigned long long int
[BACK]
|