-
Hi,
I don't understand difference between preprocessor and macros in cprogramming language after reading material on internet
I have written following code and I think value is preprocessor directive. compiler sustitute value 1 with text
#include<stdio.h>
#define value 1
#define add(y) y + 1
int main ()
{
int x = value;
int y = 5;
printf("x = %d \n", x);
printf("y = %d ", add(y));
return 0;
}
x = 1
y = 6
What is add(y) y + 1 ? Is it macros ?
how do you identify what are the preprocessor and macros in c program ?
-
It's "preprocessor macro", a macro handled by the preprocessor. They are not different things.
-
The short answer is that there is no easy way to tell if a symbol refers to a macro or not.
You basically have to do what the C-preprocessor does... read all of the code that occurs before the use of the symbol and see if there are any #defines for that symbol.
In a lot of cases authors will use all upper case for macro names -- e.g. FOO(...) refers to a macro named FOO and not a function. A lot of constants in the C standard library are implemented this way, e.g. EOF, the SEEK_* constants, ... On the other hand, there are also functions (or things you would expect to be a function) which are implemented as macros on some systems -- e.g. getc().
Macros are a relatively simple mechanism which allowed early C programmers to work around the limitations of the compiler. Nowadays in many instances there are much better ways to accomplish what macros were used for in the past. If you google "why macros are evil" you'll find discussions like this:
https://stackoverflow.com/a/14041847/866915
-
Unless someone used a preprocessor wrong,(1) for practical purposes it doesn’t matter if something is a macro expansion or an actual function call. So you may continue making a call without ever knowing if it’s really a call or text substitution. You should not assume either anyway, because it may change with another release of some library.
One thing you must understand and remember, when writing your own macros, is that the preprocessor is dumb text substitution. It has no idea about C syntax: it just replaces sequences of characters in code.
As for satisfying your curiosity and help with debugging, both gcc and clang have the -E option. It stops the compilation process after the preprocessing stage and spits out a preprocessed file without doing anything else. The output is usually messy, as it’s not meant to be regularly used by humans, but debugging at this level happens to be ugly.
(1) Which may include using it at all. ;)
-
both gcc and clang have the -E option.
I am using windows 10 operating system. What is command for winows
-
#define add(y) y + 1
Be careful writing function-like macros, and don't do it as in the example above.
Macros are pure text expansion, which means that an unprotected argument can interact with operator precedence rules - eg if someone using your macro writes
y = 6;
x = add(y)*4;
They might be quite surprised that x ends up as 10, not 28 because it expanded to
y = 6;
x = y + 1 * 4;
So always put parentheses around macro arguments - i.e write
#define add(y) ((y) + 1)
There are, however lots more traps for the unwary when writing macros.
-
there is no easy way to tell if a symbol refers to a macro or not.
If a symbol is a macro, it will "disappear" in the -E output that people have been talking about.
-
I am using windows 10 operating system. What is command for winows
There is no “command for Windows”. Compilers are not an inherent part of operating systems. The options you need depend on the compiler you use. If you are using gcc or clang under Windows, it’s -E, exacly the same as elsewhere. If it’s some other compiler, consult the documentation for it. Or tell us what is the compiler — perhaps someone will able to give some hints.
-
If you create it with #define then it is a preprocessor macro. Simple as that.
Most decent C IDEs will also support coloring macros a different color (part of syntax highlighting)
-
both gcc and clang have the -E option.
I am using windows 10 operating system.
I'm so sorry.
What is command for winows
If you use gcc or clang, then the same as anywhere else.
I don't know if there are versions of gcc and clang that work in the DOS command line -- probably -- but you can certainly use them if you install WSL or cygwin.
I believe Mingw-w64 is a port of gcc that can create Windows applications. I'm not sure whether it runs under DOS or in cygwin etc.
-
If you create it with #define then it is a preprocessor macro. Simple as that.
Do you seriously think they ask about detecting macros they themselves has written?
-
#define add(y) y + 1
Be careful writing function-like macros, and don't do it as in the example above.
Macros are pure text expansion, which means that an unprotected argument can interact with operator precedence rules - eg if someone using your macro writes
y = 6;
x = add(y)*4;
They might be quite surprised that x ends up as 10, not 28 because it expanded to
y = 6;
x = y + 1 * 4;
So always put parentheses around macro arguments - i.e write
#define add(y) ((y) + 1)
There are, however lots more traps for the unwary when writing macros.
Grumpydoc pointed out a very important point with macros in his reply above. There is another related one caused by the macro string replacement:
For a simple example, let say we have a macro to triple the value.
#define triple(y) ((y) + (y) + (y))
Now think about if you do this, this works fine:
++x; // first increment the count
z = triple(x); // set z = ((x) + (x) + (x))
But if you increment the count as macro's parameter, x is incremented three times:
z = triple(++x) // this set z = ((++x) + (++x) + (++x))
So, remember unlike a function call, macro is a text string replacement. If in your macro you use the argument multiple times and that argument changes itself, the change will be done multiple times also.
-
@brucehoult,
There's also TDM-GCC (https://jmeubank.github.io/tdm-gcc/) that runs GCC natively on Windows, and lets you build native Windows applications using the MinGW runtimes.
-
But if you increment the count as macro's parameter, x is incremented three times:
z = triple(++x) // this set z = ((++x) + (++x) + (++x))
So, remember unlike a function call, macro is a text string replacement. If in your macro you use the argument multiple times and that argument changes itself, the change will be done multiple times also.
Absolutely :-+
Another problem with the example above is that you have no idea what z would end up as - there is no sequence point between the three increments of x so you don't know whether x or x+1 will be added together.
This problem with double evaluation shows up in the "naïve" implementation of "MAX" as a macro - if you write
#define MAX(a, b) ((a) > (b) ? (a) : (b))
You might well run into this.
gcc has some extensions which can help avoid this so you can write
#define MAX(a,b) \
({ \
typeof(a) _a = (a); \
typeof(b) _b = (b); \
(_a > _b) ? (_a) : (_b); \
})
but I don't think that's portable and it is best to avoid anything which is not idempotent as a macro argument.
-
But if you increment the count as macro's parameter, x is incremented three times:
z = triple(++x) // this set z = ((++x) + (++x) + (++x))
Worse: it invokes the dreaded Nasal Demons (https://en.wikipedia.org/wiki/Undefined_behaviour) of Undefined Behaviour. You cannot apply pre- or post-increment or -decrement more than once to a variable in a single expression.
Many C compilers do usually do what you expect without surprises – except when you enable higher optimizations, and the results start to differ!
Thus, it is better to avoid that sort of a thing, and also enable compiler warnings (so the compiler will tell you if you accidentally do that). Even if a simple test shows that a particular version of a particular compiler seems to handle it right.
-
gcc has some extensions which can help avoid this so you can write
#define MAX(a,b) \
({ \
typeof(a) _a = (a); \
typeof(b) _b = (b); \
(_a > _b) ? (_a) : (_b); \
})
but I don't think that's portable and it is best to avoid anything which is not idempotent as a macro argument.
Just confirmed that works in Clang as well as gcc.
How much more portability do you need? :-)
gcc supports all the old historical stuff, and LLVM supports all the new stuff first and best.
Well, except for the 8-bit CPUs covered by cc65 and sdcc. Hmm .. LLVM has a plain C back-end, which I'd presume you can pass the output of to those compilers...
https://github.com/JuliaComputingOSS/llvm-cbe
-
But if you increment the count as macro's parameter, x is incremented three times:
z = triple(++x) // this set z = ((++x) + (++x) + (++x))
Worse: it invokes the dreaded Nasal Demons (https://en.wikipedia.org/wiki/Undefined_behaviour) of Undefined Behaviour. You cannot apply pre- or post-increment or -decrement more than once to a variable in a single expression.
Many C compilers do usually do what you expect without surprises – except when you enable higher optimizations, and the results start to differ!
Thus, it is better to avoid that sort of a thing, and also enable compiler warnings (so the compiler will tell you if you accidentally do that). Even if a simple test shows that a particular version of a particular compiler seems to handle it right.
But what is "handle it right"?
If you do that with x initially 10, my assumption, as a compiler guy, is that "handling it right" would probably be 13 + 13 + 13. But you, on the other hand, might well imagine that "handling it right" would be 11 + 12 + 13.
It's undefined. It could be either -- they are both perfectly consistent and logical. Or it could be something else, though it's harder I think to make a consistent argument for something else. Maybe 12 + 12 + 13 is arguable.
In general, it's just a very very bad way to code.
-
So I tried a couple of compilers on:
#define triple(x) x+x+x
int foo(int n){
return triple(++n);
}
clang on my Mac gives:
0000000000000000 <ltmp0>:
0: 08 04 00 0b add w8, w0, w0, lsl #1
4: 00 19 00 11 add w0, w8, #6
8: c0 03 5f d6 ret
So, that's returning 3*n + 6, i.e. for n=10 it's 11+12+13.
gcc on x86 Linux gives:
0000000000000000 <foo>:
0: f3 0f 1e fa endbr64
4: 8d 44 7f 07 lea 0x7(%rdi,%rdi,2),%eax
8: c3 retq
That's 3*n + 7, so actually for n=10 that's 12+12+13.
gcc for risc-v gives:
0000000000000000 <foo>:
0: 0025079b addiw a5,a0,2
4: 0017979b slliw a5,a5,0x1
8: 250d addiw a0,a0,3
a: 9d3d addw a0,a0,a5
c: 8082 ret
So that's 2*(n+2) + (n+3), or 3*n + 7, consistent with x86 gcc.
I'm glad I said 12+12+13 is arguable in my previous post! That was based on the idea of evaluating side effects of each operand to a binary operator (or function) just before applying that operator (or function), and leaving side-effects on arguments of other operators until they were about to be evaluated.
-
Let's say it simple and concise:
don't use macro. Never.
edit: bold
-
That's overreacting. If you are writing 'vanilla' ISO C compliant code even MISRA hasn't gone as far as totally banning function-like macros. C++ code is a different matter as it has better ways of achieving a similar result.
Ref:
- MISRA C:2004, 19.7 - A function should be used in preference to a function-like macro.
- MISRA C++:2008, 16-0-4 - Function-like macros shall not be defined.
- MISRA C:2012, Dir. 4.9 - A function should be used in preference to a function-like macro where they are interchangeable
-
No need to shout.
I wouldn't go that far.
However, it is reasonable to say that inline functions and modern complier options render a lot of macro use unnecessary, and the rest implementable in safer ways than times past.
Perhaps better to say - don't use any language feature without having taken time to understand it.
-
Perhaps better to say - don't use any language feature without having taken time to understand it.
Precisely.
-
Don't use anything you don't know how to use.
C does not have templates, so macros are used for generic programming or metaprogramming. Sometimes this is a lifesaver when it comes to readability, writability and maintainability of code bases. Yes, we all know the preprocessor sucks, and there are a few catches, you just need to know them:
* for function-like interface, need to wrap inside do{} while(0), which looks odd when you see it for the first time
* arguments must be in (parenthesis).
Replacements are not without catches, either. In real world (ignoring dreams, hallucinations, etc.), the replacement for a macro means a non-portable attribute/pragma thing, forcing inlining of the function. Also, real-world experience shows that people who intend to do that, do not do that.
Yet reality also shows that people often just forget to qualify their "private" functions static. Mistakes happen. This does not mean that you should not use functions.
-
Don't use anything you don't know how to use.
Sage advice, but obviously you generally need to use a thing to learn how to use it better.
* for function-like interface, need to wrap inside do{} while(0), which looks odd when you see it for the first time
Only multi-statement macros need this trick.
The problem is that if you define a macro such as
#define do_a_and_b(x, y) \
a(x,y); \
b(x,y)
you have a problem if you later write
if (test)
do_a_and_b(x, y);
because this is going to expand to
if (test)
a(x, y);
b(x,y);
The problem here is that b(x,y) will be called whether test is true or not - which is almost certainly not what the user of the macro expected.
You could make the two statements into a block
#define do_a_and_b(x, y) {\
a(x,y); \
b(x,y); }
that does fix the "if" problem but causes a syntax error if you add a semi-colon after the macro call.
Wrapping the block in do {...} while (0) gives a single block of code (which executes once) *and* allows a trailing semi-colon after the macro call without inducing a syntax error.
-
DiTBho: no need to shout.
As for the advice itself: you don’t have any choice. Not unless you want to stop using any library, including the C standard library.
-
Let's say it simple and concise:
don't use macro. Never.
edit: bold
Disagree.
I am 100% convinced that the preprocessor is THE reason that hacky ugly C won out over functionally-equivalent but more "pure" languages such as Pascal.
The preprocessor, skilfully used, enables extreme portability of code between very different systems, while keeping the main body of the code compact and readable.
As an example of proven extreme portability over 30+ years and dozens of different systems, I present the Boehm GC:
https://github.com/ivmai/bdwgc/blob/master/include/private/gcconfig.h
-
The preprocessor, skilfully used, enables extreme portability of code between very different systems, while keeping the main body of the code compact and readable.
I agree with that. And not just for portability. It simply allows to avoid code duplication and makes modifications easier - that goes beyond portability.
Sure it's simple and raw. With potential pitfalls. But very flexible. Most attempts at replacing it with a "cleaner" approach for metaprogramming (such as C++ templates, or various kinds of so-called "hygienic" marcro systems) have usually led to monsters to just be able to compete with the flexibility. Sure they are safer, more elegant ... etc... but to allow the same amount of flexibility, they inevitably need to grow to untractable monsters. And with C++, you still see some amount of preprocessor being used.
While the simplicity of the C preprocessor (like the simplicity of C) can be "dangerous", it's also a benefit. As long as you know how it works (and it IS simple), looking at a macro definition (unless it's absolutely horrible, like obfuscated C contest-worthy) will tell you how it expands rather easily. The same is not necessarily true for complex template stuff.
Sure there ARE horrific uses ot the preprocessor (as well as horrific uses of any language), but that doesn't mean it's bad or should be avoided at all costs.
As to Pascal, I think it was missing a bit too much - at least in its original form - to be really usable for anything either moderately complex (it didn't have separate compilation or modules!), or moderately low-level. Not just a matter of preprocessor here. So in the early 70's, not sure Pascal would have been able to "compete" even if it got a preprocessor.
Later variants of Pascal got a lot more features, so that's a different story. But it was too late.
-
As to Pascal, I think it was missing a bit too much - at least in its original form - to be really usable for anything either moderately complex (it didn't have separate compilation or modules!), or moderately low-level. Not just a matter of preprocessor here. So in the early 70's, not sure Pascal would have been able to "compete" even if it got a preprocessor.
Later variants of Pascal got a lot more features, so that's a different story. But it was too late.
Sure, the very first version was too limited. But it quickly got extended and by 1977 or 1978 things such as UCSD Pascal and VAX Pascal were completely suitable for both systems and application programming. Apple Lisa and then Mac were programmed in Pascal (including all of the OS that wasn't in assembly language). I believe the same goes for early Windows.
I don't think C was at all available or popular outside of Unix until 1985 or 1986. I never used plain C at all in those days -- I went straight from (Object) Pascal to C++ in 1989/90.
-
And there was Turbo Pascal. Which did a lot to make Pascal popular for a while.
I don't know about very early Windows, but the Windows API that I've run into (from Windows 3.1 to... current) has always exposed C interfaces. But I seem to remember that the calling convention of a lot of the API was actually Pascal - so that may come from the early days, and you may be right.
As I remember, for "general-purpose use", C started to be talked about in the late 80's indeed. I remember computer magazine articles, from that period (maybe 87, 88) that talked about C as the language you should start taking a look at...
C was still popular as a language on Windows until mid to late 90's. Remember the famous Petzold's books.
But from say mid 90's to today, I think the *popularity* of C has been mostly due to Linux on one hand, and embedded dev on the other hand.
-
I had
Hitech Hisoft C on my ZX Spectrum back in 1984. IIRC it didn't play well with the ZX Microdrives (Sinclair's attempt at random access storage using an endless tape loop in a cartridge somewhat similar to an eight track cassette but smaller than a thin matchbox). Then I bought a 68000 C compiler for my Sinclair QL in '87, and disk drives as well because compiling from Microdrive was painfully slow and somewhat unreliable, and for part of 1990 I was employed writing a graphical scientific application in Microsoft C 6.0 to run under DOS and Windows 2.0. I don't think I was a particularly early adopter as I know a number of universities were teaching engineers C programming by the mid '80s.
-
I had Hitech C on my ZX Spectrum back in 1984.
That would have been on CP/M?
I didn't have much experience of CP/M except for a DEC Rainbow in 1984 (which only had Turbo Pascal that I recall), then the CPC664 a couple of years later. But it was pretty familiar as I'd previously used DEC's RT/11.
Oh! I also did a little consulting job helping some engineer get a printer working on a Kaypro. That would I think have been summer 83-84. So my first experience of CP/M.
-
Sorry about the brain-fart. I had Hisoft C (https://worldofspectrum.org/archive/software/utilities/hisoft-c-hisoft) on the ZX Spectrum, which compiled from cassette tape or a RAM buffer. As the ZX Spectrum didn't have a cassette control interface, compilation from tape involved a lot of noting of tape counter numbers, rewinding, and start/stopping the tape as directed! I later got the +3 version which had disc support. My uncle was the CP/M wizard in the family, and ran a maxed out Amstrad PCW, and in the end, a hard drive on it before he moved onto PCs.
-
DiTBho: no need to shout.
As for the advice itself: you don’t have any choice. Not unless you want to stop using any library, including the C standard library.
... or build a massive NIH cathedral with new C standard library, complete tooling, even compilers written from scratch or modified ("hacked"), then make this a company policy, so that maybe 5-10 people on the Earth follow this new C-like language, which doesn't even allow * and & for pointer syntax but replaces it with something else. Then start filling threads giving others advice based on this internal paradigm, 98% of the time completely "forgetting" to mention this has nothing to do with normal C, causing massive confusion to everyone.
In other words, I applaud complex and interesting custom solutions; complete descriptions thereof even more; but I hate games like this.
-
I have to say that I largely agree with SiliconWizard's analysis. C was popular because it was simply easy to write code in, at almost any level of the system from almost bare metal to complex end user systems and everything between.
It was also found on everything from 8-bit CPUs to mainframes.
It helped that it was a simple, but elegant language, easy to learn and even master but with distinct "depth" when needed.
Obviously it was not perfect, quite a few features were essentially missing from the language and had to be handled by system libraries and it is safe to say that it bequeathed to the software industry whole new classes of coding errors.
But it remains my favourite language.
C++ in particular has become too complex - indeed had become too complex around the time I left the software industry in the early 2000's and has only got worse.
-
Disagree.
I am 100% convinced that the preprocessor is THE reason that hacky ugly C won out over functionally-equivalent but more "pure" languages such as Pascal.
Your opinion, bus sorry, I don't agree with that.
For me, C flexibility with "data-struct" and "data-union" coupled with more powerful "pointer arithmetic" is one of *THE* reason whey C is better than Pascal in this specific cases, as well as Pascal, Modula2 and Oberon are better about making a project modular. With C ... it's rather impossible.
--
Anyway, I'm more interested in being able to debug and test the code, therefore macro represent a serious problem (especially for team-projects) because
1) specifically because macro have no body and no type (and no namespace)
2) so (especially on the ICE side) the can be easily misused
3) and - worse still - sometimes they expand into very complex code
4) that can be difficult to identify and understand in the preprocessed file
5) also it is easy to make error-prone code in macros
6) and it's impossible to step-in a function-like macro because it's not a true-function
7) so it's impossible to make automated test-cases and automated coverage
In short, I don't like Macro because I cannot debug them, while for sure they can mess up your day, and even if you take the time to fix it, well rationally it's unacceptable that I need to manually care manual testing points with an high probability of being error-prone points instead of safely invoking automating testing tools.
-
but that doesn't mean it's bad or should be avoided at all costs.
DO178C says they must be avoided at all costs until you have a test-case-plan to test your code. Which implies ***you*** have to write manual testing points since you cannot debug Macro.
What makes me annoyed here is that Macro are like public toilets: everybody wants to use them but nobody wants to clean.
-
DiTBho: no need to shout.
As for the advice itself: you don’t have any choice. Not unless you want to stop using any library, including the C standard library.
... or build a massive NIH cathedral with new C standard library, complete tooling, even compilers written from scratch or modified ("hacked"), then make this a company policy, so that maybe 5-10 people on the Earth follow this new C-like language, which doesn't even allow * and & for pointer syntax but replaces it with something else. Then start filling threads giving others advice based on this internal paradigm, 98% of the time completely "forgetting" to mention this has nothing to do with normal C, causing massive confusion to everyone.
In other words, I applaud complex and interesting custom solutions; complete descriptions thereof even more; but I hate games like this.
This must be referred to my comments on my builder, which was completely unrelated to the Clang bug because those lines of C show the same defect even with standard C89/C99.
Ok, I made the mistake to quickly test them with what I had on my hands, but why are you still here reproach me it was somehow confusing? and can't I express what I honestly think from 25 years of professional programming experience?
I won't make the same mistake in the future. I promise.
-
both gcc and clang have the -E option.
I am using windows 10 operating system.
I'm so sorry.
What is command for winows
If you use gcc or clang, then the same as anywhere else.
I don't know if there are versions of gcc and clang that work in the DOS command line -- probably -- but you can certainly use them if you install WSL or cygwin.
I believe Mingw-w64 is a port of gcc that can create Windows applications. I'm not sure whether it runs under DOS or in cygwin etc.
one text file generates after running command. gcc -E hello.c > hello.txt
#include<stdio.h>
#define add(y) ((y) + 1)
int main ()
{
int y = 5;
printf("y = %d ", add(y));
return 0;
}
text file
# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "hello.c"
# 1 "c:\\mingw\\include\\stdio.h" 1 3
# 38 "c:\\mingw\\include\\stdio.h" 3
# 39 "c:\\mingw\\include\\stdio.h" 3
# 56 "c:\\mingw\\include\\stdio.h" 3
# 1 "c:\\mingw\\include\\_mingw.h" 1 3
# 55 "c:\\mingw\\include\\_mingw.h" 3
# 56 "c:\\mingw\\include\\_mingw.h" 3
# 66 "c:\\mingw\\include\\_mingw.h" 3
# 1 "c:\\mingw\\include\\msvcrtver.h" 1 3
# 35 "c:\\mingw\\include\\msvcrtver.h" 3
# 36 "c:\\mingw\\include\\msvcrtver.h" 3
# 67 "c:\\mingw\\include\\_mingw.h" 2 3
# 1 "c:\\mingw\\include\\w32api.h" 1 3
# 35 "c:\\mingw\\include\\w32api.h" 3
# 36 "c:\\mingw\\include\\w32api.h" 3
# 59 "c:\\mingw\\include\\w32api.h" 3
# 1 "c:\\mingw\\include\\sdkddkver.h" 1 3
# 35 "c:\\mingw\\include\\sdkddkver.h" 3
# 36 "c:\\mingw\\include\\sdkddkver.h" 3
# 60 "c:\\mingw\\include\\w32api.h" 2 3
# 74 "c:\\mingw\\include\\_mingw.h" 2 3
# 57 "c:\\mingw\\include\\stdio.h" 2 3
# 69 "c:\\mingw\\include\\stdio.h" 3
# 1 "c:\\mingw\\lib\\gcc\\mingw32\\6.3.0\\include\\stddef.h" 1 3 4
# 216 "c:\\mingw\\lib\\gcc\\mingw32\\6.3.0\\include\\stddef.h" 3 4
# 216 "c:\\mingw\\lib\\gcc\\mingw32\\6.3.0\\include\\stddef.h" 3 4
typedef unsigned int size_t;
# 328 "c:\\mingw\\lib\\gcc\\mingw32\\6.3.0\\include\\stddef.h" 3 4
typedef short unsigned int wchar_t;
# 357 "c:\\mingw\\lib\\gcc\\mingw32\\6.3.0\\include\\stddef.h" 3 4
typedef short unsigned int wint_t;
# 70 "c:\\mingw\\include\\stdio.h" 2 3
# 94 "c:\\mingw\\include\\stdio.h" 3
# 1 "c:\\mingw\\include\\sys/types.h" 1 3
# 34 "c:\\mingw\\include\\sys/types.h" 3
# 35 "c:\\mingw\\include\\sys/types.h" 3
# 62 "c:\\mingw\\include\\sys/types.h" 3
typedef long __off32_t;
typedef __off32_t _off_t;
typedef _off_t off_t;
# 91 "c:\\mingw\\include\\sys/types.h" 3
typedef long long __off64_t;
typedef __off64_t off64_t;
# 115 "c:\\mingw\\include\\sys/types.h" 3
typedef int _ssize_t;
typedef _ssize_t ssize_t;
# 95 "c:\\mingw\\include\\stdio.h" 2 3
# 1 "c:\\mingw\\lib\\gcc\\mingw32\\6.3.0\\include\\stdarg.h" 1 3 4
# 40 "c:\\mingw\\lib\\gcc\\mingw32\\6.3.0\\include\\stdarg.h" 3 4
typedef __builtin_va_list __gnuc_va_list;
# 103 "c:\\mingw\\include\\stdio.h" 2 3
# 210 "c:\\mingw\\include\\stdio.h" 3
typedef struct _iobuf
{
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
} FILE;
# 239 "c:\\mingw\\include\\stdio.h" 3
extern __attribute__((__dllimport__)) FILE _iob[];
# 252 "c:\\mingw\\include\\stdio.h" 3
__attribute__((__cdecl__)) __attribute__((__nothrow__)) FILE * fopen (const char *, const char *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) FILE * freopen (const char *, const char *, FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fflush (FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fclose (FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int remove (const char *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int rename (const char *, const char *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) FILE * tmpfile (void);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) char * tmpnam (char *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) char *_tempnam (const char *, const char *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _rmtmp (void);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _unlink (const char *);
# 289 "c:\\mingw\\include\\stdio.h" 3
__attribute__((__cdecl__)) __attribute__((__nothrow__)) char * tempnam (const char *, const char *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int rmtmp (void);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int unlink (const char *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int setvbuf (FILE *, char *, int, size_t);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) void setbuf (FILE *, char *);
# 342 "c:\\mingw\\include\\stdio.h" 3
extern int __attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__mingw_printf__,2,3))) __mingw_fprintf(FILE*, const char*, ...);
extern int __attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__mingw_printf__,1,2))) __mingw_printf(const char*, ...);
extern int __attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__mingw_printf__,2,3))) __mingw_sprintf(char*, const char*, ...);
extern int __attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__mingw_printf__,3,4))) __mingw_snprintf(char*, size_t, const char*, ...);
extern int __attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__mingw_printf__,2,0))) __mingw_vfprintf(FILE*, const char*, __builtin_va_list);
extern int __attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__mingw_printf__,1,0))) __mingw_vprintf(const char*, __builtin_va_list);
extern int __attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__mingw_printf__,2,0))) __mingw_vsprintf(char*, const char*, __builtin_va_list);
extern int __attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__mingw_printf__,3,0))) __mingw_vsnprintf(char*, size_t, const char*, __builtin_va_list);
# 376 "c:\\mingw\\include\\stdio.h" 3
extern unsigned int _mingw_output_format_control( unsigned int, unsigned int );
# 453 "c:\\mingw\\include\\stdio.h" 3
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fprintf (FILE *, const char *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int printf (const char *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int sprintf (char *, const char *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int vfprintf (FILE *, const char *, __builtin_va_list);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int vprintf (const char *, __builtin_va_list);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int vsprintf (char *, const char *, __builtin_va_list);
# 478 "c:\\mingw\\include\\stdio.h" 3
int __attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__ms_printf__,2,3))) __msvcrt_fprintf(FILE *, const char *, ...);
int __attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__ms_printf__,1,2))) __msvcrt_printf(const char *, ...);
int __attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__ms_printf__,2,3))) __msvcrt_sprintf(char *, const char *, ...);
int __attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__ms_printf__,2,0))) __msvcrt_vfprintf(FILE *, const char *, __builtin_va_list);
int __attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__ms_printf__,1,0))) __msvcrt_vprintf(const char *, __builtin_va_list);
int __attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__ms_printf__,2,0))) __msvcrt_vsprintf(char *, const char *, __builtin_va_list);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _snprintf (char *, size_t, const char *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _vsnprintf (char *, size_t, const char *, __builtin_va_list);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _vscprintf (const char *, __builtin_va_list);
# 501 "c:\\mingw\\include\\stdio.h" 3
__attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__mingw_printf__,3,4)))
int snprintf (char *, size_t, const char *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) __attribute__((__format__(__mingw_printf__,3,0)))
int vsnprintf (char *, size_t, const char *, __builtin_va_list);
__attribute__((__cdecl__)) __attribute__((__nothrow__))
int vscanf (const char * __restrict__, __builtin_va_list);
__attribute__((__cdecl__)) __attribute__((__nothrow__))
int vfscanf (FILE * __restrict__, const char * __restrict__, __builtin_va_list);
__attribute__((__cdecl__)) __attribute__((__nothrow__))
int vsscanf (const char * __restrict__, const char * __restrict__, __builtin_va_list);
# 646 "c:\\mingw\\include\\stdio.h" 3
__attribute__((__cdecl__)) __attribute__((__nothrow__)) ssize_t
getdelim (char ** __restrict__, size_t * __restrict__, int, FILE * __restrict__);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) ssize_t
getline (char ** __restrict__, size_t * __restrict__, FILE * __restrict__);
# 666 "c:\\mingw\\include\\stdio.h" 3
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fscanf (FILE *, const char *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int scanf (const char *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int sscanf (const char *, const char *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fgetc (FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) char * fgets (char *, int, FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fputc (int, FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fputs (const char *, FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) char * gets (char *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int puts (const char *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int ungetc (int, FILE *);
# 687 "c:\\mingw\\include\\stdio.h" 3
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _filbuf (FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _flsbuf (int, FILE *);
extern inline __attribute__((__gnu_inline__)) __attribute__((__cdecl__)) __attribute__((__nothrow__)) int getc (FILE *);
extern inline __attribute__((__gnu_inline__)) __attribute__((__cdecl__)) __attribute__((__nothrow__)) int getc (FILE * __F)
{
return (--__F->_cnt >= 0)
? (int) (unsigned char) *__F->_ptr++
: _filbuf (__F);
}
extern inline __attribute__((__gnu_inline__)) __attribute__((__cdecl__)) __attribute__((__nothrow__)) int putc (int, FILE *);
extern inline __attribute__((__gnu_inline__)) __attribute__((__cdecl__)) __attribute__((__nothrow__)) int putc (int __c, FILE * __F)
{
return (--__F->_cnt >= 0)
? (int) (unsigned char) (*__F->_ptr++ = (char)__c)
: _flsbuf (__c, __F);
}
extern inline __attribute__((__gnu_inline__)) __attribute__((__cdecl__)) __attribute__((__nothrow__)) int getchar (void);
extern inline __attribute__((__gnu_inline__)) __attribute__((__cdecl__)) __attribute__((__nothrow__)) int getchar (void)
{
return (--(&_iob[0])->_cnt >= 0)
? (int) (unsigned char) *(&_iob[0])->_ptr++
: _filbuf ((&_iob[0]));
}
extern inline __attribute__((__gnu_inline__)) __attribute__((__cdecl__)) __attribute__((__nothrow__)) int putchar(int);
extern inline __attribute__((__gnu_inline__)) __attribute__((__cdecl__)) __attribute__((__nothrow__)) int putchar(int __c)
{
return (--(&_iob[1])->_cnt >= 0)
? (int) (unsigned char) (*(&_iob[1])->_ptr++ = (char)__c)
: _flsbuf (__c, (&_iob[1]));}
# 734 "c:\\mingw\\include\\stdio.h" 3
__attribute__((__cdecl__)) __attribute__((__nothrow__)) size_t fread (void *, size_t, size_t, FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) size_t fwrite (const void *, size_t, size_t, FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fseek (FILE *, long, int);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) long ftell (FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) void rewind (FILE *);
# 787 "c:\\mingw\\include\\stdio.h" 3
typedef long long fpos_t;
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fgetpos (FILE *, fpos_t *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fsetpos (FILE *, const fpos_t *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int feof (FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int ferror (FILE *);
# 808 "c:\\mingw\\include\\stdio.h" 3
__attribute__((__cdecl__)) __attribute__((__nothrow__)) void clearerr (FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) void perror (const char *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) FILE * _popen (const char *, const char *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _pclose (FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) FILE * popen (const char *, const char *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int pclose (FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _flushall (void);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _fgetchar (void);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _fputchar (int);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) FILE * _fdopen (int, const char *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _fileno (FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _fcloseall (void);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) FILE * _fsopen (const char *, const char *, int);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _getmaxstdio (void);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _setmaxstdio (int);
# 859 "c:\\mingw\\include\\stdio.h" 3
unsigned int __attribute__((__cdecl__)) __mingw_get_output_format (void);
unsigned int __attribute__((__cdecl__)) __mingw_set_output_format (unsigned int);
int __attribute__((__cdecl__)) __mingw_get_printf_count_output (void);
int __attribute__((__cdecl__)) __mingw_set_printf_count_output (int);
# 885 "c:\\mingw\\include\\stdio.h" 3
extern inline __attribute__((__gnu_inline__)) __attribute__((__always_inline__)) unsigned int __attribute__((__cdecl__)) _get_output_format (void)
{ return __mingw_get_output_format (); }
extern inline __attribute__((__gnu_inline__)) __attribute__((__always_inline__)) unsigned int __attribute__((__cdecl__)) _set_output_format (unsigned int __style)
{ return __mingw_set_output_format (__style); }
# 910 "c:\\mingw\\include\\stdio.h" 3
extern inline __attribute__((__gnu_inline__)) __attribute__((__always_inline__)) int __attribute__((__cdecl__)) _get_printf_count_output (void)
{ return 0 ? 1 : __mingw_get_printf_count_output (); }
extern inline __attribute__((__gnu_inline__)) __attribute__((__always_inline__)) int __attribute__((__cdecl__)) _set_printf_count_output (int __mode)
{ return 0 ? 1 : __mingw_set_printf_count_output (__mode); }
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fgetchar (void);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fputchar (int);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) FILE * fdopen (int, const char *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fileno (FILE *);
# 930 "c:\\mingw\\include\\stdio.h" 3
extern inline __attribute__((__gnu_inline__)) __attribute__((__always_inline__)) FILE * __attribute__((__cdecl__)) __attribute__((__nothrow__)) fopen64 (const char *, const char *);
extern inline __attribute__((__gnu_inline__)) __attribute__((__always_inline__))
FILE * __attribute__((__cdecl__)) __attribute__((__nothrow__)) fopen64 (const char * filename, const char * mode)
{ return fopen (filename, mode); }
int __attribute__((__cdecl__)) __attribute__((__nothrow__)) fseeko64 (FILE *, __off64_t, int);
extern inline __attribute__((__gnu_inline__)) __attribute__((__always_inline__)) __off64_t __attribute__((__cdecl__)) __attribute__((__nothrow__)) ftello64 (FILE *);
extern inline __attribute__((__gnu_inline__)) __attribute__((__always_inline__))
__off64_t __attribute__((__cdecl__)) __attribute__((__nothrow__)) ftello64 (FILE * stream)
{ fpos_t __pos; return (fgetpos(stream, &__pos)) ? -1LL : (__off64_t)(__pos); }
# 958 "c:\\mingw\\include\\stdio.h" 3
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fwprintf (FILE *, const wchar_t *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int wprintf (const wchar_t *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int vfwprintf (FILE *, const wchar_t *, __builtin_va_list);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int vwprintf (const wchar_t *, __builtin_va_list);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _snwprintf (wchar_t *, size_t, const wchar_t *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _vscwprintf (const wchar_t *, __builtin_va_list);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _vsnwprintf (wchar_t *, size_t, const wchar_t *, __builtin_va_list);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fwscanf (FILE *, const wchar_t *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int wscanf (const wchar_t *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int swscanf (const wchar_t *, const wchar_t *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) wint_t fgetwc (FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) wint_t fputwc (wchar_t, FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) wint_t ungetwc (wchar_t, FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int swprintf (wchar_t *, const wchar_t *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int vswprintf (wchar_t *, const wchar_t *, __builtin_va_list);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) wchar_t * fgetws (wchar_t *, int, FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int fputws (const wchar_t *, FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) wint_t getwc (FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) wint_t getwchar (void);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) wint_t putwc (wint_t, FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) wint_t putwchar (wint_t);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) wchar_t * _getws (wchar_t *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _putws (const wchar_t *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) FILE * _wfdopen(int, const wchar_t *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) FILE * _wfopen (const wchar_t *, const wchar_t *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) FILE * _wfreopen (const wchar_t *, const wchar_t *, FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) FILE * _wfsopen (const wchar_t *, const wchar_t *, int);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) wchar_t * _wtmpnam (wchar_t *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) wchar_t * _wtempnam (const wchar_t *, const wchar_t *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _wrename (const wchar_t *, const wchar_t *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _wremove (const wchar_t *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) void _wperror (const wchar_t *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) FILE * _wpopen (const wchar_t *, const wchar_t *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int snwprintf (wchar_t *, size_t, const wchar_t *, ...);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int vsnwprintf (wchar_t *, size_t, const wchar_t *, __builtin_va_list);
# 1016 "c:\\mingw\\include\\stdio.h" 3
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int vwscanf (const wchar_t *__restrict__, __builtin_va_list);
__attribute__((__cdecl__)) __attribute__((__nothrow__))
int vfwscanf (FILE *__restrict__, const wchar_t *__restrict__, __builtin_va_list);
__attribute__((__cdecl__)) __attribute__((__nothrow__))
int vswscanf (const wchar_t *__restrict__, const wchar_t * __restrict__, __builtin_va_list);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) FILE * wpopen (const wchar_t *, const wchar_t *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) wint_t _fgetwchar (void);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) wint_t _fputwchar (wint_t);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _getw (FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int _putw (int, FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) wint_t fgetwchar (void);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) wint_t fputwchar (wint_t);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int getw (FILE *);
__attribute__((__cdecl__)) __attribute__((__nothrow__)) int putw (int, FILE *);
# 2 "hello.c" 2
# 4 "hello.c"
int main ()
{
int y = 5;
printf("y = %d ", ((y) + 1));
return 0;
}
what should be observed in this text file
-
what should be observed in this text file
The third non-empty line from bottom.
-
Disagree.
I am 100% convinced that the preprocessor is THE reason that hacky ugly C won out over functionally-equivalent but more "pure" languages such as Pascal.
Your opinion, bus sorry, I don't agree with that.
For me, C flexibility with "data-struct" and "data-union" coupled with more powerful "pointer arithmetic" is one of *THE* reason whey C is better than Pascal in this specific cases
Pascal has records, variant records, pointers, and the ability to cast between integers and pointers and find the size of objects. It has *exactly* the same capabilities as C in those regards.
-
Ok, I made the mistake to quickly test them with what I had on my hands, but why are you still here reproach me it was somehow confusing? and can't I express what I honestly think from 25 years of professional programming experience?
I'm not sure that's regarded as being a lot around here :-)
What is "professional", exactly?
I hadn't graduated yet (one more year...), but in 1982 I got paid to spend 10 weeks of my summer vacation writing COBOL (and a bit of FORTRAN) on a PR1ME minicomputer at the Whangarei City Council. I was the only person working on the project and it was successful.
I'm far from the most experienced on this forum ...
-
Pascal has records, variant records, pointers, and the ability to cast between integers and pointers and find the size of objects. It has *exactly* the same capabilities as C in those regards.
Pascal was my first programming language, and I know in TurboPascal v7 there is no "cast" feature which does this in the compile process like C. In Turbo Pascal there are standard procedures (built-in, or external) which *convert* a type to another type.
That's how the first Pascal works. Call it "unchecked converter" (usually written in Turbo Assembly) and you get exactly what I have always tried to reproduce in every of C projects (in my case, written in C, but segragated outside the project space, so they won't be checked, that's why they are called "unchecked converters").
If comparing pre-C89 to the first Pascal makes sense, then you also have to consider that the second one is more limited because it's more typed, and you cannot directly convert a record (struct) to an other record like with Unions in C, again you need to use "unchecked converters", and that's why I think people prefer C.
Freepascal is the evolution of first Pascal, it adds new features and two flavors of typecasting - implicit typecasting and explicit typecasting, so now you also have - value typecasting - and - variable typecast - but if here it's like comparing c89 with something new, then for example you have to consider that when source’s range of value does not fit into the destination operand’s range, the compiler will not compile the program, unless it is instructed to ignore this, and that exactly what I means: still you desperately need unchecked converters to achieve your purpose.
I think C people are not comfortable with this. When I switched to C I didn't feel it was limiting, but most of my friends left Pascal for this reason.
-
What is "professional", exactly?
I run software testing as part of the product life-cycle; "professional" means I won't get paid until I can prove with facts that a piece of code passes a series of test-cases.
So when I look at a piece of code, I usually care about the possibility to write test cases by exploiting a debugging tool like the ICE: it's theoretically wrong (you cannot test the quality on the product, but) it is a sufficient condition to do my job with the best possible results with the least effort.
If something can be debugged, it can be debugged automatically, then automatically tested with human supervision. I've been doing a lot of weird stuff over the past 15 years, recently I'm writing this kind of testing tools for a living.
When I talked about my hack to Clang, well, that was a prototype that is paying off!
-
Pascal has records, variant records, pointers, and the ability to cast between integers and pointers and find the size of objects. It has *exactly* the same capabilities as C in those regards.
Pascal was my first programming language, and I know in TurboPascal v7 there is no "cast" feature which does this in the compile process like C. In Turbo Pascal there are standard procedures (built-in, or external) which *convert* a type to another type.
I've never used a version of Turbo Pascal as late as v7. I used 1.0 and you could certainly cast between pointers of varying types.
Here is the manual for Turbo Pascal v3. It looks pretty much like what I remember from v1.0.
http://bitsavers.informatik.uni-stuttgart.de/pdf/borland/turbo_pascal/TURBO_Pascal_Reference_Manual_CPM_Version_3_Dec88.pdf (http://bitsavers.informatik.uni-stuttgart.de/pdf/borland/turbo_pascal/TURBO_Pascal_Reference_Manual_CPM_Version_3_Dec88.pdf)
May I refer you to section 22.12.2 "POINTERS AND INTEGERS"?
The standard functions Ord and Ptr provide direct control of the
address contained in a pointer. Ord returns the address contained in
its pointer argument as an Integer, and Ptr converts its Integer
argument into a pointer which is compatible with all pointer types.
These functions are extremely valuable in the hands of an experienced
programmer as they allow a pointer to point to anywhere in memory.
If used carelessly, however, they are very dangerous, as a dynamic
variable may be made to overwrite other variables, or even program
code.
Also please see 22.8 ADDR FUCNTION
Syntax: Addr(Name);
Returns the address in memory of the first byte of the type, varialbe,
procedure, or function with the identifier Name. If Name is an array, it
may be subscripted, and if Name is a record, specific fields may be
selected. The value returned is of type Integer.
Also please see 16.3.2.4.8. SIZEOF:
Syntax: SizeOf(Name);
Returns the number of bytes occupied in memory by the variable or
type Name. The result is of type Integer.
Therefore you can write code such as the following:
var
s : array[1..11] of char;
p : ^char;
begin
s := 'Hello World';
p := Ptr(Addr(s) + 6 * SizeOf(char));
WriteLn(p^);
end
... and you will see printed "W". And this will work no matter whether Char is 1 or 2 or 4 bytes.
Pascal is functionally *exactly* the same as C in this regard. The only thing different is the exact syntax for achieving it.
I didn't do any DOS or Windows programming, only Apple ][, then PDP-11/VAX and CP/M, and then Macintosh and Unix. EVERY Pascal I used had the ability to do this. The only difference I recall is some used @ instead of Addr.
-
Oh yeah, now I remember this in TP 3.0 that I used on CP/M. You could even insert assembly code directly in procedures and functions - there was no internal assembler, so you had to list the opcodes, but there was a syntax for putting the address of a parameter or variable directly in the opcodes stream.
The only thing I was really missing in Pascal was the ability to pass functions and procedures as parameters/variables. (I think that was added in TP much later?)
Of course, Wirth added that in Oberon (and maybe Modula, but I admit I don't know Modula well.)
The other thing missing was separate compilation/modules (at least in TP 3.0), but this was added in later versions as "units", which were relatively similar to Modula and Oberon modules. Units were available in the first (and I think only) version of TP for MacOS, that I also used in the early 90's. It was a decent environment to program for MacOS back then. =)
-
The only thing I was really missing in Pascal was the ability to pass functions and procedures as parameters/variables. (I think that was added in TP much later?)
Of course, Wirth added that in Oberon (and maybe Modula, but I admit I don't know Modula well.)
That has *always* been in Pascal. Maybe TP didn't include it? Borland did have a habit of just leaving stuff out.
See the railway track diagram for Parameter list on p355 of Wirth's 1976 "Algorithms + Data Structures = Programs", which includes "function foo : returnType" or "procedure bar" as items in a parameter list.
http://www.cl72.org/110dataAlgo/Algorithms%20%20%20Data%20Structures%20=%20Programs%20 (http://www.cl72.org/110dataAlgo/Algorithms%20%20%20Data%20Structures%20=%20Programs%20)[Wirth%201976-02].pdf
There is, obviously, an un-Worthian lack of type checking in this early version.
Apple's 1980 Apple Pascal Language Reference Manual explicitly states on p87 that it differs from standard Pascal in NOT allowing procedures and functions to be passed as arguments.
https://mirrors.apple2.org.za/Apple%20II%20Documentation%20Project/Software/Languages/Apple%20II%20Pascal/Manuals/Apple%20Pascal%20Language%20Reference%20Manual.pdf (https://mirrors.apple2.org.za/Apple%20II%20Documentation%20Project/Software/Languages/Apple%20II%20Pascal/Manuals/Apple%20Pascal%20Language%20Reference%20Manual.pdf)
-
what should be observed in this text file
The third non-empty line from bottom.
hello.c
printf("y = %d ", add(y));
text
printf("y = %d ", ((y) + 1));
In text file body of add (y) execute
But still there's a lot confusion
-
But still there's a lot confusion
Precisely. Confusion. Macros are confusing. What did I say from the start?
W-H-A-T?!? :D
-
The standard functions Ord and Ptr provide direct control of the address contained in a pointer. Ord returns the address contained in its pointer argument as an Integer, and Ptr converts its Integer argument into a pointer which is compatible with all pointer types.
I didn't say there are no pointers, I said there is no easy-Unions like in C, and in general, in Pascal you need more effort to get the same result.
A Pascal-union-like is called "case inside a record", it makes it possible to give a variable multiple types. In this example w1, w2, w3 are stored at the same location in memory:
OType = (type1, type2, type3);
OMe = record
case myType: OType of <-------------- this is a union-like in Pascal
type1: (w1: Integer);
type2: (w2: Integer);
type3: (w3: Integer);
end;
It's not safe, there are Pascal implementations that don't check the simple semantic correctness(1), if you want a "typesafe union", you should use "inheritance", which is a plus for me because it forces you to think twice about what you are doing with the result that it usually makes things cleaner, but not for most people who have switched to C.
Those two standard functions Ord() and Ptr() are exactly what I was talking about when I write "unchecked converters". It's a term I invented to describe things that are somehow part of the language, like "sizeof()" is part of the C language, but they are not written with the same language they belong to as operators/functions, but rather with an external language, usually assembly.
(1) certain implementations don't detect accessing an inactive variant in a case blocks in variant records.
var
O: OMe;
begin
O.myType := Type1;
O.w2 := 666; <-------- There is no w2 for Type1, w2 belongs to Type2!
end.
- ISO 7185 Standard Pascal implementations usually care about that
- Turbo Borland Pascal usually don't care about that
-
@Dadu@
I usually use cpp *only* for two things
- to define the size of a static array.
- to define the version and revision of a module
#define pool_N 200
pool_item_t pool[pool_N];
#define Ver "v1.0"
#define Rev "revA"
If you look at my topic on complex numbers, well ... this is a nice example of when I say that "macros are like public restrooms, everyone wants to use, nobody (me included) wants to clean". See, glibc adds a lot of Macro layers to describe different kinds of functions, which sounds good to listen to because you have "float atan()", "double atan()", etc ... "type_x func_y()" , but things get so messed up that gcc-v8-v11 is out of the box and nobody in Gentoo wants to clean it up because it would involve a patch to glibc, which is a prohibition since it's too fragile (if you break glibc, the whole system collapses) .
And since I don't like to waste time on that, I had to create an ugly hack to bypass some of those Macros, so things are back working and I can complete my projects.
-
The standard functions Ord and Ptr provide direct control of the address contained in a pointer. Ord returns the address contained in its pointer argument as an Integer, and Ptr converts its Integer argument into a pointer which is compatible with all pointer types.
I didn't say there are no pointers, I said there is no easy-Unions like in C, and in general, in Pascal you need more effort to get the same result.
A Pascal-union-like is called "case inside a record", it makes it possible to give a variable multiple types.
Yes, I'm perfectly aware of Pascal variant records. I used Pascal professionally as my main language for about ten years.
It's not safe
Neither are C unions. That's the whole point. You can use them for exactly the same tricks.
In some Pascal implementations you have to change the selector field in order to not get an error when accessing an aliased field. So you change the selector. It never hurts to do so anyway.
I theory, perhaps some conforming implementation could zero out the other fields when you change the selector, but I've never see that happen. The raw bits are still there, and can be accessed via the alternate field names.
As I said, C and Pascal are almost totally interchangeable.
Sometimes you need to type a little more code in Pascal but the generated machine code and runtime results are equivalent.
By far the biggest difference is that C/C++ don't have lexically nested functions with the ability to access outer function local variables from inner functions -- even when the inner function is passed to a yet another function (e.g. qsort()).
But gcc and clang implement that in C anyway, as an extension.
Another difference is that Pascal typically allows you to directly do boolean AND, OR, bit set/clear/test on bit vectors of at least 256 or 512 bits, under the type "set of ...". In C you need to explicitly create and array of char or int (etc) and write some loops or div/mod and shift.
Again, the generated machine code is exactly the same in the end.
-
What exactly is an "easy union" compared to variant records?
-
I often run
gcc -Wall -dM -E source-files... | sort
to obtain the list of all preprocessor macros that apply when compiling source-files....
I also use the Pre-defined Compiler Macros (https://sourceforge.net/p/predef/wiki/Home/) wiki, to look for preprocessor macros that let me create portability workarounds or optimized versions for specific hardware/OS/compiler/C library combinations.
As illustrated by the above discussion, there are strong opinions regarding preprocessor macros. Whether you can rely on them (consider e.g. C11 6.5.1.1 _Generic() selections), or you need to avoid them, depends on external reasons. Those external reasons can be standards like MISRA C (https://en.wikipedia.org/wiki/MISRA_C), company or organization coding conventions (https://en.wikipedia.org/wiki/Coding_conventions), and so on.
Because C itself does not force anything with respect to preprocessor macros, it is up to us humans to deal with it. The most common approach is to use all UPPERCASE names (with underscores _ replacing spaces) for only preprocessor macros. If you use an advanced source code editor, it can usually help with this kind of stuff.
Me, I try to go with the principle of least surprise, unix philosophy (https://en.wikipedia.org/wiki/Unix_philosophy), and POSIX (https://en.wikipedia.org/wiki/POSIX); the important/meaningful exceptions to the "uppercase thing is a macro; macros are in all uppercase" rule are so few one can actually remember them all: FILE (is a type and not a macro); getc() and getchar() (https://man7.org/linux/man-pages/man3/getc.3.html), and putc() and putchar() (https://man7.org/linux/man-pages/man3/putc.3.html), all four of which may be macros that evaluate the stream argument (the FILE pointer parameter) more than once. There are a few additional ones, but these five are the important ones.
-
OType = (type1, type2, type3);
OMe = record
case myType: OType of //<-------------- this is a union-like in Pascal
type1: (w1: Integer);
type2: (w2: Integer);
type3: (w3: Integer);
end;
var
O: OMe;
begin
O.myType := Type1;
O.w1 := 1; // this is ok!
O.w2 := 666; //<-------- There is no w2 for Type1, w2 belongs to Type2!
end.
typedef union
{
struct
{
uint32_t w1;
} type1;
struct
{
uint32_t w2;
} type2;
struct
{
uint32_t w3;
} type3;
} OMe_t;
void foo()
{
OMe_t O;
O.type1.w1 = 1;
O.type1.w2 = 666; // <-------- There is no w2 for Type1, w2 belongs to Type2!
}
Turbo Pascal v7 (embedded inside DoxBox on a Linux machine)
compiling foo.pas ... done
(no error)
C (Gcc on a Linux machine)
compiling foo.c ... failed
foo.c:25: error: 'struct <anonymous>' has no member named 'w2'
Conclusion: unions in C are by - de fact - simpler to check in C than in Pascal.
-
Turbo Pascal v7 (embedded inside DoxBox on a Linux machine)
compiling foo.pas ... done
(no error)
OK, so TP doesn't check the current variant. It's allowed to not check, but just to be (un)safe you can do O.myType := Type2 before trying to access w2, and it will work also on compilers that do check.
C (Gcc on a Linux machine)
compiling foo.c ... failed
foo.c:25: error: 'struct <anonymous>' has no member named 'w2'
Conclusion: unions in C are by - de fact - simpler to check in C than in Pascal.
This is stupid. Obviously you need to write O.type2.w2 = 666 if you want to get or set field w2.
You can equally easily use variant records in Pascal and unions in C to do unsafe typecasting when you want to. Which is the point -- everything you can do in C, you can also do in Pascal.
In fact Pascal has the advantage here because in C you can't tell which union member you should use.
Let's add a procedure printOme(O : Ome) and function void printOme(OMe_t O) that print the correct current field in the data passed to them.
(This is a dumb example because w1, w2, w3 all have the same type, so it doesn't matter, but whatever ... assume one of them is a float and maybe another is a char)
In Pascal:
procedure printOme(O : Ome)
begin
case O.myType of
type1: WriteLn(O.w1);
type2: WriteLn(O.w2);
type3: WriteLn(O.w3)
end
end
var
O: OMe;
begin
O.myType := Type1;
O.w1 := 1;
printOme(O);
O.myType := Type2;
O.w2 := 666;
printOme(O);
end.
And in C...
void printOme(OMe_t O){
// ?????
printf("%d\n", O.type1.w1);
printf("%d\n", O.type2.w2);
printf("%d\n", O.type3.w3);
}
void foo()
{
OMe_t O;
O.type1.w1 = 1;
primtOme(O);
O.type2.w2 = 666;
primtOme(O);
}
How do you decide which one to print in the C version?
Answer: you can't.
Of course, since Pascal and C are equivalent, you *can* do this, with extra work, by defining an OType enum and wrapping the union in a struct with an extra myType field. And then in printOme do a switch on the myType field.
-
you *can* do this, with extra work, by defining an OType enum and wrapping the union in a struct with an extra myType field. And then in printOme do a switch on the myType field.
Of course. That's what you usually do one way or another, unless you love shooting yourself in the foot.
-
you *can* do this, with extra work, by defining an OType enum and wrapping the union in a struct with an extra myType field. And then in printOme do a switch on the myType field.
Of course. That's what you usually do one way or another, unless you love shooting yourself in the foot.
Exactly.
And in case anyone wants to say "Oh, but Pascal and C aren't equivalent because if you somehow know from some other means which variant is the current one then you don't have to have the wasted storage for the type tag field".
No .. in Pascal the type of the selector is required when you declare the variant record type, but the selector name (and the colon) are not. In which case it isn't stored.
In general, I stand by my assertion that the standardised preprocessor is the only important distinguishing feature between Pascal and C.
-
In general, I stand by my assertion that the standardised preprocessor is the only important distinguishing feature between Pascal and C.
Well, I don't quite agree, but that's a matter of perspective.
I mentioned functions/procedures as parameters, you corrected me saying that they were in the Pascal definition, and I'll believe you. I didn't check. (I admit I never read the official specs of Pascal, contrary to the later Oberon and even - out of curiosity - Modula 3.) I admit I mostly used TP back then, and it didn't implement them. So anyway, let's cross that out.
But there was the matter of separate compilation. I don't think the original spec defined that, but apparently UCSD Pascal was the first defining "units", so that part was long existing too. (And in a better form than just includes.) TP didn't implement this until later versions.
So yeah, not much left missing? IIRC, you could even do conditional compilation in most Pascal compilers with "directives".
Now, outside of unions (for which I agree with you), C still was easier to use for accessing "memory" through structures than Pascal.
I don't think you could, for instance, define the memory location of a record and then access its fields to access particular memory locations as you could do with C. While the equivalent in Pascal was possible, it was not as "elegant" (that may be a matter of taste.) But here again, maybe you have a better memory and command of Pascal, and maybe that was just as easy?
Ultimately, as for later Wirth languages, the real problem was not the language itself, but the lack of existing tools for a large range of platforms.
The reason for this is a complex one - we can refer to our discussions about what makes a language popular or not. Its technical merits are most often marginal for explaining popularity.
An interesting example was Modula-3. It was *the* descendant of a wirthian language that was carefully defined by a group of industry professionals. It was fully usable, and in many aspects, would definitely still be. The tools were very few though. CM3 which was one of the few has known some kind of rebirth: https://github.com/modula3/cm3
One could also say that Ada is a descendant of those wirthian languages, and Ada is still alive and kicking, even though it's still in niche applications.
-
outside of unions (for which I agree with you), C still was easier to use for accessing "memory" through structures than Pascal.
Precisely! Plus, easier for the ICE, therefore easier for debugging, which is what I care most about everything.
-
outside of unions (for which I agree with you), C still was easier to use for accessing "memory" through structures than Pascal.
Precisely! Plus, easier for the ICE, therefore easier for debugging, which is what I care most about everything.
To be fair - I don't remember all the details of Pascal, haven't used it in ages, so I need to brush up! Taking what Bruce said:
Ptr converts its Integer argument into a pointer which is compatible with all pointer types.
Defining a pointer to a record at a specific address should be trivial in Pascal. So, contrary to what I said, this should be no problem at all.
Debugging is a different issue if you consider tools. The fact there were more tools for C does not mean C was better than Pascal. It's just a popularity problem, as I mentioned earlier, not a technical issue.
So now, I'll just have to agree with Bruce. There was no sizeable difference between Pascal and C, apart from the preprocessor. The only difference that really mattered for developers, as you yourself noted, were the tools available. But the language itself certainly never prevented such tools to exist. A number of compilers, TP being the most popular, actually lacked some features because they were meant to be simple - TP 3.0 just fit within about 30 KBytes on CP/M, with an integrated editor!
Getting to understand why some languages become more popular than others is a complex matter, which we have talked about in numerous threads already. The causes are complex to understand, and again the technical merits are often just a marginal point. But once a language becomes popular, the ecosystem grows, there's a lot more tools, libraries, etc, available, and so that reinforces the popularity.
One point that could explain the lack of debugging tools for Pascal, even back at the time when it was moderatly popular, may also have something to do with a lack of... demand. Not to sound corny, but possibly stuff written in Pascal required a lot less "debugging" with low-level tools than stuff written in C. The fact you yourself once had this particular need can by no means be generalized.
-
I think by today hardly anyone develop commercial systems using Pascal. By commercial systems I mean anything used in a product or business. So, comparing Pascal and C is rather academic now.
Yeah C is not safe. A hammer is not safe when used wrong. We should think of it as the need to improve the quality of the users (programmers) rather than putting yet another layer of steel on the boots to prevent the user from shooting himself/herself on the foot.
So allow me to remind folks of an old saying: "When it is absolutely idiot proof, it would be so overburdened only idiots will use it..."
-
In general, I stand by my assertion that the standardised preprocessor is the only important distinguishing feature between Pascal and C.
Well, I don't quite agree, but that's a matter of perspective.
I mentioned functions/procedures as parameters, you corrected me saying that they were in the Pascal definition, and I'll believe you. I didn't check. (I admit I never read the official specs of Pascal, contrary to the later Oberon and even - out of curiosity - Modula 3.) I admit I mostly used TP back then, and it didn't implement them. So anyway, let's cross that out.
But there was the matter of separate compilation. I don't think the original spec defined that, but apparently UCSD Pascal was the first defining "units", so that part was long existing too. (And in a better form than just includes.) TP didn't implement this until later versions.
Yes, that dates from 1977. VAX Pascal did something similar -- VMS allowed you to do separate compilation and mix object files from different compilers and languages right from the start.
So yeah, not much left missing? IIRC, you could even do conditional compilation in most Pascal compilers with "directives".
Yes. Not my experience was with DEC and Apple computers. I didn't use MS-DOS or Windows at all.
Apple Pascal (UCSD) and VAX Pascal had %include but not conditional compilation.
Apple Lisa Pascal had compile-time defines and conditional compilation. I've screenshotted the relevant page from the manual:
https://pbs.twimg.com/media/FNSYsAuaAAAUbhp.png (https://pbs.twimg.com/media/FNSYsAuaAAAUbhp.png)
Full manual at https://archive.org/details/bitsavers_applelisawl3.0ReferenceManual1984_16927177/page/n151/mode/2up (https://archive.org/details/bitsavers_applelisawl3.0ReferenceManual1984_16927177/page/n151/mode/2up)
Pascal compilers on the Macintosh followed Lisa Pascal. Early Macintosh apps (and the OS) were written and compiled on Lisa computers, using Lisa Pascal. So there is at least Macintosh Pascal (interpreted), THINK Pascal, MPW Pascal, Metroworks (CodeWarrior) Pascal. All maintained close source compatibility with Lisa Pascal, including conditional compilation.
There was a short-lived Mac version of Turbo Pascal which I believe only had include files not conditional compilation. Manual here:
https://winworldpc.com/download/73283e1b-9e4b-11e8-893a-fa163e9022f0 (https://winworldpc.com/download/73283e1b-9e4b-11e8-893a-fa163e9022f0)
What was missing was macros, and in particular function-like macros.
Now, outside of unions (for which I agree with you), C still was easier to use for accessing "memory" through structures than Pascal.
I don't think you could, for instance, define the memory location of a record and then access its fields to access particular memory locations as you could do with C. While the equivalent in Pascal was possible, it was not as "elegant" (that may be a matter of taste.) But here again, maybe you have a better memory and command of Pascal, and maybe that was just as easy?
Not sure what you mean there. I believe I already showed how to do that in ...
https://www.eevblog.com/forum/programming/how-do-you-identify-what-are-the-preprocessor-and-macros-in-c-program/msg4045750/#msg4045750 (https://www.eevblog.com/forum/programming/how-do-you-identify-what-are-the-preprocessor-and-macros-in-c-program/msg4045750/#msg4045750)
That was making a pointer to a char point to an arbitrary place in memory, but it's just the same with a record.
-
Pointer to arbitrary place in memory work very good with the mentioned "unchecked converters". I haven't yet played with the Mikroe's Pic-Pascal, but years ago I used Turbo51 (https://turbo51.com/), a Pascal compiler for intel51 chips in expanded mode (full 64Kbyte addressing space) on a weather station(1) powered by an enhanced 51 chip.
Turbo51 offered excellent code generation an optimization, it didn't offer any ICE support only source-level debugging with OMF object file, it means you have to pay for static-debugging tools, which is ok and fair, just a note: the weather-station-project was only partially written in Pascal, all the memory-side things (so through user-defined "unchecked converters") were written in Assembly.
-
Is “separate compilation units” really considered a feature of the “language”?
I would have thought it orthogonal, the way some assemblers support a linker, and some don’t.
I guess if you want to have the strong typing and range checking of something like pascal, you need an equivalent of “extern” that conveys all that in the absence of full source?
-
Is “separate compilation units” really considered a feature of the “language”?
I would have thought it orthogonal, the way some assemblers support a linker, and some don’t.
I guess if you want to have the strong typing and range checking of something like pascal, you need an equivalent of “extern” that conveys all that in the absence of full source?
Compilers for languages such as Pascal and the Modula family generally output two things: 1) an object file for the executable code, and 2) a "definition" file that contains all the type information for globally visible declarations. A binary equivalent of a C header file. Or pre-compiled header file.
-
Pascal units are like modules for other languages, they are DEFINITELY a feature of the language. Modules in general are not just object files that you happen to link.
For C, it could be argued that separate compilation is less of a feature of the language, but that still wouldn't be entirely true. The language has to define that global symbols (that aren't static) are "exported" in object files, otherwise separate compilation would never work. So even for such a basic support as in C, it IS a feature of the language.
-
Now, outside of unions (for which I agree with you), C still was easier to use for accessing "memory" through structures than Pascal.
I don't think you could, for instance, define the memory location of a record and then access its fields to access particular memory locations as you could do with C. While the equivalent in Pascal was possible, it was not as "elegant" (that may be a matter of taste.) But here again, maybe you have a better memory and command of Pascal, and maybe that was just as easy?
Not sure what you mean there. I believe I already showed how to do that in ...
Yes, I corrected that in a later post.
All in all, as I said, I think the restrictions were more due to available tools and what they implemented than to the language itself.
And there's been some kind of "battle" between C-like languages and "wirthian" languages for several decades. (I consider Ada a wirthian language.)
Common points made "against" wirthian languages is that they are seen as more "verbose" and more "rigid". Of course, some will see that as meaning more readable, more maintainable and "safer", while many tend to see this as just plain annoying and counter-productive. The latter seem to be the majority these days, but interestingly, those "arguments" are almost entirely subjective, while the former have at least some objective basis to them.
-
... the only important distinguishing feature between Pascal and C ...
It always worries me when the distinction between "a language as defined" and "particular implementations of the language" becomes blurred. "BASIC is a fine language with modern structured programming constructs, compiled code, and optional line numbers - I don't see what people are complaining about!"
Pascal as defined by Wirth was perhaps so obviously useless that no one ever tried to develop actual products with it; it remained (?) confined to the educational uses it was aimed at. I mean, no equivalent of fopen()?? "we'll just leave that all the the JCL?" (but the BNF for the core lanaguage was sure pretty!)
One of my college projects involved adding formatted output to the Pascal compiler, because a lot of programming back then ended up wanting to output nice neat tables of numbers, just like Fortran could make. There was never a thought that this could be written in Pascal itself, because user-level Pascal procedures could have a variable number of arguments (even though they were used by the Pascal IO procedures.)
I guess all that got fixed in the various real-world compilers. Which is fine, but...
OTOH, I'm not sure that early C ever had a clear "definition." By the time it escaped into the world at large, it instead essentially had a "reference implementation." If you wanted to be a successful C compiler, you had better implement things the way the PDP/11 Unix Compiler did it. "We'll define it later." (and they did.) (I particularly remember the early implementations of VARARGS: "well, we know that the caller put all the arguments on the stack in THIS order, and everything is promoted to an int, so if we take the address of the last (or first) argument, we can point to things past that and there will be the additional arguments!")
-
Now, outside of unions (for which I agree with you), C still was easier to use for accessing "memory" through structures than Pascal.
I don't think you could, for instance, define the memory location of a record and then access its fields to access particular memory locations as you could do with C. While the equivalent in Pascal was possible, it was not as "elegant" (that may be a matter of taste.) But here again, maybe you have a better memory and command of Pascal, and maybe that was just as easy?
Not sure what you mean there. I believe I already showed how to do that in ...
Yes, I corrected that in a later post.
Yes, I've seen that now.
And there's been some kind of "battle" between C-like languages and "wirthian" languages for several decades. (I consider Ada a wirthian language.)
Common points made "against" wirthian languages is that they are seen as more "verbose" and more "rigid". Of course, some will see that as meaning more readable, more maintainable and "safer", while many tend to see this as just plain annoying and counter-productive. The latter seem to be the majority these days, but interestingly, those "arguments" are almost entirely subjective, while the former have at least some objective basis to them.
I don't "get" such battles. I'll very happily use either.
What annoys me much more is all the languages that decided they should look like C while having vastly different semantics to C. Java and JavaScript and C# being the obvious offenders.
Whatever is good about C, it's not the syntax, and most especially not the syntax of declarations.
-
Whatever is good about C, it's not the syntax, and most especially not the syntax of declarations.
Oh, I do agree with that. But syntax is part of a language, and a significant part of it nonetheless. And, give C a pascal-like syntax, and many people will likely shy away from it, even if you don't change a single feature except the syntax.
Language designers deciding to make them look C-like syntax-wise is a whole part of that IMHO. It looks like a vast majority of developers prefer that kind of syntax precisely because it is seen as "non-verbose". Heck, if you look at even more recent languages, such as Rust, they seem to go out of their way to further shorten keywords to apparently make them look even less verbose. They also want to get rid of statement terminators (see Go and Swift?). Because even that looks like too much. Etc. This is my view of it, but it seems rather apparent that there is a commonly shared obsession with reducing the "verbosity" of programming languages, sometimes to a ridiculous degree.
-
Back to #define ... OMG u-boot has four layers of nested #define
#define A ... // from (file1, and don't forget to check the right #ifdef, or perhaps it's the #else branch? )
#define B A // from (file2, and don't forget to check the right #ifdef, or perhaps it's the #else branch? )
#define C B // from (file2, and don't forget to check the right #ifdef, or perhaps it's the #else branch? )
#define D C // from (file3, and don't forget to check the right #ifdef, or perhaps it's the #else branch? )
#define again what is defined here of what is defined there of what /// I am lost
That is very confusing because hard to follow (-E doesn't save your days but it helps a lot) and I don't actually know what a line of code does. It's the pATA part because some pATA controllers work in legacy-mode (like ISA cards), some in PCI-mode, and some in PCI-mem-only.
Two lines of C code cost me 6 hours of struggling, rather annoying :o :o :o
Octeon MIPS64 has something similar for their SoCs, and the Wr703 code made for Atheros is a full similar mess.
I seriously hate nested #define, really, I promise I will hug a tree and walk barefoot, but don't do this! PleaZe :'(
-
I don't understand why people do not follow normal indentation practices with the preprocessor.
Nesting itself isn't a problem. This is unreadable as well:
void func()
{
if(asdf == 123)
{
for(int i=0; i<42; i++)
{
if(condition)
break;
}
}
}
Yet people do exactly this with preprocessor. Except me, I follow normal indentation practices:
#ifdef THING
#if 1==2
void func()
{
...
}
#endif
#endif
Lines get too long? #include things away.
-
Yah, this is annoying.
emacs by default does not indent preprocessor directives -- it forces them to NOT be indented.
This can be changed by adding the following to your .emacs in the c-mode hook:
(c-set-offset (quote cpp-macro) 0 nil)
-
Yah, this is annoying.
emacs by default does not indent preprocessor directives -- it forces them to NOT be indented.
This can be changed by adding the following to your .emacs in the c-mode hook:
(c-set-offset (quote cpp-macro) 0 nil)
I admit I usually do not indent preprocessor directives. It's out of habit entirely; probably from having been exposed to a lot of source code that did not indent them, early on. Also, some compilers did not like it, but that was probably a long time ago.
That's less readable though, so I agree and will try to change habits. ;D
-
Formatting the sources is not a problem, the problem isn't whether you indent or not, the problem is four bloody layers of definitions! One inside the other like a Matryoshka.
I like soviet Matryoshkas, but only when they are made of wood and visually look like a babushka.
-
All software is layers after layers after layers. If you look at a video of cat dropping things from the table on Youtube, and count all levels of layers (all functions and ifs and elses) from the upmost level to the pixel drawing in the GPU HDL, you have possibly thousands. They are just... encapsulated.
Four layers in itself isn't the problem. They should have done some form of encapsulation; utilize makefiles and separate .c files, or maybe just used #include better, or so on. You can totally do abstraction in preprocessor, and compilers can follow it in error messages etc. ("in macro XXX, included from file YYY...")
And indentation helps for a few levels. But arguably, 4 levels of just preprocessor indentations is pushing it. Code ending up far right reveals it is too much. Solution is not to remove indentation.
-
All software is layers after layers after layers. If you look at a video of cat dropping things from the table on Youtube, and count all levels of layers (all functions and ifs and elses) from the upmost level to the pixel drawing in the GPU HDL, you have possibly thousands. They are just... encapsulated.
So you can compare *layers of code* (under control) with *layers of macro* (out of control, just text-replaced with text).
-
I don't understand the difference you talk about. Why macros are "outside of control"? What does this "control" even mean?
Why can't you testbench the macros with test vectors?
Compiler can decide to inline any function (even non-static!) without asking you, at all, for optimization, acting exactly like it was a macro.
Sure, preprocessor is kind of its own language, and thus has its own footguns.
-
Like tabs vs. spaces and brace style, macros are yet another of those hot topics.
Some people having a problem with multi-level macros appears to be a common thing - I've heard that quite a few times.
I do not quite get it either myself. As you said, like with general code, it's just a matter of writing readable and correct stuff.
The benefit of writing multi-level macros is the same as using macros in general: avoiding to duplicate code by hand, and making some modifications much easier (when there is only ONE place where changes need to be made.)
As Siwastaja said, if you're really unsure of macros - just write a set of tests for them. It's unusual (I've never done that myself and never seen it done directly), but it's definitely doable. Just write a series of tests invoking macros and the expected output, then invoke the preprocessor separately (all C compilers I've ever used could do this), and compare. It can be fully automated.
There are well known "pitfalls" of macros due to the fact they are just text substitution. The first class of them appears when macro parameters are not all parenthesized. Just put parentheses around all parameters in macro expressions, and this issue disappears immediately. The other one, of course, is calling a macro with parameters that have side-effects, if said parameters are used more than once in a given macro definition. To mitigate this one, you can either make it a rule never to call a macro with parameters that have side-effects, or never write a macro that substitutes any of its parameters more than once. Done.
-
The benefit of writing multi-level macros is the same as using macros in general: avoiding to duplicate code by hand, and making some modifications much easier (when there is only ONE place where changes need to be made.)
Um like what? Qualcom's code? Denx's code? Atheros's code? Yes, macro avoid them to duplicate code by hand, but when you have to fix their code you cry up with dozen of order on Amazon ...
What? Which orders? baskets of migraine pills, family packages :o :o :o
just write a set of tests for them. It's unusual (I've never done that myself and never seen it done directly), but it's definitely doable
Which brings us to what I wrote a page ago: I know I can write a series of tests, but guess what? Who do you think has usually to write those tests because other people simply don't care?
Dude, when you have to fix a code like that and you're not getting paid for it, you don't really like wasting days and days on it because you don't understand what layers of macro do.
-
I do not quite get it either myself. As you said, like with general code, it's just a matter of writing readable and correct stuff.
No, it's not a question of elegant code, but rather of having tools that understand and process the code so that you can do your job without constant headaches.
-
I think by today hardly anyone develop commercial systems using Pascal. By commercial systems I mean anything used in a product or business. So, comparing Pascal and C is rather academic now.
Not ISO Standard Pascal certainly, but Pascal with numerous extensions lives on in the commercial world in the form of Delphi:
https://www.embarcadero.com/products/delphi (https://www.embarcadero.com/products/delphi)