So now you have a problem if you write a library that will be used by both old-school code written with wchar_t defined as an alias for unsigned short and new-school code written with wchar_t as a separate internal type. What data type do you need to use for string parameters?

This is a translation of The sad history of Unicode printf-style format specifiers in Visual C++.

Windows implemented Unicode earlier than most others operating systems. As a result Windows solutions for many problems are different from the solutions made by those who waited for the dust to settle¹. The most striking example of this is using Windows UCS-2 as Unicode encoding. It was then the encoding recommended by the Unicode Consortium because Unicode 1.0 only supported 65"536 characters². The Unicode Consortium changed its mind five years later, but by then it was too late for Windows, which had already released Win32s, Windows NT 3.1, Windows NT 3.5 , Windows NT 3.51 and Windows 95 - all of which used UCS-2³.

But today we'll talk about printf style format strings.

This is the translation of If FlushInstructionCache doesn’t do anything, why do you have to call it, revisited .

You are expected to call the FlushInstructionCache function when you generate or modify executable code at run-time - so that the processor, when executing your generated/modified code, reads the instructions you wrote, rather than old instructions that may remain in the processor's instruction cache.

Previously we learned that . This is because a simple function call was enough to clear the command cache.

But on Windows NT the FlushInstructionCache function does real job, because it needs to notify all other processors to clear their caches.

However, if you look at Windows 10, you will find that the FlushInstructionCache function looks like the Windows 95 version: she doesn't do anything.

What's the matter?

    Each program must begin with a comment containing the first and last name of the programmer, the purpose of the program, the version number and the date of creation.

    Each subroutine must begin with a comment briefly describing the purpose of the subroutine. Comments should describe What does the code, not How he does it. By tracking the code, the programmer will see for himself how it works.

    Blank lines are very useful in source code to indicate that the program is divided into sections.

    If the purpose of a statement or group of statements is difficult to understand from the source code, then it should be provided with a brief comment. However, you should describe individual statements as little as possible. Usually the effectiveness of such comments is low. If a comment describes something that is easy to see from the code, it will even make the code harder to read. If such a comment is still necessary, place it at the beginning of the block so as not to break the structure of the program.

    The names of variables and subroutines must be informative, i.e. such that, by looking at them, another programmer could guess the purpose of the variable or subroutine. A poorly chosen name that confuses the reader is even worse than an uninformative name such as X or A. Variable names should consist of lowercase letters other than the letters that begin the second or subsequent words of the name, e.g. taxRate or numberOfCars. Routine names must begin with an uppercase letter.

    Object, property, and method names must begin with uppercase letters.

    Each component name must begin with a prefix consisting of three lowercase letters indicating the component type. For example, the form name contains the prefix frm, input fields – edt, buttons – btn etc. The letters after the prefix describe the purpose or content of the component. For example, input field edtLastName contains a surname.

    To improve the readability of the source text of the program, it is recommended to write no more than one statement per line, which is caused by the peculiarities of human perception of text. It also makes it easier to step-by-step debugging in symbolic debuggers.

    There is no need to worry about the program becoming too long, since real programs are already so long that a few “extra” pages (or even dozens of pages) do not change the overall situation. The gain in understanding more than covers the increase in length.

    Two statements in a line are perfectly acceptable if the second is subordinate to the first, and is the only subordinate. The use of two or more operators in a line is not only acceptable, but also desirable if this allows you to emphasize a certain system in the local sequence of operators.

4.2. Program development rules

    All real constants must have digits both to the left and to the right of the decimal point, for example 0.15 rather than .15.

    Mixed mixtures should be avoided whenever possible. arithmetic operations, performed on operands of different types. Use built-in type conversion routines.

    To convert a real value to a string and back, use the procedures Str() And Val(), and for integer values ​​in the Object Pascal language - functions StrToInt() And IntToStr() respectively.

    Expressions containing multiple statements must contain parentheses to make the expressions easier to understand.

    Remove unnecessary code. This is especially true for statements inside loops that are executed repeatedly. For example, there is no need to assign values ​​to variables or array elements before other operators assign values ​​to them. Any such assignment operator reduces program efficiency and makes the code more cumbersome.

    Avoid using global variables. If several subroutines use the same global variable, problems may arise. serious problems during debugging, since it will be difficult to track which routine changes its value. Variables used in multiple subroutines are best passed as parameters, as this makes their use more obvious.

Updated 01/30/2009

If you are engaged in programming, then no matter what language you write in, there are always general ideas and concepts that, following them, will make your work easier. Here's my take on the basic rules when programming.

0. THE HIGHEST priority when developing any program is to maintain the ability to development And reuse. On practice only reusing code can reveal errors and improve the structure (for example, the appearance of old data, but of a “new” type). A good program is permanently modified.

Reusability should make it easier modular program structure (encapsulation and interfaces). For example, the main advantage of OOP is the ability to:

a) safe (the old one does not deteriorate) modification of ALREADY existing code,

b) the possibility of modification without source code.

On the other hand, coupling data and procedures to process it inevitably degrades modularity.

From this point of view, the main requirements for a programming language (or style that should be followed) are

Good readability;

Closedness (code explicitness);

Quick compilation of the project as a whole to immediately see the results of the modification.

1.Design of individual functions.

Pessimistic point of view on input variables.

Bool function type with initial result FALSE .

Mandatory value assignment on days off variables at the very first moment of entering the function.

It is desirable to assign output variables at the very end, and in the function body work with "internal copies of those variables" so that the calling function receives NIL as a result if an error occurs in the function body.

Extensive use of exception handling. Error messages should clearly indicate the location in the code where the failure occurred. It is usually assumed that exception handling is a matter of external, calling function. However, to support the modular structure, it is desirable to handle the error internally.

When manually managing memory, always use NULL prefilling.

2.Naming conventions for variables and functions

All global variables begin with “Gl”.

Follow standard function names: Free, Create, Init, Get, Set, Is, etc.

3.Methods for fixing errors.

Debugger.

Organizing the flow of error messages. Different types errors - identifiers of dangerous errors.

Using trace files to roughly localize the crash site.

Tools for monitoring memory leaks (for manual memory management).

Method for reconciling dll versions: storing SizeOf (UsedRecord) as a field in the record passed to the dll input.

4. Perhaps the easiest way to decide what the interface should be is to “play” with it: write examples of code that will use this module, before writing the module itself. These "fake" examples will not be lost when you finish developing the module. You can simply turn these examples into demo programs, examples for documentation, or use as tests for a module. The key principle is to write code as if the module already exists, and to assume that the module has the interface that is most useful.

We are all human and we tend to make mistakes. And even computers make mistakes if the program that powers them was written with an error. To help you avoid some mistakes when writing programs in any programming language, I will talk about some rules used when writing programs and programming methods. Following the methods described below will not only help you avoid some errors, but will also prevent their occurrence and simplify debugging your program. All of the examples and program code below are written in Visual Basic 6.0. You can use the material in this article for other programming languages. The article does not tie you to any specific language and is universal.

Design method software environments stv

To solve programming problems, the "Design Method" is used software". This method consists of several stages:

1. Determination of the conditions of the problem.
2. Analysis of the task.
3. Creation of an algorithm for solving the problem.
4. Implementation of the algorithm.
5. Testing and debugging of the finished program.
6. Support and update of the finished program.

At the first stage, the conditions of the problem are determined and it is necessary to clearly understand what is required to solve it. The main goal in this case is to weed out minor aspects from the main essence of the task.

At the second stage, input data, output data, intermediate data and what additional difficulties may arise when solving the problem are determined.

The third step is to create an algorithm - recording step-by-step procedures and then ensuring that the algorithm solves the problem properly. The algorithm can also be written in the form of flowcharts.

Probably everyone who studied programming or computer science in educational institutions was told how to draw flowcharts. And most people probably don’t like to draw them. Your humble servant is also one of them. But don't think that flowcharts are absolutely useless in programming. Personally, I never draw flowcharts when developing programs, but I can do it well. And I strongly advise you to learn how to draw them better than anyone else. If you do not learn to draw flowcharts on your own and correctly, you will not be able to understand the essence of how the program works! It is the block diagram that allows you to clearly and clearly (schematically) show how the program is executed step by step (line by line). After all, a block diagram is the algorithm for executing the program itself. What is an algorithm?

Algorithm- this is a description of the exact, step-by-step execution of actions that solve the task properly. Therefore, if you can write an algorithm for executing a program, you can write the program itself, and in any programming language (which, of course, you will know). Having learned to draw flowcharts, you will learn to analyze program code line by line, processing it in your head, just like a computer does. This is very important when debugging the program (fifth stage).

When you are faced with solving a very complex problem, you can (and should) use the “decomposition” method. The essence of the method is to split one complex problem into many interrelated, small and simple subtasks, solving which separately you will get the desired result. Here we can give an analogy with a broom. It is very difficult to break the entire broom, but if you break it one twig at a time, everything will work out.

The fourth stage of the software environment design method is to write the created algorithm as a program in a specific programming language.

At the fifth stage, the program is tested and debugged. It consists of finding all kinds of errors and allowing the program to work correctly. In the process of identifying errors, “manual program debugging” is used. It consists of mentally executing each step of the algorithm that solves its problem (as the computer will subsequently do this), and allows you to make sure that this algorithm will function properly.

Also, in the process of testing the program, a “reference solution” is used. This is a solution to a given problem using another method (for example, mathematically), which allows you to obtain obviously correct calculation results. Moreover, the reference solution uses not single data, but a variety of different data that allows covering all possible calculation situations. Based on such a “reference solution”, it is possible to identify all possible pitfalls and program errors or situations in which the program will not work properly. You also need to implement “foolproofing” in your program. This is when situations for which the program is not intended in principle are taken into account. For example, entering numbers into a field or variable designed to store a last name.

Some situations and errors cannot be identified even on the basis of a “reference solution”. Such errors appear only during long-term use of the program with a lot of input data. It is at the sixth stage that they are corrected, as well as the program is changed in accordance with changed government regulations or company policies.

When writing programs, you must also follow other rules, which will be discussed below.

Variables

Variable names

In almost all programs we have to use variables, sometimes even a lot of variables. The main thing is not to get confused later on which variable stores what data. To do this, you need to give the variable names meaningful names. The only exceptions can be variables used in loops, but not repeatedly involved in calculations.

For i = 1 to 10 Step 1
For j = 1 to 10 Step 1
dmsTable(i, j) = i * j
Next j
Next i

If we need to save someone's year of birth into a variable. Name the variable "vblYearsBorn" rather than "A". Try to ensure that the variable name reflects the essence of the data it is intended to store, so that it is intuitive. This may make the variable name a little long, but it will prevent you from getting confused and eliminate reuse of a given variable in other calculations for which it is not intended. It is advisable to start the variable name with capital letters vbl, from the English variable (variable). This is especially true in Object-Oriented Programming (hereinafter referred to as OOP). Since the name of a variable can be confused with the name of some object on the form (this will be discussed below).

For clarity, let's call these three initial letters “Object Identifier”, since further on I will mention them more than once.

After the identifier, without spaces, you should write the variable name. It needs to start with a capital letter so that when viewing the program listing you can see that this is a variable or some other object, so that the letters stand out and do not merge into one line.

Declaring Variables

Some programming languages ​​(for example, Visual Basic) allow you to work with variables without declaring them in the program code. I think this is a big mistake and not a solid programming language (but this does not mean that this programming language is bad)!

Declaring a variable is defining its type and name.

Dim vblFirstName as String (Visual Basic)
Var vblFirstName: String; (Turbo Pascal)
Char vblFirstName; (C++)

Those. we kind of indicate to the program that we will use a variable named vblFirstName and the data type of this variable is String (text/literal).

Why is this important (this only applies to those programming languages ​​that allow this. For example, if you do not declare a variable in C++ or Turbo Pascal, an error will be generated during compilation that the variable being used is not declared)? Everything is very simple. If we do not declare a variable, it will automatically be assigned the Variant type, this means that data of almost any type can be stored in the variable. Firstly, we will be able to write numerical data into the variable that stores the last name, or vice versa. IT IS NOT RIGHT! Since the surname cannot contain numbers. We deliberately create a gap in the program, an opportunity for making a mistake. These are the types of errors that hackers use to hack into systems and so on. Secondly, the type assigned automatically takes up a lot of space in random access memory. A good program should weigh as little as possible. And it doesn’t matter how many gigabytes of RAM you have on your computer. Thirdly, explicit declaration of variables will allow you to assign them the data type that you need. And it will be much easier for you to find out which variables are already used in the program. It will be enough to look at the beginning of the program code or module to see which variables have already been set, rather than going through the entire program code. You can never re-declar an already declared variable in the same module, or mix up their names, which means you don’t use the variable in calculations for which it is not intended.

OOP allows you to declare variables with the same names multiple times, provided that these variables are local and are used only in different modules. The visibility of variables will be discussed below.

Initializing Variables

After you have declared a variable, it must be initialized, i.e. assign it some value or reset it to zero. This is very important for variables used in calculations. The fact is that when a variable is declared, memory is allocated (reserved) for it. Memory reservation does not clear cells of the values ​​that were previously stored in them, so if the declaration of a variable is not followed by its initialization, then the current value of this variable will be unpredictable, and not zero, as many people think. It is not necessary that this is exactly what will happen, but if this happens, the reason for incorrect calculations is sometimes difficult to identify, since the program code is correct syntactically and logically, but the calculations are still incorrect.

If this is a variable of a numeric type and is used to accumulate a sum, then it is enough to simply reset it to zero.

vblSum = 0
For I = 1 to 10 Step 1
vblSum = vblSum + i
Next i

Or assign one to it if the variable is used as a multiplier or divisor.

vblSum = 1
For I = 1 to 10 Step 1
vblSum = vblSum * i
Next i

If it's a string variable just clean it up.

vblFirstName = ""

Personally, I always initialize variables, even if I assign it a different value on the next line.

Global and local variables

All variables have their own scope depending on how you declared them. Variables can be local or global.

Local variables- these are variables declared inside a function (subroutine). They are visible only within a given function and cannot be called directly from the main program text. When program execution returns from a function to the main code of the program or other function, local variables are removed from memory.

Global Variables- these are variables defined outside the body of a function (for OOP, variables declared in project modules). These variables have global scope and are accessible from any procedure, function, or subroutine.

Errors often occur when using global variables.

In some programming languages, a global variable name that has the same name as a local variable can change each other's values.

The danger of using global variables comes from their public nature, which means that one function can change the value of a variable without another function noticing. In such situations, errors may appear that are very difficult to identify.

Use global variables only when absolutely necessary, when it is impossible or problematic to use other methods.

Constants

About constants in just a nutshell. You also need to be careful when using them in a program (as when doing programming in general). It is best to start names of constants with three letters con, from the English constant (constant).

Const conPi = 3.14159265

Remember that the values ​​of constants cannot be changed in the program (after all, they are constants, which means permanent), otherwise an error will be generated. It is best to use constants when there is a value that is involved in many calculations and its value never changes in the program. Or when it is necessary to use a coefficient (and they, as a rule, are also constant).

Structured programming

Structured programming- a disciplined approach to programming that ensures the creation of programs that are easy to understand and reduces the likelihood of errors. The essence of this approach is to respect the generally accepted programming style and ensure the readability of the source text. You probably noticed that all the program code given in the examples is written with a specific structure. This is it structured programming.

The statements at the beginning and end of the cycle are written strictly below each other, and all statements inside the cycle are slightly to the right. All indentations are made using tabs (Tab key). They are written in exactly the same way logic. Writing this way makes your program more readable and easier to understand. It also makes it easier to debug the program. You can compare the example below, writing code using the principle of structured programming and without it. The example is given in Turbo Pascal (part of the array sorting code).

Example "Structured Code":

For i:=1 to 9 do
begin
vblMin:=A[i];
k:=i;
For j:=1+i to 10 do
begin
If (vblMin>A[j]) Then
begin
vblMin:=A[j];
k:=j;
end;
end;
vblStuf:=A[i];
A[i]:=vblMin;
A[k]:=vblStuf;
end;

Example "Normal Code":

For i:=1 to 9 do
begin
vblMin:=A[i];
k:=i;
For j:=1+i to 10 do
begin
If (vblMin>A[j]) Then
begin
vblMin:=A[j];
k:=j;
end;
end;
stuf:=A[i];
A[i]:=vblMin;
A[k]:=vblStuf;
end;

Which option is more readable and understandable? Undoubtedly the first.

You must accustom yourself to observe all the nuances described above. Of course, non-compliance is not critical, but using these methods will make it much easier for you to write programs and maintain them in the future.

Errors

Programs very rarely work correctly the first time. Murphy's Law, which states, "If something bad can happen, it will," seems to have been written specifically for computer programs.

Errors that occur during program operation can be divided into several types:

1. Syntax errors
2. Runtime errors
3. Logical errors

Syntax errors occur when source The program is written in violation of one of the grammar rules of the programming language in which you are writing the program. This violation is detected when trying to compile the program.

Run-time errors are detected by the computer while the program is running. Such errors occur when a program instructs the computer to perform an incorrect operation (such as dividing by zero or manipulating undescribed or incorrect data).

Data entry errors are also runtime errors. The cause of such errors is an attempt to enter data of the wrong type (for example, a variable of an integer type is assigned a string parameter or a number of a real type).

Logical errors occur when a program executes an incorrect algorithm. These errors are the most insidious, they are very difficult to identify, because they are not recorded during compilation. These errors appear when the program outputs incorrect results. Only possible ways identifying such errors is the use of methods: “reference solution” and “manual debugging”.

Skillful use of all these methods and rules: decomposition method, structured programming, software design method, reference solution and others speak of professionalism.

The computer is always right

The most annoying situation in programming is when the code is correct but doesn't work. “ Yes, there are three lines, damn it, there’s simply nowhere to make a mistake! Probably a bug! I'll go spend three days studying the bug reports of the compiler/interpreter/framework..." It feels like the computer is mocking you!

The main thing to remember here is that there is an error in these three lines. If the code does not work correctly, then the code is written incorrectly. Dot. Only you are to blame. Universal advice - go to sleep! Or at least take a break with a cup of tea. When, after some time, you return to the code, it will probably become clear that there is an extra negation operator, or two variables with similar names are mixed up, or some other little thing that we will never admit to anyone.

Calm down and everything will work out

Emotions are our worst enemy. Personally, as you may have guessed by the number of exclamation marks, I am an emotional person. And sometimes it is difficult for me to concentrate on the code, especially if this code was not written by me, and the code was not written by myself best quality. The brain somehow automatically switches to developing particularly sophisticated methods of torture for the code author.
You need to force yourself to calm down. You need to accept the task not as a mockery of your brain, but as a challenge. Yes the code is bad, yes there is no documentation, yes it is difficult, but I'm a programmer, this is part of my job and I can handle it.

The hardest thing is to start

Sometimes you look at a problem and don’t know how to approach it. Which side should I start with? And in general, I’m kind of lazy today. “I’ll sit on Vkontakte for 10 minutes, then I’ll start. Well, after coffee. Well, the old code needs to be refactored, and then I’ll start. What kind of low priority task is this? I’ll complete it and definitely start...”.
Just start. Start from either end. Break the task into small parts and start doing them. Stop procrastinating, discard extraneous thoughts, concentrate on the task, and start working. From there it will go like clockwork.

Read books

Read books. I will write again: Read books!
For some reason, many programmers completely ignore books. “ I’m also very enlightened at work.”, “I have no time”, “I read articles on the Internet" This is all great, but personally I believe that the best source of knowledge is still books. I consistently buy one or two books a month, plus from time to time I re-read something old. I won't lie I have an impressive stack of stuff on my shelf that I bought but haven’t read yet (like with games on Steam), but I will get there, I will definitely get there.

Know your tools

Take the time to study in detail the tools and technologies you work with. This will pay for itself many times over. Thoroughly study all the features of the language in which you program. Just go ahead and read the official documentation from cover to cover. Don't just use an IDE as a code editor, any modern environment has tons of tools to improve your code quality and your productivity. Don't use the framework only as a skeleton architecture. Learn it and it will save you a lot of time. Understand the intricacies of the version control system. The better you know your tools, the more work they do it for you.

Don't be a perfectionist

I wrote above that the most difficult thing is to start. So, finishing is not always easy either. You can debug and refactor the code endlessly. “ What is this long method?”, “Maybe this should be in a separate class?”, “It would be more convenient if...”, “What if you need it later...”, “But what if..." You can't be a perfectionist in programming. The problem is that all you have to do is read Robert Martin or the Gang of Four, and you immediately feel the urge to rewrite your entire code. You need to understand that there is no perfect code. I adhere to the rule: “ The code must work without bugs, be testable and readable" All. As long as the method code meets this requirement, I don't touch it. Even if it has two cycles, three conditional operator and four parameters.

Know how to rest

The biggest problem with programmers is that we love our work. I'm starting to feel sick on vacation, a week without programming - and I have a dream about how I'm thinking through the architecture of a new application. Plus, it’s not difficult for a programmer to find orders on the side that can be dealt with at night. Plus my projects. How many times have you been unable to sleep because your brain refused to switch from the task you've been doing all day?
All this leads to overwork, and, as a rule, to a decrease in productivity. A rested programmer is an effective programmer. Get enough sleep. Find yourself a hobby that has nothing to do with brain activity and devote a couple of hours a day to it. This will allow you to distract your brain from work and reboot it. The most interesting ideas and the best decisions have recently come to me in the gym.