Friday afternoon C trivia:

Since there are so many experienced C programmers on this group. How about a little test? Just for fun...

I have four pointer definitions:

typdef void * LPVOID;

  1. const void * ptr = ...
  2. const LPVOID ptr = ...
  3. void * const ptr = ...
  4. LPVOID const ptr = ...

Questions:

  1. Which pointers point to constant memory. E.g. you can't change the memory behind the pointer?

  1. Which pointers are constant. E.g. you can't assign them a different value?

Have fun! I already had my share of embarrassed workmate faces :-)

Cheers, Nils Pipenbrinck

Reply to
Nils
Loading thread data ...

It might be a bit of fun doing such tests, but anyone who actually writes that sort of nonsense in real code should be embarrassed. If it is not immediately obvious how the consts and pointers fit together, the types should be broken down to typedefs until it /is/ obvious.

Reply to
David Brown

It's no big deal. With `const ptr`, ptr cannot be changed. With `const

*ptr`, *ptr cannot be changed. End of story.

Mel.

Reply to
Mel

Like anyone who thinks LPVOID is clearer than void *

Reply to
mac

Correct. But it is still possible to write things in confusing ways, such as by putting "const" in different places, changing where you put spaces around the "*", or for the real obfustication experts, mixing pointer and non-pointer declarations in the same statement.

My point is just that code should be clear. If people reading the code find it hard to figure out where the "const" applies, the code is badly written.

Note that this depends highly on your expected audience. In some development groups the meaning of something like "volatile uint8_t * const p" is obvious, in others it is not. In the OP's case, his colleagues had trouble with some of these constructs - therefore using them in his work would be bad programming.

Reply to
David Brown

That depends on the circumstances, but if you mean that gratuitous typedefs make the code less clear, then I agree.

Reply to
David Brown

  1. Using pointers to void is malpractice.
  2. LPVOID is long pointer, which is not the same thing as void *.

Vladimir Vassilevsky DSP and Mixed Signal Design Consultant

formatting link

Reply to
Vladimir Vassilevsky

Nah - my colleagues know where to put the const-keyword.. Most of the time at least. And keep in mind that this is not a real world example.

It just happend that I stumbled upon the little difference that the typedef makes, and I'm still fascinated that even the experienced among us didn't got it right...

Most of us have 20 years of experience and didn't knew this.

typedef void * LPVOID;

void testcase (void) { char something[10];

// two pointers.. Seem to do the same thing: const void * ptr1 = 0; const LPVOID ptr2 = 0;

// but they behave different: ptr1 = something; // compiles fine ptr2 = something; // gives compiler error.. }

Reply to
Nils

Possibly. But some of us have to read code that others have written.

Which can add to the confusion if you spread the typedefs over several header files.

--
www.wescottdesign.com
Reply to
Tim Wescott

Agreed. It is far too easy for people reading code ex post facto to "read what they want to read" (i.e., "see what they hope to see") and not what the code actually *says*. Writing good code means writing code that is easily understood and so needless careless mistakes are minimized.

For example, I parenthesize arithmetic expressions involving anything other than +- and */ as it is too easy for people to misremember rules of precedence (and, in some languages, the rules are quite different than what you would expect! e.g., how is "x = 5 < 2" evaluated?)

I *dis*agree, here. How you write should be consistent as you can't *know* your ultimate audience. Code that you wrote years ago that was "bleeding edge" may end up being maintained by "new hires" today.

Reply to
D Yuniskis

See above. It might claim to be "long", but you have to look up the declaration to see that it isn't.

Reminds me of a Pascal textbook that included exapmples with const NINE = 9; Presumably this allows the program maintainer to easily change the value of NINE to something else, such as 7.

Since this is comp.arch.embedded, it's excusable that we're still dealing in "long" and "short" pointers. Is that the same as far and near?

Reply to
mac

The correct answer is, none of them. You can't assign a void type. You need to cast the pointer to something else, and then it's not the same pointer, is it?

Had you done the same thing with char however, the answer is based on a simple principle; const applies to the thing to its immediate left, unless it's at the start, when it applies to the leftmost thing.

So case 1 disallows changing the underlying memory, 2,3,4 allow it.

Cases 2,3,4.

Clifford Heath.

Reply to
Clifford Heath

Hmm, it should be, as you declare ptr2 to be constant.

Note: const void * ptr1 is equivalent to void const * ptr1 telling the compiler that what ptr1 points to is constant, not ptr1 itself. Otherwise const x tells the compiler that x of is constant, so no matter what is.

const void * const ptr1; is the equivalent to const LPVOID ptr1;

BTW: In (embedded) compilers one needs often this:

const char * const table[] = { "Hello","World" }; to be sure both the strings as well as the table are in ROM.

writing const char * table[] ... will place the table into .data section (=> RAM).

--
42Bastian
Do not email to bastian42@yahoo.com, it's a spam-only account :-)
 Click to see the full signature
Reply to
42Bastian Schick

Pointers to void are a generic pointer, used to point to data of any type for moving, sending as message payload, etc. The standard C library uses them to good effect and so do I.

--
Thad
Reply to
Thad Smith

So the information about type is lost. No good. Seen many problems becase somebody liked the style of programming with void pointers.

C is outdated dialect of C++, which sometimes has to be used for the resource constrained systems.

Vladimir Vassilevsky DSP and Mixed Signal Design Consultant

formatting link

Reply to
Vladimir Vassilevsky

Certainly if you are passing a reference to a structure and the called function only deals with that structure, the argument type should be a pointer to the specific structure.

If you have a function to send a message of arbitrary content, it makes sense to me to pass a generic pointer and size. You could instead write a wrapper function for each type of entity to be sent or even a standalone separate function for sending each data type, but I don't see it as sufficient value, as long as it is reasonably easy to use a generic function correctly. I use malloc(), memcpy(), memset(), and memcpy(), for example, without problems.

What experiences have you had to influence your design? Can you give an example of problems seen or better design through typed pointers where others would have used generic pointers?

Well, most of my C programming is for resource constrained systems.

--
Thad
Reply to
Thad Smith

You /do/ know something about your audience - they will be people at least roughly qualified to work on the code in question. C code written for the Win32 api would be gobbledegook to a Linux kernel programmer, and vice versa, because they are written in different styles and with different standard types, macros, common functions, naming conventions, etc.

But you are right that you shouldn't make extra and unnecessary assumptions about the reader's knowledge and experience.

Reply to
David Brown

are safer and more efficient.

  1. There was a large project which included PC part, embedded DSP part and a library of common functions shared between PC and DSP. Different toolsets, different build chains. Several programmers worked on the project; one of them decided to save effort by using void pointers for passing data. Cleaning the bugs which appeared because of that was a nasty job.
  2. A function like memcpy() can be more efficient if it knows what is the data, how it is aligned, if the size is a multiple of dwords, where is useful data and where is padding, etc.
  3. "Shallow" copy problem. When manipulating objects, I generally don't have to know what is inside and if they are referencing to something. Therefore I avoid using "generic" functions and use methods specific to those objects instead.

The development time and the engineering costs are limited also.

Vladimir Vassilevsky DSP and Mixed Signal Design Consultant

formatting link

Reply to
Vladimir Vassilevsky

We were talking about resource-constrained systems, weren't we? Templates are not Generics, thus templatizing code blows up your binary size, and complicates linking a lot. If you implement something like Windows' FindWindow as a function template in a header file, can you tell me where the object code will end up?

The C++ solution would be to send a callback object with a virtual function, of course. On architectures that support it.

This is one error source amongst many. Keeping code size low in heavily templatized code is an even nastier job. Chasing void pointers may be your anecdote. Being the one who developed our bootloader, chasing bloat is mine.

Apples vs. Oranges. Of course we don't use memcpy if a single assignment also does. On the other hand, only few compilers optimize a for (i = 0; i < N; ++i) pDestByte[i] = pSrcByte[i]; into "check if it's aligned, if it isn't, make it aligned, then use word-wise copy". 'memcpy' implementations often do.

But even then, whereas it is at least possible to implement a typed 'memcpy', I definitely don't want to implement typed 'read' and 'write' in my filesystem just to get rid of void pointers.

Unless we're talking about huge multi-megabyte systems with gigantic user interfaces, I wouldn't say C++ saves so much engineering costs. After all, you can not just take the next student from around the corner who uses all the neat tricks he read in chapter 1 of the Stroustrup book. You need people who understand what the compiler will make out of their giant abstract virtual template monster (newbie question #1: why cannot I use a member function as interrupt handler?).

Plain C just makes it harder to produce monsters of *that* particular type. Sure, it still allows monsters of different type ("huge while() loop and fifty gazillion boolean flags"), but C++ isn't immune to that either.

Stefan

Reply to
Stefan Reuther

That's only true if you work in a particular niche. I frequently move code from desktop platforms to embedded (or vice versa). The types of people who code in each are vastly different (IME). It's always frustrating, for example, watching someone come fresh from school or The PC World and think they can just start writing code for an embedded system on Day One -- the disbelief they have over how things just don't work the same, etc.

Or, vice versa.

E.g., somoene coming from the embedded world sees no difference in:

for (i = 0; i < MAX_I; i++) for (j = 0; j < MAX_J; j++) x[i][j] = foo...;

and:

for (j = 0; j < MAX_J; j++) for (i = 0; i < MAX_I; i++) x[i][j] = foo...;

but someone used to working in a desktop world *knows* (or *should* know) the difference!

Coming from a hardware & assembly language background, I instinctively use pointers for *everything*. When newcomers look at my code and see me invoking functions

*through* pointers and having those functions *return* pointers to other functions, etc. can get upset as it's not something they are used to doing. Dispatching through tables of function pointers, etc.

Commentary can go a long way to helping -- assuming you don't *waste* comments (which tends to result in "comment rot" as those useless comments tend not to get maintained).

I've also found that people often *think* they know things that they actually *don't* and this misunderstanding can be *reinforced* or *corrected* depending a lot on "style". E.g., chosing a do-while() instead of a while() loop to reinforce the fact that you *must* pass through the loop at least once.

Well, you *can* -- but, be prepared for them to break your code! :>

I find that consistency helps people (including myself when I have to revisit some "old code") focus on what is actually happening instead of getting caught up in syntax-related issues. Here, cut and paste can be a

*real* win if you deliberately exploit the similarity in code, naming and commentary: "Oh, I already saw something *just* like this a page earlier so I know what it is doing..." (of course, you also have to make any changes very deliberate to alert the reader to the fact that this *isn't* the same as what he recently encountered)
Reply to
D Yuniskis

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.