You can end any initializer list and enumeration with a comma, just as in C. ... = { 1, 2, 3, }; Dennis M. Ritchie (may God rest his soul in peace) wrote that he was always forgetting to include a comma at the end of a row, when editing/extending such a list, and therefore changed the underlying grammar so that the C compiler accepts a last comma. 40 years later and many derived languages, nobody dared to change this. It would be interesting to check if this syntax still applies to "modern" languages like JavaScript, Rust etc.
Fly Gheorghe
Posts
-
Understanding the Influence of C Programming in C# .NET -
Write test cases as if a 5 year old will do the tests.- Mind internationalization. Is your app translated? In how many languages? Serious work to do if this number is greater then 10. 2) How does the GUI looks like on a native Chinese (Traditional or Simplified)/Japanese/Korean PC? 3) Input long texts in various languages, with many specific characters. Special care when comparing and processing texts - in which culture? 4) Did you specify a culture when installing the app DB? If not, the SQL server default one will be used. Try different cultures/collations etc. 5) Input/use numbers in various cultures, alternating decimal/thousand separators etc. 6) Input/use datetimes in various cultures. 7) Set the date of the PC to 29th february in a leap year and test app functionality. 8) If you read text from disk/stream, create test files containing binary data (e.g. 0x1A/EOF - can your app read text beyond that byte?). Or create test files that have \r\n or only \n as end of line etc.
-
K&R for C++?We should take the opportunity and praise the book K&R. After 36 years since the publication of the 2nd edition (1988) and 46 years since the 1st one (1978), it is still a masterpiece of programming that every young programmer should read carefully. The importance of this book and of the "C" language to everithing that followed (C++, java, C# etc.) cannot be overestimated. Ever wonder why all books write key words with proportional font and the rest of text with true-type? K&R is a small book, but because it is very concise: every word counts - I would say every comma counts. Take for example the phrase that describes how to interprete a missing "else" from a nested "if" sequence: "...by associating the else with the closest previous else-less if." One should read this book slowly and carefully, then run all the programs inside, solve all the exercises, and then read the book again. Code examples are true marvels, from elementary to complex ones, like the Unix file implementation or the memory allocator. The C reference manual and the description of the standard libraries af the end of the book are also splendid. One should also consider the "C Problem Book" with answers to exercises. You will never find a truly similar book for any other language or topic. Rest in piece, DMR.
-
Closures (C#)From the good, old K&R C bible: A for that does not include a continue: for (e1; e2; e3) Block; is equivalent to: e1; while(e2) { Block; e3; } Replace e1 with (int i = 0;) and it will become clear what is the scope of i, and why it will have value 11. All subsequent languages like C++, java, C# etc. follows the same logic, since nobody dares to change what DMR (may God rest his soul in peace) has ruled.
-
function translated to ASMI am trying to keep discussions to a general level, so that statements were valid 20 years ago, are valid today and will be valid 20 years from now, at least with the classic CPU architecture. 1) Regarding stack management: From CPU perspective, when entering a procedure, the stack is just a memory contiguous area defined by a segment descriptor and by a stack pointer. It is irrelevant how this memory area was allocated: statically, when the process was started or dynamically before the call. Dynamically means that somebody must deallocate that area as well. 2) Run-time/development environment matters when choosing how to pass/return parameters Allocating memory on the heap is fine, assuming you have a heap in the first place. This assumes calls to the OS to get/release memory, but what if don't have on OS at all? What if you write code for a dedicated hardware controller and the only memory is statically defined? There are special environments like space/military/medical in which you are not even allowed to use dynamic memory allocation, for obvious reasons. 3) I still say that a good insight in hardware and in assembly language is essential for becoming a good software engineer. If not, who should have these insights? I don't write assembler as well nowadays, but the fact that once I did helps me write better C/C++/C# code. 4) XOR AX, AX vs. MOV AX, 0 It is not only about speed, but also about instruction encoding. "XOR AX, AX" occupies just one byte of memory, while "MOV AX, 0" occupies one byte for the op code and 2 bytes for the "immediate" 16-bit operand. If you consider 32 bits, then "MOV EAX, 0" occupies 5 bytes: one for the instruction code and 4 bytes for the 32-bit operand. The compiler treats all immediate operands in the same way. Following the same logic, on a 64-bit CPU, "MOV RAX, 0" will occupy 10 bytes, since the operand is on 8 bytes, while "XOR RAX, RAX" will be on 2 bytes only (64-bit prefix and op code). There is also another aspect. If you want to do compare operations then you must be sure that the arithmetic flags are correctly set with respect to the entity you want to compare. A conditional jump "JNZ address" will not work as expected after "MOV AX, 0" (if the AX is what you want to jump on) because "MOV" does not set any of the arithmetic flags, but will work fine after "XOR AX, AX", because "XOR" does. So those students who insisted on using XOR instead of MOV were fully right.
-
Managed c++ how to memcpy a handle reference//Assuming int is on 4 bytes typedef union u { int i; byte b[4]; } U; U x; x.b[0] = 0 // Put first byte here x.b[1] = 0 // second byte here, etc. // Then use x.i as an integer // Keep in mind that on Intel CPUs, integer values are represented in memory in reverse order. // This means that hex value 0x11223344 will be represented in memory as 0x44, 0x33, 0x22, 0x11 // This is called little-endian vs. big-endian representation. // There can be CPUs that uses the other convention. // Make a small test project and play around with some values etc.
-
function translated to ASMIn ASM there is no distinction between functions and procedures. The name procedure is usually used. You can only CALL a procedure. A function in the high-level sense (a procedure that returns something) is just a variant. Regarding passing parameters to procedures in ASM. This can be done: a) By putting values to CPU registers. This works if the number of parameters is small and parameters are rather simple data types. The procedure has direct access to parameters by means of registers. Compilers do this for simple functions/procedures/methods. Of course you need to save registers to stack and restore them after return. This is named the call sequence/frame of the procedure. b) By pushing parameters to the stack. This is the facto standard. You can push parameters from left to right (the so-called "Pascal" convention) or from right to left (the so-called "C" convention). The "C" convention works also with procedures that have a variable number of parameters. This is why the C function printf has the format as the first (and mandatory) parameter - it will be on the top of the stack when entering printf and printf will know where to find it (the format is supposed to correctly describe the number ad type of each other parameters like %s, %d etc.) When returning from the procedure the stack must be discarded of the parameters that were put on the stack. This can be done by the caller (the "C" approach) or by the procedure (the "Pascal" approach). E.g. "ADD SP, 24" or "RET 24". The c/C++ compilers use of course the "C" approach. Observe that the caller "knows" exactly how many parameters were pushed onto the stack so discarding the stack by the caller is more natural. Windows SDK uses "Pascal" convention. When dealing with large objects that must be passed, it's easier to pass then by reference, i.e. to pass an address (pointer) to a memory area where the object is stored. A pointer is a simple type. If you really need to pass a large object by value (i.e. make a copy), you can copy the internal representation of the object onto the stack, and define the stack frame so that procedure has access to it. However this is more time-consuming. b) Combinations of the above 2 methods. A procedure can return a value (i.e. becoming a function) by: 1) A register (if the return value is a scalar type). For Intel CPU, the convention is to return in the accumulator (AL, AX, DX:AX, EAX, etc., depending on the processor type). Observe that scalar types include all numerical values (int, float, double) and poin
-
What the NaN?This is how you represent infinity in a computer: a) For simple precision (32 bits), set sign = 0/1, mantis = 0x7FFFFF, exponent = 0xFF, and you get +/-INF. b) Similar for double and extended precision. This is how you set a NaN in simple precision: Any number except +/-INF above, that has exponent = 0 or 0xFF is a NaN. Gheorghe