Journal Articles
Browse in : |
All
> Journals
> CVu
> 123
(22)
All > Topics > Programming (877) Any of these categories - All of these categories |
Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.
Title: Questions and Answers
Author: Administrator
Date: 07 May 2000 13:15:36 +01:00 or Sun, 07 May 2000 13:15:36 +01:00
Summary:
Body:
Before I get on with this issue's collection of questions and answers I want to appeal for two things. First, I can do with a steady supply of questions. Of course I can mine Internet newsgroups for extras but many of you must come across puzzling questions while you are programming. Even if you think you have the answer, please donate the question. If you want I will hide your name, but send the question. Note that even very simple questions can have layers of problem hidden within (see the answers to question 2 below)
My second request is for someone else to take over collating this column. It is a simple well-defined task that should take a couple of hours per issue. Yes, I can do it, but the more that such tasks can be distributed the easier it will be to replace me as editor. Or if you like to think of it in a different light, the less likely I will be just to stop.
By the way, please consider extra answers to earlier questions. This column is in the section headed Dialogue because it is intended to be just that; a dialogue between readers moderated by a columnist.
I am trying to output a double to a string using sprintf. The code I am using right now is
sprintf(#, "%f ",d);
where d is a value of type double. What this does is automatically output to six decimal places. So if d = 1.0 buf becomes '1.000000.' A more relevant example would be d = 3.142 resulting in generating '3.142000' in buf. Is it possible to clip all the trailing zeroes from the output of a double?
The formatted output functions of <stdio.h> use the conversions characters g and G to output floating point numbers without trailing zeroes. So %f in sprintf(buf, "%f ",d); should be replaced with %g (or alternatively %G) yielding sprintf(buf, "%g ",d);.
The table on page 244 of The C Programming Language by Brian W. Kernighan and Denis M. Ritchie, 2nd edition, Prentice Hall Software Series, ISBN 0-13-11-0362-8, is very handy for deciding which conversion characters to use if you are unsure. There is a similar table for formatted input from <stdio.h> functions on page 246.
Answer from Colin Hersom <colin@hedgehog.cix.co.uk>
Both %f and %e formats to the printf family of functions have a default precision of 6. This means that unless you override this there will be six digits printed after the decimal point. The %g format will suppress trailing zeros, but will use the exponent format when the number is too small (less than 0.001) and there is no way to prevent this.
[But not everyone agrees with that last statement]
Answer from Graham Patterson <grahamp@econ.Berkeley.EDU>
There is a little used format specifier that can be used to control trailing zero production. The g or G specifier will only output non-zero characters after the decimal point, and will usually not include the decimal point if the value has no fractional part. The # flag can be used to include the decimal point, but this usually results in trailing zeros again. The exact behaviour depends on the field width and precision specified.
To write the value into a buffer using sprintf(), I would suggest a format of %-1.9G, where the '-' signifies left justification, the '1' is a minimum field width that will be expanded automatically to the minimum required for the value, '9' is the precision for your significant decimal digits to be. The value you use will depend on your needs. The G specifier will degrade to the E+00 form of exponent if the value exceeds the format width. The g specifier can be used if you prefer lowercase exponents. Leading zero and space can also be applied. The trailzero.c file demonstrates some of the variations.
Most people writing mathematical or scientific software will recognise the need to report values to a suitable precision, either in terms of decimal places (where the magnitude of the result is generally known and the error in the calculation governs the reporting precision), or as significant digits (where the magnitude is variable). Essentially it's the difference between a zero as a space filler and as a quantity.
/* Simple demonstration of floating point formatting using the %G specifier. Graham Patterson April 2000 Trailing zero truncation */ #include <stdio.h> #include <math.h> enum {fieldlength = 15}; int main(void) { const double test_value = 0.03141590000; char *formats[] = {"%#1.9G", "%#-1.9G", "%-1.9G", "%-9.9G", NULL}; char **ptr = formats; long places; while(*ptr) { for(places = 1000000000L; places > 0L; places /= 10L) { int magnitude = (int)log10(fabs(test_value*places)); printf("[%s]\t[", *ptr); printf(*ptr, test_value * places); printf("]\tMag. %d", magnitude); printf("\t[%%-1.%dG]\t[%-1.*G]", magnitude + 1, magnitude + 1, test_value * places); printf("\n"); } ptr++; } { /* create new scope for variable */ double test = 0.1; int i = 0; while(test > 0.0) { i++; test /= 10.0; } printf("%d places\n", i); } }
Why doesn't the following change the pointer it is passed?
void findspace(int * points_at, char * text) { int i; for (i=0; i<strlen(text); i++) if (text[i] = ' ') points_at = text+i; return; }
The pass-by-reference allows a programmer to pass a variable to a function that in turn is able to modify the variable. Within the function, the passed parameter is de-referenced using the * operator before it can be changed. Thus, a good check for a function that uses pass-by-reference is to see if the parameter is being de-referenced. In your example, points_at is not de-referenced, so the variable being passed by reference is not being modified. To modify the points_at parameter, the code should be rewritten as follows:
void findspace(int **points_at, char *text) { int i; for (i=0; i<strlen(text); i++) if (text[i] = ' ') *points_at = text+i; return; }
I suggest, however, that you rewrite the function as follows to reduce the risk of receiving a pointer which points past the end of the string (in case the '\0' character was inadvertently omitted)?
#include <ctype.h> char *findspace(int buffersize, char *text) { int i; for (i=0; i<buffersize && text[i] != '\0'; i++) if (isspace(text[i]) return (text[i+1]); return((char *)NULL); }
Alternatively, you may find it useful to research the strchr() function which performs the same exercise as that in your question.
Answer from Colin Hersom <colin@hedgehog.cix.co.uk>:
C always passes arguments by value and changing its value in a function has no effect on the caller. If you want to change the value that is being passed in, then you must take the address of the value and pass that in (this is than a pointer argument). Dereferencing that pointer in the function reveals the value and allows you to change it. So if the function is declared:
void f(int *ap)
you can get the integer value like:
int i = *ap;
or change it by:
*ap = i+1;
Looking at the types in an expression usually helps to determine what are sensible operations and what are not. In the findspace function, points_at is a pointer to an integer, and text is a pointer to a char. This means that text+i (i an integer) is also a pointer to a char, so assigning it to points_at is not a sensible operation. A decent compiler ought to provide a warning about this. In order to change the value that points_at points to, you have to defererence it:
*points_at = <something>
Now since points_at is an int*, *points_at is an int, therefore <something> must be an int also. The only other int you have in the function is the variable i, so unless you are really perverse, <something> is likely to be i. This would mean that the definition of findspace is that it returns with the index of the first/last space in text in the address pointed to by first parameter. You need to find out what this value ought to be when there is no space in text - currently it does not get changed at all.
I think this question raises several points that should be addressed. As we will see this simple four line function is riddled with bugs.
The first is the explicit question. The question arises from confusion as to what a parameter is, and a pointer parameter in particular. Many people find this difficult. The only distinction between a parameter and a local variable where it is initialised. A parameter is initialised with a value provided by the calling function, other local variables have to be initialised from within the scope of the block in which they are defined.
All local variables die with their current values when you exit the block in which they were defined. In the case of a parameter that is effectively at the point of return from the function. This means that whatever you do to a parameter, even if it is a pointer, is purely local to the function. Look at your code and you will see that you make changes to points_at, this is useless. What you need is to be able to use the address in a pointer to get at the storage of an object that has existence outside the local scope. You will know you are doing this because you will use the * operator to dereference the pointer to give you the object addressed. In your case you want to get storage for a char* so the parameter must be initialised with the address of a char*. Note that, not a char* but the address of one. This tells us that you will need to have a char** parameter which will have to be dereferenced to get the required storage.
Things like this can be profoundly confusing even to fairly experienced programmers. One mechanism that can help is to use a typedef to name the type you are playing with. We could rewrite the code you wrote as:
typedef char * mystring_ptr; void findspace(mystring_ptr points_at, char * text) { int i; for (i=0; i<strlen(text); i++) if (text[i] = ' ') points_at = text+i; return; }
In making that change I have not only highlighted the problem by making it apparent that points_at is entirely local but I have also fixed another bug that is lurking in your code. text+i is a char* which is being silently converted to a int *. Even in C, I think your compiler should be giving you a very loud warning. The conversion may change the value from the that of the char* because a pointer to char can be significantly different to a pointer to int. For example some 32-bit hardware does not provide byte addressing and a char* consists of an int address together with an offset. When you convert a char* to an int* on such a machine you lose the offset information.
The fully corrected function is:
void findspace(mystring_ptr * points_at, char * text) { int i; for (i=0; i<strlen(text); i++) if (text[i] = ' ') *points_at = text+i; return; }
Note those two extra *s. They make all the difference. Before we go on, please study the above until you are sure you understand it.
Now to the second problem; why are you writing this function? More precisely, why are you using a write-back parameter (one where you change the state of the object pointed to by a parameter) rather than a return value. And why are you returning the address of the last space in the string pointed to by text? And why is text a char * and not a char const *? I cannot answer these questions but unless a code reviewer asks them the code review has not been done correctly. I think your code should be:
char * findspace(char const * text) { int i; for (i=0; i<strlen(text); i++) if (text[i] = ' ') return (text + i); return 0; }
Note that this version will handle a read-only string, and one without a space in it. But it will not compile in C++ because it is not const correct. That is another story.
My final question is why you did not use strchr() from the Standard C Library? Go and look it up if you are not already familiar with it.
Can someone explain what on Earth Koenig lookup is? I used to think I understood overloading in C++ but this seems to be no longer true.
Koenig lookup functionality is explained in sections 8.2.6 and 11.2.4 of The C++ Programming Language, 3rd Edition . The basic idea is that a lookup for a function (or operator) is done within the namespace of the arguments if the function cannot be found within the context of its use, then the namespace(s) of the arguments are checked.
The algorithm for looking up functions based on argument namespaces was generated by Andrew Koenig. The intent is that programmers can take advantage of multiple, diverse namespaces without having to constantly, explicitly state the namespace. Unfortunately, I stumbled across a large number of compilers that do not support Koenig lookup despite its being part of the standard.
I am not going to write very much about this because while the fundamental idea is fine specifying it precisely is extremely difficult. If you doubt that you should know that there is at least one defect report registered on this subject against the C++ Standard. In other words the C++ Standards Committees accept that despite all their years of effort they failed to precisely specify Koenig lookup so that it met their intent.
The fundamental idea is that the overload set constructed should include functions that can be found in the namespaces of the argument types used in a function call. For example:
void fn(){ mytype mt; yourtype yt; gn(mt, yt); }
When looking for a gn() not only should the current scope be searched but so should namespaces associated with mytype and yourtype. When it works it is great but many current compilers only partially support it. In addition more work has to be done tying down the exact specification.
Question 1 from Colin Hersom <colin@hedgehog.cix.co.uk>
Looking at the fused problem (see the Q&A column in C Vu 12.2), I wondered about how I might try to patch such a problem if it occurred. Assuming that the reference is necessary in the Fused class, the solution has to be in the usage of the class. This means that the value passed into the constructor must not be a temporary, and the simplest way to change a temporary to something permanent is to put the word static before the declaration:
Fused *foo() { static int const anInt = 4; return new Fused(anInt); }
Should be OK now. Strangely, it isn't, at least not using GCC. If I remove the const keyword, all is OK, but with const present then the compiler seems to generate a temporary whether or not anInt has function, file, namespace or global scope.
Is this correct or is GCC in error?
Question 2 from: Claus Wagner <Claus.Wagner@ascom.ch>
I need some help in converting legacy C code (#define macros) into C++ template classes/functions. The situation is as follows:
The legacy code has a lot of #define's like the following:
#define Macro16(X, OPER, Y) ( (*(u16 *)(X))\ OPER (*(u16*)(Y)) ).
The macro is called as follows:
typedef unsigned char u8; typedef unsigned short u16; u16 var1; u8 var2[2]; Macro16(&var1, =, var2);
This copies 2 bytes from var2 to var1. But there may be also other self-defined data types, such as structs containing a u8 var3[2] array etc. Sometimes the u16 is on the left-hand side, sometimes on the right-hand side. Moreover, there are also 32-bit variants for all these combinations (i.e. 32-bit integers and 4-byte arrays, structures containing 4-byte arrays...)
The problem I have is that the macro can also be called with different operators, e.g., Macro16(&var1, ==, var2). This is then a check for equality. I found a total of 7 operators, this macro can be called with: =, ==, !=, &=, |=, <, and >.
The problem is that this worked fine on a 16-bit architecture, but on a 32-bit one it crashes. The reason is that an array of four characters is not necessarily 32-bit aligned as the 32bit integer is. During the casting and assignment the system segfaults.
[At this point Claus posted his own temporary fix. I have left that out because I think that a fresh start might be more helpful. What ideas have you got for solving this problem. Actually I think I full treatment might be an entire article, in which case it should be submitted to John Merrells <overload@accu.org> as it is clearly more appropriate to his readership. ]Can someone please explain in detail what the -> (arrow symbol) does in C++
I recently saw the following prototype for a function:
matrix * multiply(matrix * restrict m1, matrix * restrict m2);
I have looked in all my reference books for both C and C++ and none of them mention restrict as a keyword. Can someone tell me whether this is some special dialect of C or C++ and if so what does restrict do?
I think this is a good question and not just for inexperienced programmers. I am not going to give a full answer here (else there would be little point in publishing the question here) but I will give a partial answer. restrict is one of a number of new keywords that were introduced into C in the latest Standard (often called C99). I leave it to readers to write about what it is for, what it does and why the UK C Standards panel insisted on modifications before accepting it as part of the new standard for C.When I learnt C I was taught that there was no purpose in qualifying a value with const because values are always immutable. Now I understand that there can be some benefit from declaring a parameter as const because that allows the compiler to check that you do not change it. That is a purely local (to the definition) issue. However I have recently seen C++ member functions declare their return types as const. Why?
Notes:
More fields may be available via dynamicdata ..