SlideShare a Scribd company logo
Lesson 13. Pattern 5. Address arithmetic
We have chosen the 13-th lesson to discuss the errors related to address arithmetic deliberately. The
errors related to pointer arithmetic in 64-bit systems are the most insidious and it would be good that
number 13 made you more attentive.

The main idea of the pattern is - use only memsize-types in address arithmetic to avoid errors in 64-bit
code.

Consider this code:

unsigned short a16, b16, c16;

char *pointer;

...

pointer += a16 * b16 * c16;

This sample works correctly with pointers if the result of the expression "a16 * b16 * c16" does not
exceed INT_MAX (2147483647). This code could always work correctly on a 32-bit platform, because on
the 32-bit architecture a program does not have so much memory to create an array of such a size. On
the 64-bit architecture, this limitation has been removed and the size of the array may well get larger
than INT_MAX items. Suppose we want to shift the value of the pointer in 6.000.000.000 bytes, so the
variables a16, b16 and c16 have the values 3000, 2000 and 1000 respectively. When calculating the
expression "a16 * b16 * c16", all the variables will be cast to "int" type at first, according to C++ rules,
and only then they will be multiplied. An overflow will occur during the multiplication. The incorrect
result will be extended to the type ptrdiff_t and the pointer will be calculated incorrectly.

You should be very attentive and avoid possible overflows when dealing with pointer arithmetic. It is
good to use memsize-types or explicit type conversions in those expressions that contain pointers. Using
an explicit type conversion we may rewrite our code sample in the following way:

short a16, b16, c16;

char *pointer;

...

pointer += static_cast<ptrdiff_t>(a16) *

                static_cast<ptrdiff_t>(b16) *

                static_cast<ptrdiff_t>(c16);

If you think that inaccurately written programs encounter troubles only when dealing with large data
amounts, we have to disappoint you. Consider an interesting code sample working with an array that
contains just 5 items. This code works in the 32-bit version and does not work in the 64-bit one:

int A = -2;

unsigned B = 1;
int array[5] = { 1, 2, 3, 4, 5 };

int *ptr = array + 3;

ptr = ptr + (A + B); //Invalid pointer value on 64-bit platform

printf("%in", *ptr); //Access violation on 64-bit platform

Let us follow the algorithm of calculating the expression "ptr + (A + B)":

    •   According to C++ rules, the variable A of the type int is converted to unsigned.
    •   A and B are summed and we get the value 0xFFFFFFFF of unsigned type.
    •   The expression "ptr + 0xFFFFFFFFu" is calculated.

The result of this process depends upon the size of the pointer on a particular architecture. If the
addition takes place in the 32-bit program, the expression is equivalent to "ptr - 1" and the program
successfully prints the value "3". In the 64-bit program, the value 0xFFFFFFFFu is fairly added to the
pointer. As a result, the pointer gets far outside the array while we encounter some troubles when
trying to get access to the item by this pointer.

Like in the first case, we recommend you to use only memsize-types in pointer arithmetic to avoid the
situation described above. Here are two ways to correct the code:

ptr = ptr + (ptrdiff_t(A) + ptrdiff_t(B));

ptrdiff_t A = -2;

size_t B = 1;

...

ptr = ptr + (A + B);

You may argue and propose this way:

int A = -2;

int B = 1;

...

ptr = ptr + (A + B);

Yes, this code can work but it is bad due to some reasons:

    •   It trains programmers to be inaccurate when working with pointers. You might forget all the
        details of the code some time later and again redefine one of the variables with unsigned type
        by mistake.
    •   It is potentially dangerous to use non-memsize types together with the pointers. Suppose the
        variable Delta of int type participates in an expression with a pointer. This expression is quite
        correct. But an error may hide in the process of calculating the variable Delta because 32 bits
        might be not enough to perform the necessary calculations while working with large data arrays.
        You can automatically avoid this danger by using a memsize-type for the variable Delta.
•   A code that uses the types size_t, ptrdiff_t and other memsize-types when working with
        pointers leads to a more appropriate binary code. We will speak about it in one of the following
        lessons.


Array indexing
We single out this type of errors to make our description more structured because array indexing with
the use of square brackets is just another way of writing the address arithmetic we have discussed
above.

You may encounter errors related to indexing large arrays or eternal loops in programs that process
large amounts of data. The following example contains 2 errors at once:

const size_t size = ...;

char *array = ...;

char *end = array + size;

for (unsigned i = 0; i != size; ++i)

{

    const int one = 1;

    end[-i - one] = 0;

}

The first error lies in the fact that an eternal loop may occur if the size of the processed data exceeds 4
Gbytes (0xFFFFFFFF), because the variable "i" has "unsigned" type and will never reach a value larger
than 0xFFFFFFFF. It is possible but not certain - it depends upon the code the compiler will build. For
example, there will be no eternal loop in the debug mode while it will completely disappear in the
release version, because the compiler will decide to optimize the code using the 64-bit register for the
counter and the loop will become correct. All this adds confusion and a code that was good yesterday
stops working today.

The second error is related to negative values of the indexes serving to walk the array from end to
beginning. This code works in the 32-bit mode but crashes in the 64-bit one right with the first iteration
of the loop as an access outside the array's bounds occurs. Let us consider the cause of this behavior.

Although everything written below is the same as in the example with "ptr = ptr + (A + B)", we resort to
this repetition deliberately. We need to show you that a danger may hide even in simple constructs and
take various forms.

According to C++ rules, the expression "-i - one" will be calculated on a 32-bit system in the following
way (i = 0 at the first step):

    1. The expression "-i" has "unsigned" type and equals 0x00000000u.
    2. The variable "one" is extended from the type "int" to the type "unsigned" and equals
       0x00000001u. Note: the type "int" is extended (according to C++ standard) to the type
       "unsigned" if it participates in an operation where the second argument has the type
       "unsigned".
3. Two values of the type "unsigned" participate in a subtraction operation and its result equals
        0x00000000u - 0x00000001u = 0xFFFFFFFFu. Note that the result has "unsigned" type.

On a 32-bit system, calling an array by the index 0xFFFFFFFFu is equivalent to using the index "-1". I.e.
end[0xFFFFFFFFu] is analogous to end[-1]. As a result, the array's item is processed correctly. But the
picture will be different in a 64-bit system: the type "unsigned" will be extended to the signed "ptrdiff_t"
and the array's index will equal 0x00000000FFFFFFFFi64. It results in an overflow.

To correct the code you need to use such types as ptrdiff_t and size_t.

To completely convince you that you should use only memsize-types for indexing and in address
arithmetic expressions, here is the code sample for you to consider.

class Region {

    float *array;

    int Width, Height, Depth;

    float Region::GetCell(int x, int y, int z) const;

    ...

};

float Region::GetCell(int x, int y, int z) const {

    return array[x + y * Width + z * Width * Height];

}

This code is taken from a real program of mathematical modeling where the amount of memory is the
most important resource, so the capability of using more than 4 Gbytes on a 64-bit architecture
significantly increases the computational power. Programmers often use one-dimensional arrays in
programs like this to save memory while treating them as three-dimensional arrays. For this purpose,
they use functions analogous to GetCell which provide access to the necessary items. But the code
above will work correctly only with arrays that contain less than INT_MAX items because it is 32-bit "int"
types that are used to calculate the item's index.

Programmers often make a mistake trying to correct the code in this way:

float Region::GetCell(int x, int y, int z) const {

    return array[static_cast<ptrdiff_t>(x) + y * Width +

                       z * Width * Height];

}

They know that, according to C++ rules, the expression to calculate the index has the type "ptrdiff_t"
and hope to avoid an overflow thereby. But the overflow may occur inside the expression "y * Width" or
"z * Width * Height" because it is still the type "int" which is used to calculate them.

If you want to correct the code without changing the types of the variables participating in the
expression, you may explicitly convert each variable to a memsize-type:
float Region::GetCell(int x, int y, int z) const {

    return array[ptrdiff_t(x) +

                      ptrdiff_t(y) * ptrdiff_t(Width) +

                      ptrdiff_t(z) * ptrdiff_t(Width) *

                      ptrdiff_t(Height)];

}

Another - better - solution is to change the types of the variables to a memsize-type:

typedef ptrdiff_t TCoord;

class Region {

    float *array;

    TCoord Width, Height, Depth;

    float Region::GetCell(TCoord x, TCoord y, TCoord z) const;

    ...

};

float Region::GetCell(TCoord x, TCoord y, TCoord z) const {

    return array[x + y * Width + z * Width * Height];

}


Diagnosis
Address arithmetic errors are well diagnosed by PVS-Studio tool. The analyzer warns you about
potentially dangerous expressions with the diagnostic warnings V102 and V108.

When possible, the analyzer tries to understand when a non-memsize type used in address arithmetic is
safe and refuse from generating a warning on this fragment. As a result, the analyzer's behavior may
seem strange. In such cases we ask users to take their time and examine the situation. Consider the
following code:

char Arr[] = { '0', '1', '2', '3', '4' };

char *p = Arr + 2;

cout << p[0u + 1] << endl;

cout << p[0u - 1] << endl; //V108

This code works correctly in the 32-bit mode and prints numbers 3 and 1 on the screen. On testing this
code we get a warning only on one string with the expression "p[0u - 1]". And this warning is quite right!
If you compile and launch this code sample in the 64-bit mode, you will see the value 3 printed on the
screen and the program will crash right after it.
If you are sure that the indexing is correct, you may change the corresponding parameter of the analyzer
on the settings tab Settings: General or use filters. You may also use an explicit type conversion.

The course authors: Andrey Karpov (karpov@viva64.com), Evgeniy Ryzhkov (evg@viva64.com).

The rightholder of the course "Lessons on development of 64-bit C/C++ applications" is OOO "Program
Verification Systems". The company develops software in the sphere of source program code analysis.
The company's site: http://www.viva64.com.

Contacts: e-mail: support@viva64.com, Tula, 300027, PO box 1800.

More Related Content

PDF
Development of a static code analyzer for detecting errors of porting program...
PDF
C++11 and 64-bit Issues
PDF
Computer
PDF
Lesson 11. Pattern 3. Shift operations
PDF
Undefined behavior is closer than you think
PDF
Comparison of analyzers' diagnostic possibilities at checking 64-bit code
PDF
GSP 215 Effective Communication - tutorialrank.com
PDF
C Programming Interview Questions
Development of a static code analyzer for detecting errors of porting program...
C++11 and 64-bit Issues
Computer
Lesson 11. Pattern 3. Shift operations
Undefined behavior is closer than you think
Comparison of analyzers' diagnostic possibilities at checking 64-bit code
GSP 215 Effective Communication - tutorialrank.com
C Programming Interview Questions

What's hot (16)

PDF
Flag Waiving
PPS
C programming session 05
PPTX
Memory management of datatypes
DOCX
GSP 215 Education Organization - snaptutorial.com
DOC
Gsp 215 Enthusiastic Study / snaptutorial.com
DOCX
Manoch1raw 160512091436
DOC
Gsp 215 Enhance teaching-snaptutorial.com
DOC
Gsp 215 Exceptional Education / snaptutorial.com
DOC
Gsp 215 Believe Possibilities / snaptutorial.com
PDF
[ITP - Lecture 04] Variables and Constants in C/C++
PPTX
Intermediate code representations
PPTX
Intermediate code
DOC
Gsp 215 Future Our Mission/newtonhelp.com
PPTX
COMPILER DESIGN AND CONSTRUCTION
PDF
[ITP - Lecture 05] Datatypes
PDF
Symbology upc
Flag Waiving
C programming session 05
Memory management of datatypes
GSP 215 Education Organization - snaptutorial.com
Gsp 215 Enthusiastic Study / snaptutorial.com
Manoch1raw 160512091436
Gsp 215 Enhance teaching-snaptutorial.com
Gsp 215 Exceptional Education / snaptutorial.com
Gsp 215 Believe Possibilities / snaptutorial.com
[ITP - Lecture 04] Variables and Constants in C/C++
Intermediate code representations
Intermediate code
Gsp 215 Future Our Mission/newtonhelp.com
COMPILER DESIGN AND CONSTRUCTION
[ITP - Lecture 05] Datatypes
Symbology upc
Ad

Viewers also liked (8)

PDF
Lightning Talk #9: How UX and Data Storytelling Can Shape Policy by Mika Aldaba
PDF
SEO: Getting Personal
PDF
Succession “Losers”: What Happens to Executives Passed Over for the CEO Job?
PDF
The impact of innovation on travel and tourism industries (World Travel Marke...
PDF
Open Source Creativity
PPSX
Reuters: Pictures of the Year 2016 (Part 2)
PDF
The Six Highest Performing B2B Blog Post Formats
PDF
The Outcome Economy
Lightning Talk #9: How UX and Data Storytelling Can Shape Policy by Mika Aldaba
SEO: Getting Personal
Succession “Losers”: What Happens to Executives Passed Over for the CEO Job?
The impact of innovation on travel and tourism industries (World Travel Marke...
Open Source Creativity
Reuters: Pictures of the Year 2016 (Part 2)
The Six Highest Performing B2B Blog Post Formats
The Outcome Economy
Ad

Similar to Lesson 13. Pattern 5. Address arithmetic (20)

PDF
Lesson 17. Pattern 9. Mixed arithmetic
PDF
Lesson 24. Phantom errors
PDF
Optimization in the world of 64-bit errors
PDF
A Collection of Examples of 64-bit Errors in Real Programs
PDF
The article is a report about testing of portability of Loki library with 64-...
PDF
A 64-bit horse that can count
PDF
A collection of examples of 64 bit errors in real programs
PDF
A Collection of Examples of 64-bit Errors in Real Programs
PDF
Zero, one, two, Freddy's coming for you
PDF
Safety of 64-bit code
PDF
Lesson 9. Pattern 1. Magic numbers
PDF
Monitoring a program that monitors computer networks
PDF
20 issues of porting C++ code on the 64-bit platform
PDF
20 issues of porting C++ code on the 64-bit platform
PDF
Program errors occurring while porting C++ code from 32-bit platforms on 64-b...
PDF
How to make fewer errors at the stage of code writing. Part N1.
PDF
How to make fewer errors at the stage of code writing. Part N1
PPTX
Things to Remember When Developing 64-bit Software
PDF
How to make fewer errors at the stage of code writing. Part N1.
PDF
About size_t and ptrdiff_t
Lesson 17. Pattern 9. Mixed arithmetic
Lesson 24. Phantom errors
Optimization in the world of 64-bit errors
A Collection of Examples of 64-bit Errors in Real Programs
The article is a report about testing of portability of Loki library with 64-...
A 64-bit horse that can count
A collection of examples of 64 bit errors in real programs
A Collection of Examples of 64-bit Errors in Real Programs
Zero, one, two, Freddy's coming for you
Safety of 64-bit code
Lesson 9. Pattern 1. Magic numbers
Monitoring a program that monitors computer networks
20 issues of porting C++ code on the 64-bit platform
20 issues of porting C++ code on the 64-bit platform
Program errors occurring while porting C++ code from 32-bit platforms on 64-b...
How to make fewer errors at the stage of code writing. Part N1.
How to make fewer errors at the stage of code writing. Part N1
Things to Remember When Developing 64-bit Software
How to make fewer errors at the stage of code writing. Part N1.
About size_t and ptrdiff_t

Recently uploaded (20)

PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Electronic commerce courselecture one. Pdf
PPTX
Cloud computing and distributed systems.
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
Spectral efficient network and resource selection model in 5G networks
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PPT
Teaching material agriculture food technology
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Review of recent advances in non-invasive hemoglobin estimation
PPTX
MYSQL Presentation for SQL database connectivity
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Electronic commerce courselecture one. Pdf
Cloud computing and distributed systems.
The AUB Centre for AI in Media Proposal.docx
Spectral efficient network and resource selection model in 5G networks
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Mobile App Security Testing_ A Comprehensive Guide.pdf
Network Security Unit 5.pdf for BCA BBA.
MIND Revenue Release Quarter 2 2025 Press Release
Teaching material agriculture food technology
NewMind AI Weekly Chronicles - August'25 Week I
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
Digital-Transformation-Roadmap-for-Companies.pptx
Building Integrated photovoltaic BIPV_UPV.pdf
Review of recent advances in non-invasive hemoglobin estimation
MYSQL Presentation for SQL database connectivity
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
20250228 LYD VKU AI Blended-Learning.pptx

Lesson 13. Pattern 5. Address arithmetic

  • 1. Lesson 13. Pattern 5. Address arithmetic We have chosen the 13-th lesson to discuss the errors related to address arithmetic deliberately. The errors related to pointer arithmetic in 64-bit systems are the most insidious and it would be good that number 13 made you more attentive. The main idea of the pattern is - use only memsize-types in address arithmetic to avoid errors in 64-bit code. Consider this code: unsigned short a16, b16, c16; char *pointer; ... pointer += a16 * b16 * c16; This sample works correctly with pointers if the result of the expression "a16 * b16 * c16" does not exceed INT_MAX (2147483647). This code could always work correctly on a 32-bit platform, because on the 32-bit architecture a program does not have so much memory to create an array of such a size. On the 64-bit architecture, this limitation has been removed and the size of the array may well get larger than INT_MAX items. Suppose we want to shift the value of the pointer in 6.000.000.000 bytes, so the variables a16, b16 and c16 have the values 3000, 2000 and 1000 respectively. When calculating the expression "a16 * b16 * c16", all the variables will be cast to "int" type at first, according to C++ rules, and only then they will be multiplied. An overflow will occur during the multiplication. The incorrect result will be extended to the type ptrdiff_t and the pointer will be calculated incorrectly. You should be very attentive and avoid possible overflows when dealing with pointer arithmetic. It is good to use memsize-types or explicit type conversions in those expressions that contain pointers. Using an explicit type conversion we may rewrite our code sample in the following way: short a16, b16, c16; char *pointer; ... pointer += static_cast<ptrdiff_t>(a16) * static_cast<ptrdiff_t>(b16) * static_cast<ptrdiff_t>(c16); If you think that inaccurately written programs encounter troubles only when dealing with large data amounts, we have to disappoint you. Consider an interesting code sample working with an array that contains just 5 items. This code works in the 32-bit version and does not work in the 64-bit one: int A = -2; unsigned B = 1;
  • 2. int array[5] = { 1, 2, 3, 4, 5 }; int *ptr = array + 3; ptr = ptr + (A + B); //Invalid pointer value on 64-bit platform printf("%in", *ptr); //Access violation on 64-bit platform Let us follow the algorithm of calculating the expression "ptr + (A + B)": • According to C++ rules, the variable A of the type int is converted to unsigned. • A and B are summed and we get the value 0xFFFFFFFF of unsigned type. • The expression "ptr + 0xFFFFFFFFu" is calculated. The result of this process depends upon the size of the pointer on a particular architecture. If the addition takes place in the 32-bit program, the expression is equivalent to "ptr - 1" and the program successfully prints the value "3". In the 64-bit program, the value 0xFFFFFFFFu is fairly added to the pointer. As a result, the pointer gets far outside the array while we encounter some troubles when trying to get access to the item by this pointer. Like in the first case, we recommend you to use only memsize-types in pointer arithmetic to avoid the situation described above. Here are two ways to correct the code: ptr = ptr + (ptrdiff_t(A) + ptrdiff_t(B)); ptrdiff_t A = -2; size_t B = 1; ... ptr = ptr + (A + B); You may argue and propose this way: int A = -2; int B = 1; ... ptr = ptr + (A + B); Yes, this code can work but it is bad due to some reasons: • It trains programmers to be inaccurate when working with pointers. You might forget all the details of the code some time later and again redefine one of the variables with unsigned type by mistake. • It is potentially dangerous to use non-memsize types together with the pointers. Suppose the variable Delta of int type participates in an expression with a pointer. This expression is quite correct. But an error may hide in the process of calculating the variable Delta because 32 bits might be not enough to perform the necessary calculations while working with large data arrays. You can automatically avoid this danger by using a memsize-type for the variable Delta.
  • 3. A code that uses the types size_t, ptrdiff_t and other memsize-types when working with pointers leads to a more appropriate binary code. We will speak about it in one of the following lessons. Array indexing We single out this type of errors to make our description more structured because array indexing with the use of square brackets is just another way of writing the address arithmetic we have discussed above. You may encounter errors related to indexing large arrays or eternal loops in programs that process large amounts of data. The following example contains 2 errors at once: const size_t size = ...; char *array = ...; char *end = array + size; for (unsigned i = 0; i != size; ++i) { const int one = 1; end[-i - one] = 0; } The first error lies in the fact that an eternal loop may occur if the size of the processed data exceeds 4 Gbytes (0xFFFFFFFF), because the variable "i" has "unsigned" type and will never reach a value larger than 0xFFFFFFFF. It is possible but not certain - it depends upon the code the compiler will build. For example, there will be no eternal loop in the debug mode while it will completely disappear in the release version, because the compiler will decide to optimize the code using the 64-bit register for the counter and the loop will become correct. All this adds confusion and a code that was good yesterday stops working today. The second error is related to negative values of the indexes serving to walk the array from end to beginning. This code works in the 32-bit mode but crashes in the 64-bit one right with the first iteration of the loop as an access outside the array's bounds occurs. Let us consider the cause of this behavior. Although everything written below is the same as in the example with "ptr = ptr + (A + B)", we resort to this repetition deliberately. We need to show you that a danger may hide even in simple constructs and take various forms. According to C++ rules, the expression "-i - one" will be calculated on a 32-bit system in the following way (i = 0 at the first step): 1. The expression "-i" has "unsigned" type and equals 0x00000000u. 2. The variable "one" is extended from the type "int" to the type "unsigned" and equals 0x00000001u. Note: the type "int" is extended (according to C++ standard) to the type "unsigned" if it participates in an operation where the second argument has the type "unsigned".
  • 4. 3. Two values of the type "unsigned" participate in a subtraction operation and its result equals 0x00000000u - 0x00000001u = 0xFFFFFFFFu. Note that the result has "unsigned" type. On a 32-bit system, calling an array by the index 0xFFFFFFFFu is equivalent to using the index "-1". I.e. end[0xFFFFFFFFu] is analogous to end[-1]. As a result, the array's item is processed correctly. But the picture will be different in a 64-bit system: the type "unsigned" will be extended to the signed "ptrdiff_t" and the array's index will equal 0x00000000FFFFFFFFi64. It results in an overflow. To correct the code you need to use such types as ptrdiff_t and size_t. To completely convince you that you should use only memsize-types for indexing and in address arithmetic expressions, here is the code sample for you to consider. class Region { float *array; int Width, Height, Depth; float Region::GetCell(int x, int y, int z) const; ... }; float Region::GetCell(int x, int y, int z) const { return array[x + y * Width + z * Width * Height]; } This code is taken from a real program of mathematical modeling where the amount of memory is the most important resource, so the capability of using more than 4 Gbytes on a 64-bit architecture significantly increases the computational power. Programmers often use one-dimensional arrays in programs like this to save memory while treating them as three-dimensional arrays. For this purpose, they use functions analogous to GetCell which provide access to the necessary items. But the code above will work correctly only with arrays that contain less than INT_MAX items because it is 32-bit "int" types that are used to calculate the item's index. Programmers often make a mistake trying to correct the code in this way: float Region::GetCell(int x, int y, int z) const { return array[static_cast<ptrdiff_t>(x) + y * Width + z * Width * Height]; } They know that, according to C++ rules, the expression to calculate the index has the type "ptrdiff_t" and hope to avoid an overflow thereby. But the overflow may occur inside the expression "y * Width" or "z * Width * Height" because it is still the type "int" which is used to calculate them. If you want to correct the code without changing the types of the variables participating in the expression, you may explicitly convert each variable to a memsize-type:
  • 5. float Region::GetCell(int x, int y, int z) const { return array[ptrdiff_t(x) + ptrdiff_t(y) * ptrdiff_t(Width) + ptrdiff_t(z) * ptrdiff_t(Width) * ptrdiff_t(Height)]; } Another - better - solution is to change the types of the variables to a memsize-type: typedef ptrdiff_t TCoord; class Region { float *array; TCoord Width, Height, Depth; float Region::GetCell(TCoord x, TCoord y, TCoord z) const; ... }; float Region::GetCell(TCoord x, TCoord y, TCoord z) const { return array[x + y * Width + z * Width * Height]; } Diagnosis Address arithmetic errors are well diagnosed by PVS-Studio tool. The analyzer warns you about potentially dangerous expressions with the diagnostic warnings V102 and V108. When possible, the analyzer tries to understand when a non-memsize type used in address arithmetic is safe and refuse from generating a warning on this fragment. As a result, the analyzer's behavior may seem strange. In such cases we ask users to take their time and examine the situation. Consider the following code: char Arr[] = { '0', '1', '2', '3', '4' }; char *p = Arr + 2; cout << p[0u + 1] << endl; cout << p[0u - 1] << endl; //V108 This code works correctly in the 32-bit mode and prints numbers 3 and 1 on the screen. On testing this code we get a warning only on one string with the expression "p[0u - 1]". And this warning is quite right! If you compile and launch this code sample in the 64-bit mode, you will see the value 3 printed on the screen and the program will crash right after it.
  • 6. If you are sure that the indexing is correct, you may change the corresponding parameter of the analyzer on the settings tab Settings: General or use filters. You may also use an explicit type conversion. The course authors: Andrey Karpov (karpov@viva64.com), Evgeniy Ryzhkov (evg@viva64.com). The rightholder of the course "Lessons on development of 64-bit C/C++ applications" is OOO "Program Verification Systems". The company develops software in the sphere of source program code analysis. The company's site: http://www.viva64.com. Contacts: e-mail: support@viva64.com, Tula, 300027, PO box 1800.