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 was a member on the ANSI C committee. He also does custom C programming for communications, graphics, image databases, and hypertext. His address is 4201 University Dr., Suite 102, Durham, NC 27707. You may fax questions for Ken to (919) 489-5239. Ken also receives email at kpugh@dukemvs. ac. duke.edu (Internet).
Q
I work with ANSI-C and I have a general problem. How is it possible to call one function with a variable number/types of arguments inside a second function with variable number/types of arguments, with the same arguments I called the second function?
For example, I have three functions with a variable number and variable type of arguments (myfunction1, myfunction2, myfunction3 shown in Listing 1) . I want to call the functions myfunction1 and myfunction2 from inside myfunction3, with the same arguments I called myfunction3. Is there an easy way to do that?
Thank you very much for your answer.
Willi Fleischer
Moerfelden, GermanyA
The error involves the difference between a variable parameter list and a parameter list containing a value of type va_list. When you pass parameters to a function, the values of those parameters are pushed onto the stack. Usually the first or second parameter to a variable parameter function indicates directly (with a count) or indirectly (as with format specifiers) how many values have been passed.
The printf and fprintf functions expect to see the values on the stack. Each value has an address (its position on the stack). The vfprintf function expects its third parameter will be the address of the first value of a set of parameters on the stack. It then uses this address to retrieve those parameters. To use each parameter, it needs to know the type of the value. That information it gets from the format list specifiers, just like printf and fprintf. The type of the parameter is also used to find the next parameter.
Perhaps it might be instructive to look at typical definitions for these macros. Those in Listing 2 are from Microsoft C.
Variables of type va_list are actually addresses. va_start puts into the first argument the address of the first variable parameter. va_arg increments the first argument (ap) by the sizeof the type which is passed as the second argument (t), as well as yielding a value of that type.
How does vfprintf get to a value on the stack? Since it knows the type of argument (from the format list), it uses the va_arg macro with the appropriate type.
Your my_function1 and my_function2 require a list of values to be passed to them. When you used the va_start macro in my_function3, you retrieved the address of the first variable parameter that was passed to my_function3. You then passed that address to my_function1 and my_function2 and they produced garbage. You need to rewrite my_function1 and my_function2 such that they expect an address (i.e. type va_list). Listing 3 shows how they should look.
Q
I've been programming in C for about two years now and have not seen anything in the literature about my question. The company I work for does quick and dirty production programming. We are in the process of moving from PL/I to C. The programming involves sequential processing of mostly fixed fielded files. In PL/I we read the file into an input buffer and use the string function to load it into a structure. Listing 4 is an instance.
What this in effect does is load the structure recin from the input buffer inn. There is no corresponding C function for loading such a structure and I've been trying to develop something that will do this. What I've come up with is Listing 5.
While the coding for load_struct works (at least in VAX C) I am not convinced this is the best way or even an effective way for doing what I desire. I realize the error checking is non-existent, but is it correct to assume that a structure of character arrays will have contiguous addresses? Is there another method used by more experienced programmers? I need something that I can put into a library that is very generic. How would you tackle this problem?
Tom Crosman
Brooklyn Park, MNA
On some machines individual fields in a structure do have packing bytes between them to align them to word boundaries, giving them contiguous addresses. Records which consist of character only (such as your example) tend not to have packing bytes.
Your method in general is fine. Since you asked for my solution, I've given it in Listing 6. Let me explain the modifications that I've made. The first is that one should usually never declare a variable in the same statement that declares the structure template. Anyone who wishes to use that template gets stuck with that extra variable. Second, I used an array of sizes for each of the fields. These could actually be picked up using the sizeof operator. As another alternative, one could #define the size of each field and use those in the initialization list.
I prefer using an array for the sizes instead of passing them in the parameter list. The declaration can be close to the structure template. Any changes in order or size of the fields can be simply coordinated. Using an array to pass the sizes also simplifies the function, since it is no longer concerned with a variable parameter list.
To show an additional use for the array, I included a print function. It requires most of the same parameters as the load function.
To make the function more generic, I added a nul_terminated flag. Some programmers do not like using the extra character space to hold a terminator in each field. The nul, if present, can signify the end of a value less than the field length. If not present, the field length is the size of the field. Though this requires a slight bit more coding, it does save significant disk space if you store thousands of copies of a structure.
If you were concerned with the packing of the fields in either the input record or the output structure, then you could add an array of field addresses to the calls. If that is necessary, I might suggest not using a generic function and simply hard coding any record conversions required. At some point the work of providing and using a generic interface exceeds the benefit.
Reader Feedback
Coding style
I notice in your find_maximum function that you declare temporary storage for the return value. Is there some reason this is preferred over just returning the value directly? This allows the simplifications in Listing 7.Also in your listings, for the function put_line, it seems to me that putchar would be preferred to printf("%c",...). It would not have to process the format string and convert the character before putting it on stdout.
I enjoy your column and comments.
Edward C. Sarlls, III
Houston, TXI tend to use an automatic variable for the return value from a function. That makes it easier to put a printf statement in the code to print the return value of the function. Or if you are a debugger person, it makes it easy to watch the value.
The disadvantage is a slight decrease in speed. If I made it a register variable (or if the compiler does so automatically), even that should not be a problem.
If you decide to change the return value of the function that uses an expression, and then with the local parameter, you only have to change the expression in one place. If you have debugging output, you need to change it in two.
I must admit that I have had this style for a long long time. In one of my C classes that I taught back in the early '80s, I had a student who insisted that parentheses were required around the expression that follows the return statement. It turns out that all the examples of return statements he had seen had complex expressions around them (as the one in your second example). Psychologically the parenthesis were needed to surround the expression and "make it one." That points up the other coding style that I use quite often (see Listing 8) . I state in All on C that having a single return statement with gotos is preferable to having multiple return statements. If I want to trace the return value, with multiple return statements, I have to do something like the code shown in Listing 9.
If I were using a debugger, there would be several breakpoints to set (assuming the function was long enough that I simply didn't single step through it).
The difference in the complexity of code between multiple returns and multiple gotos (to the end of a function) does not seem to be a big issue, at least to me.
I may get hundreds of letters regarding this seemingly idiosyncratic style of programming (or maybe with an adjective using only the first two syllables). Before that occurs, I wish to make a few caveats regarding it. First, I try to program the logic not to require multiple returns/gotos. Hence the original listing has neither in it. Second, there should be a single label at the end of the function with a standard name (say end), that is the label for the goto. If that is the case, then ret = xxx; goto end takes on the same meaning as return xxx;.(KP)
Q
I just read your question and answer article entitled "Using typedef" in the December 1991 issue of The C Users Journal. I am writing in reference to the question regarding the writing of a function returning the lowest and highest integer out of the three integers passed. Listing 10 is my attempt at answering this question.
With all due respect, I think this approach is more eloquent and less convoluted than the methods offered in Listing 2 (December 1991 issue) and Listing 3 on page 120 (December 1991 issue) and in Listing 4 on page 122 (December 1991 issue), whether or not you choose to use the conditional shorthand.
I am also puzzled by the fact that you were a member of the ANSI C committee and you didn't use prototypes in your code. Did you have a special reason for not prototyping your examples?
S.J. Stern
Bothell, WAA
Eloquence is in the eye of the beholder. Some people might consider the second part of Listing 7 (from Mr. Sarlls letter previously in this column) the most eloquent. I don't particularly prefer any of my Listing 2, Listing 3, or Listing 4. My favorite is Listing 5 (from the December 1991 issue, reproduced here as Listing 11) . It computes the maximum for any number of input parameters. The logic in your sample matches the logic in that listing.
I guess I could have arranged the logic in my function as shown in Listing 12. It matches your logic and has fewer lines than my previous listing. Unless I am going to call a routine a few thousand times, I tend to stick with whatever I come up with first that works. Also, I usually avoid using the conditional expression operator, for the reasons explained in last month's column.
As far as prototypes, I usually do not include them unless they are required by ANSI C or they are essential to the answer. The information contained in prototypes is mostly redundant. The case of the variable parameter function (as in Listing 10) requires one. When I do need prototypes (e.g. for C++), I let the compiler or PC-Lint generate a file of them.
Thank you for your feedback.(KP)
typedefs and lint
In my column a few months ago, I answered a question regarding typedefs. At the recent C-Forum sponsored by the Wang Institute of Boston University, I bumped into Jim Gimpel, the author of PC-Lint. He told me that the latest version of PC-Lint has an option for strong typing. This means that it can report errors in the use of typedefed variables which the compiler would just ignore. For example, given the code in Listing 13, the assignment of
short_distance = high_speed;is accepted by the compiler without question since both variables are declared to be type double, once the typedefs are resolved into the underlying types. However PC-Lint can yield an error message, if strong typing is turned on.As another example, a function declared as shown in Listing 14 will give a PC-Lint error if it is passed the code in Listing 15.
There are many options available for strong typing, which are all described in the manual. In addition, you can declare that particular arrays can only be indexed by variables of particular types. For example, arrays of type HISTOGRAM can only be indexed by variables of type INDEX. This would look something like Listing 16.
The not_good_ index reference to my_array would yield an output error.
For those who are considering changing to C++ purely for its type-checking abilities, I suggest you look at PC-Lint as an alternative.(KP)