Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C language courses for corporations. He is the author of C Language for Programmers and All On C, and is a member on the ANSI C committee. He also does custom C programming for communications, graphics, and image databases. His address is 4201 University Dr., Suite 102, Durham, NC 27707.
You can fax me your questions at (919) 493-4390. While you hear the answering message, hit the * button on your telephone. Or you can send me e-mail kpugh@dukeac.ac.duke.edu (Internet) or dukeac!kpugh (UUCP).
Q
I was having problems using malloc on a UNIX machine. After allocating some memory with malloc(), I wrote past the end of the allocated memory. The next time I called malloc(), it hung up. I ran the same program on an IBM-PC and it worked fine. What gives?
Jim Campbell
Durham, NCA
Writing beyond (or before) the memory space that is allocated with malloc and related functions can cause some serious problems. These functions allocate a block of memory from the heap (memory space not used for code, data, and stack). They return the address of the memory block. The memory remains allocated until you call free(), passing it the address of the block. This deallocates the block and returns it to the heap. When the program exits, it will free up any allocations you have for which you have not called free(). These functions look like:
#include <stdlib.h> void *malloc(size_requested) size_t size_requested; /* Number of bytes */ void free(pointer) void *pointer; /* Address of memory to free */You request an amount of memory in bytes. The function returns to you an address which points to the first byte of the allocated memory. You can use this memory for any purpose. However, you should not write in the memory preceding or following the allocated block.The operating system and/or the compiler usually use a few bytes of memory adjacent to the allocated block. These bytes, sometimes called the "block header", may come before or after the block. The header keeps such information as the size of the block allocated, and usually some pointers, including one to the next block (i.e., a linked list). If the information in this block header is destroyed, the system cannot allocate a new block or deallocate an old block. Basically, the block looks something like the diagram in Figure 1.
Let's assume that the information is kept after the block, as it appears in the case of your UNIX machine. You probably did something like:
char *pc; pc = malloc(100); ... *(pc + 100) = 0; ... pc1 = malloc(200);and overwrote the first byte in the block header. When you attempted the next allocation, malloc() hung up as you destroyed the block header for the previous block.On a PC, the block header typically appears before the allocated memory. In that case, your program ran okay, as you were simply writing into unallocated memory, which contains no information.
Depending on the order in which you perform allocations and illegal accesses, you could still have problems. For example, let's assume that you performed both allocations first, and then an illegal access:
char *pc; char *pc1; pc = malloc(100); pc1 = malloc(200); *(pc + 100) = 0;Assuming that you do not attempt to allocate blocks later on in the program, this will execute as if no error occurred until the program attempts to exit. When the operating system tries to free the allocated memory, it will become confused due to the erroneous block header information. You will get a dreaded "Memory allocation error system halted" message.With some compilers, malloc() does not call the operating system routine if the request can be satisfied from its own unallocated buffer. In this case, you may not see this allocation error, since the exit operations will simply free all the buffer at once and not the individual pieces.
Q
I am using an array of pointers; each pointer points to a structure; and each structure contains several strings of various lengths.
My array of pointers is declared something like this:
struct { char firstname[MAX_FIRSTNAME+1]; char lastname[MAX_LASTNAME+]; char homephone[MAX_HOMEPHONE+1]; char workphone[MAX_WORKPHONE+1]; char areacode[MAX_AREACODE+1]; char street [MAX_STREET+1]; char city[MAX_CITY+1]; char state[MAX_STATE+1]; char comments[MAX_COMMENTS+1]; } *record[MAX_RECORDS];It follows that I could display each element of the structure that represents the current record as follows:
show_record () { printf("%s\n",record[current-record]->firstname); printf("%s\n",record[current-record]->lastname); printf("%s\n",record[current-record]->homephone); printf("%s\n",record[current-record]->workphone); printf("%s\n",record[current-record]->areacode); printf("%s\n",record[current-record]->street); printf("%s\n",record[current-record]->city); printf("%s\n",record[current-record]->state); printf("%s\n",record[current-record]->comments); }However, it seems that much of the code is unnecessarily duplicated. It would be more efficient if I could create a loop and access a different element of the structure each time through the loop. My show_record() function would then look something like this:
show_record() { int i: for(i = 0; i < NUM_OF_FIELDS; i++) { printf("%s\n",record[current_record]->??? ); } }Where ??? is the part I can't figure out. I could think of ways to do it in assembly language by providing additional data types and accessing them in the loop. Since the elements of a structure are usually word aligned, it's hard to even be sure how many bytes are between each element of the structure.Again, any information you could provide would be greatly appreciated.
Jonathan Wood
Irvine, CAA
Accessing individual members of a structure in a loop is a commonly needed operation. There are several ways that you can do this. Let me change your structure template slightly and add a tag-type. I normally avoid declaring variables when declaring a structure template, eliminating the need to declare those variables when you use the template in another program. A clean structure template is a handy thing to have around because it makes declaring variables of the same structure a breeze.
struct s_record { char firstname[MAX_FIRSTNAME + 1]; ... }; struct s_record *record[MAX_RECORDS];You could use a static variable, which will have constant addresses and set up an array of pointers to those addresses. show_record() might then look like:
static struct s_record print_record; #define NUMBER_FIELDS 9 char *record_field_address[NUMBER_FIELDS] = { &print_record.firstname, &print_record.lastname, ... /* Remainder of the fields */ }; show_record() { int i; /* Copy in the record to be printed */ print_record = *record[current_record]; for (i=0; i < NUMBER_FIELDS; i++) { printf("%s\n", record_field_address[i]); } }One feature in the new ANSI standard, the offsetof() macro, can help you out here. Its syntax is:
#include <stddef.h> offsetof( type, member-name)The type is a structure type and the member-name is a member in the structure. Instead of keeping the address of individual members in an array, you simply keep the offsets from the start of a structure. For example,
#define NUMBER_FIELDS 9 size_t record_offsets[NUMBER_FIELDS] = { offsetof(struct s_record, firstname), offsetof(struct s_record, lastname), ... /* Remainder of the fields */ };Now show_record could look something like:
show_record() { int i; char *pc; pc = (char *) &record[current_record]; for (i=0; i < NUMBER_FIELDS; i++) { printf("%s\n", pc + record_offsets[i]); } }Note that the conversion of the address to a char pointer is necessary. If you simply printed out &record[current_record] + record_offsets[i], you would get the address of something which is record_offsets[i] * sizeof(struct s_record) after the beginning of record.I would suggest that you change the calling sequence of show_record so that it expects a record (or an address of a record). This way, you can print out records that are not part of the array (such as a record that might be used for input purposes).
show_record(record) /* Prints out a record */ struct s_record record; { int i; char *pc; pc = (char *) record; for (i=0; i < NUMBER_FIELDS; i++) { printf("%s\n", pc + record_offsets[i]); } }or
show_record(precord) /* Prints out a record, whose address is passed */ struct s_record *record; { int i; char *pc; pc = (char *) precord; for (i=0; i < NUMBER_FIELDS; i++) { printf("%s\n", pc + record_offsets[i]); } }You might want to be even more organized and create another structure that contains not only the offsets, but also the names of the members, so that you can use the same names everywhere you print the record.
struct s_field { char name[MAX_FIELD_NAME + 1]; size_t offset; } #define NUMBER_FIELDS 9 struct s_field fields[NUMBER_FIELDS] = {"First name", offsetof(struct s_record, firstname)}, {"Last name", offsetof(struct s_record, lastname)}, ... /* Remainder of the fields */ };With this you might have a function like:
show_record_with_field_names(precord) /* Prints out a record, whose address is passed */ struct s_record *record; { int i; char *pc; pc = (char *) precord; for (i=0; i NUMBER_FIELDS; i++) { printf("%-20.20s: %s\n", fields[i].name, pc + fields[i].record_offsets); } }You should note that elements of a structure are not necessarily word aligned. On a PC, they can be byte aligned or word aligned. I prefer packed (i.e., byte alignment) structures, in order to save space, but there is a slight element of speed in using non-packed structures. Note that the sizeof() operator and the offsetof() macro take into account any padding bytes (unused bytes due to alignment). In fact, it is the potential presence of padding bytes that made the ANSI committee eliminate the equality comparison of structures. For example:
func() { static struct s_record record_1; struct s_record record_2; if (record_1 == record_2) ... }The padding bytes in record_1 will be set to 0, since it is a static variable. The padding bytes in record_2 will be garbage, since record_2 is an automatic variable. You could use the fields array shown above to create a structure comparison function, if you required it.Q
How do you make a binary data file that is portable between the MAC and the IBM PC?
Richard Walton
Wellesley, MAA
Porting data files between any two systems presents a problem in that the representation of the numbers varies from computer to computer. A common way of avoiding this problem is to output the data to an text file using fprintf() and to read the data on the other machine using fscanf(). For example, on one machine you would have:
struct_s record { int one_number; double another_number; }; write_record_to_file(data_file, record) FILE *data_file; struct s_record record; { int ret; ret = fprintf(data_file, "%d %lf\n", record.one_number, record.another_number); return ret; }On the other machine, you would use:
read_record_from_file(data_file, precord) FILE *data_file; struct s_record *precord; { int ret; ret = fscanf(data_file, "%d %lf", &(precord-> one_number), &(precord->another_number) ); return ret; }If you do not wish to have the overhead of the conversions done by fprintf() and fscanf(), then you will need to write some specific code. For example, suppose on an IBM you have written out the records as:
write_record_to_file(data_file, record) FILE *data_file; struct s_record record; { int ret; ret = fwrite(&record, sizeof(struct s_record), 1, data_file); return ret; }On the other machine, you will have to rearrange the bit patterns manually:
#define SIZE_BUFFER 8 /* Size of record on other machine */ read_record_to_file(data_file, precord) FILE *data_file; struct s_record *precord; { int ret; char buffer[SIZE_BUFFER]; ret = fread(&buffer, SIZE_BUFFER, 1, data_file); /* Now you need to convert each value individually */ convert_ibm_int_to_mac_int(&buffer[0]°, & (precord->one_number); convert_ibm_double_to_mac_double((&buffer[2], & (precord->another_number); return ret; }Now each of the individual members must be dealt with separately. The double conversion is a bit of a bear. As they say in the teaching business, it is reserved as an exercise for the student. The integer conversion might look like:
convert_ibm_int_to_mac_int(pibm_number,pmac_number) char *pibm_number; char *pmac_number; { /* Reverse the byte order */ *(pmac_number) = *(pibm_number + 1); *(pmac_number + 1) = *(pibm_number); }Note that I have simply shown a return value for each of these file functions. You probably want to be more clever and test the functions so that the return value is consistent among all the functions. For example, the first function might look like:
#define BAD_IO 1 #define GOOD_IO 0 write record_to_file (data_file, record) FILE *data file; struct s_record record; { int ret; int io_ret; ret = fprintf(data_file, "%d %lf\n", record.one_number,record.another_number); if (ret < 1) io_ret = BAD_IO; else io_ret = GOOD_IO; return io_ret; }QI am in the process of implementing hotkey-controlled real-time data acquisition for some laboratory experiments. This is being achieved by taking control of the keyboard interrupt number 0x09. My compiler is Microsoft C v5.1.
The experimental apparatus has three distinct modes of operation: A, B, and C, which are to begin upon the striking of their respective keys from the keyboard. Assume that task A, defined by its function, fA(), is currently executing and that the user now strikes the key to commence task B, similarly defined by its function, fB(), so that fA() stops and fB() starts. My question is this: Can you continually interrupt function i and start function j and expect to escape a stack overflow? How does one handle suspending a function at an arbitrary time with no a priori intention of returning to it (which would free the stack space used by the function). I would imagine that you could do this a few times, but what about suspending A and starting B (or C) an arbitrary number of times? Perhaps setjmp() and longjmp() are the solution. Another serious problem that concerns me is that my method does not seem to admit a way to signal end-of-interrupt to the keyboard handler (or to whatever is listening). Because the directives to begin execution of function A, B, or C are embedded in the new 0x09 interrupt handler, the handler could potentially never finish executing during the experiment. Is there a better implementation which can achieve what I need and still use hotkeys?
Mark S. Petrovic
Stillwater, OKA
You are right in your concern over stack overflow. If you keep calling an interrupt function without clearing up the stack (i.e., with an IRET instruction), you will eventually run out of stack space. An interrupt function that might cause overflow could look like the following, where keyboard_input() is a function that gets the actual keystroke.
control_function() /* This will only be called if a keyboard interrupt */ { int c; /* Get the key that was hit */ c = keyboard_input(); switch(c) { case 'A': function_a(); break; case 'B': function_b(); break; case 'C': function_c(); break; default: function_default(); break; } /***** This function never returns *****/ } function_A() { /* Code to perform function A */ } function_B() { /* Code to perform function B */ } function_C() { /* Code to perform function C */ } function_default() { /* Code to perform default function */ }Everytime you invoke the interrupt, another set of flags and return addresses are pushed onto the stack. set_jmp/longjmp provide an appropriate mechanism for implementing the sort of structure you desire. These two functions allow you to set a place marker in your code (setjmp) and then jump directly back to it from another routine (longjmp). Without setjmp/longjmp to report an error that occurred several levels deep in a program, you could return an error value at every level as you exit the nested calls. With setjmp/longjmp you can instead simply jump back to a central error handler and give it the error value. The function calls are:
#include <setjump.h> int setjmp(environment) jmp_buf environment; /* Will hold the place information */and
void longjmp( environment, return_value) jmp_buf environment; /* Place information from setjmp */ int return_value; /* To be returned to setjmp */setjmp() returns 0 the first time it is invoked. The calling function can test this and ignore any error condition. When longjmp() is called, the next C instruction to be executed is the equivalent of a return from setjmp(). This returns execution to the place marked by the call to setjmp(). One of the parameters to longjmp() is a non-zero value which was setjmp()'s return value. longjmp() cleans up the stack from any nested function calls.The parameter passed to setjmp() is of type jmp_buf. This variable holds information regarding the current position of the stack. You can call setjmp() in many different places and pass it different variables of type jmp_buf. The value passed to longjmp() determines to which of the setjmp() calls it will return. The code below gives an indication of how your problem might be programmed. You would connect this up to the keyboard interrupt.
#include <setjmp.h> #define TRUE 1 #define FALSE 0 control_function() /* This will only be called if a keyboard interrupt */ { int c; /* Character input */ int ret; /* Return value from setjmp() */ jmp_buf environment; /* For the setjmp */ static int init = FALSE; /* First time through flag */ if (init) { /* Stop previous execution */ longjmp(environment, 1); } ret = setjmp(environment); if (ret == 0) { /* This is the return from the initial setup */ init = TRUE; } else { /* This is the return from the longjmp */ ; } /* Get the key that was hit */ c = keyboard_input(); switch (c) { case 'A': function_a(); break; case 'B': function_b(); break; case 'C': function_c(); break; default: function default(); break; } /******* THIS FUNCTION NEVER RETURNS */ }Alternatively, you could avoid using an interrupt by coding each function to periodically check for something on the keyboard stack. This approach does kludge up your lower level functions. However, if the lower level functions have sections of code that should not be interrupted, then this less elegant method may be preferable. Two Microsoft (and some other compiler) functions (not ANSI standard) support this alternate approach. The kbhit() function returns non-zero if there is a key in the buffer. The getch() function returns the character in the buffer, without waiting for a carriage return.
#include <setjmp.h> #define TRUE 1 #define FALSE 0 main() { int c; /* Get the key the first key*/ while (1) { c = getch(); switch (c) { case 'A': function_a(); break; case 'B': function_b(); break; case 'C': function_c(); break; default: function_default(); break; } } /* End while loop */ function_A() { /* Code to perform function A */ /* Inside each loop: */ if (kbhit()) return; } function_B() { /* Code to perform function B */ /* Inside each loop: */ if (kbhit()) return; } function_C() { /* Code to perform function C */ /* Inside each loop: */ if (kbhit()) return; }