C+ + error e2277

c+ + error e2277

Find answers to Lvalue required error when trying to find palindrome from the expert community at [C++ Error] sprers.eu(25): E Lvalue required. understand how to fix this. [C++ Error] sprers.eu(): E Lvalue required. void TFormPcher::ChangeBytes(char *FileName, char *OldBytes. [C++ Error] sprers.eu(): E Lvalue required. The solution in this case is just to comment out the sprers.eu lines and

C+ + error e2277 - pity, that

Hi guys, trying to use Borland C++ to create a program to read in a string value to find out if it's a  palindrome. Lots of examples online but none that are too useful. My code is as below but i keep getting the errors (as seen at the bottom of the page).
On the code i have marked out which lines the errors refer to.
I'm a newbie can you help, what am i doing wrong?
//
int main(int argc, char* argv[])
{
AnsiString Line;
int Front;
int Back ;
bool Palindrome(char *, int, int);
//Pseudo-Code
WriteString("Enter the string to be tested");
Line = ReadStringPr("Enter the string to be tested");
Front = 1;
Back = Length(Line);
Palindrome = true; <ERROR (1)
while ((Front < Back) & (Palindrome = true));  <ERROR (2 & 3)
{
if (Line[Front] == Line[Back])
{
Palindrome = false; <ERROR (4)
}
else
{
Front = Front + 1;
Back = Back - 1;
 }  //ifend
}  //loopend
WriteString("Line");
if (Palindrome = true)  <ERROR (5)
{
WriteString("is a palindrome");
}
else
{
WriteString("is not a palindrome");
}
getchar();
return 0;
}

ERROR MESSAGES

1. [C++ Error] sprers.eu(25): E Lvalue required
2. [C++ Error] sprers.eu(26): E Lvalue required
3. [C++ Warning] sprers.eu(26): W Code has no effect
4. [C++ Error] sprers.eu(30): E Lvalue required
5. [C++ Error] sprers.eu(39): E Lvalue required

Function Templates


Function Templates

  • A function template is a generic way of describing a function.
  • You define a function in terms of a generic type instead of a specific type.
  • This kind of programming is often referred to as generic programming.
  • Templates are also called parameterized types because you "pass a parameter" to the template at compile time and the compiler generates the appropriate code.
If we wanted a cube() function, this is how we would implement it using overloaded functions:
Overloaded functionsClient calls

 

 
int cube(int val) { return val * val * val; } long cube(long val) { return val * val * val; } float cube(float val) { return val * val * val; } double cube(double val) { return val * val * val; }
int i = cube(2); // cube(int), i is 8long l = cube(L); // cube(long), l is Lfloat f = cube(f); // cube(float), f is fdouble d = cube(e25); // cube(double), d is e+76
  • Because the compiler can distinguish among the various parameter types, it knows which function to call.
  • This makes it very convenient for the users: they just need to know about the cube function.
  • However, this is inconvenient on the programmer who must maintain many "similar" versions of the cube function.
  • It would be nice if we could specify one function that would be use for all different types.
  • Fundamentally, we want to reuse the algorithm.
  • We can do that and the solution is function templates.
This is our new cube function using a template:
template <typename T> T cube(T value) { return value * value * value; }
  • The keyword template indicates that the function is a template function.
  • In between the angle brackets we put in the "parameter" name using the typename keyword
  • The rest of the function is the same except that the types that were specified before (int, float, etc.) are now replaced with the name of our parameter, in this case, T.
  • We can use any name we want as the name of our parameter, just as with ordinary parameters.
  • The uppercase letter 'T' is a common name for the type name.
  • Also, the keyword class can be used in place of the typename keyword. (It should be supported by all compilers for backward compatibility.) Note that not all template types are classes.
Often, the function template is written with the template and typename keywords on a separate line above:
template <typename T> T cube(T value) { return value * value * value; }
Other notes:
  • The template above does not generate any code in the program.
  • Code is only generated when the cube function is actually used. (Called from some code.)
  • If we never call cube(), no code is generated for the function.
  • This automatic code generation by the compiler is called template instantiation.
  • However, if we do this in our program:
    int i = cube(2);
    then code similar to this is generated for us:
    int cube(int value) { return value * value * value; }
  • If we do this in our program:
    int i = cube(2); long l = cube(L); float f = cube(f); double d = cube(e25);
    then code similar to this is generated for us:
    int cube(int value) { return value * value * value; } long cube(long value) { return value * value * value; }
    float cube(float value) { return value * value * value; } double cube(double value) { return value * value * value; }
  • A template is a way of describing to the compiler how to generate functions (if and when they are needed).
  • You need to make sure that the compiler knows about your template functions before you call them. Most compilers require you to define them before using them.
  • The resulting generated executable program is not any smaller using templated functions.
  • The compiler generates (expands) all of the functions and these will be placed into the program.
  • The compiler is just "writing" the overloaded functions for you, based on the "template" you've given it.
  • All of the work is done implicitly, and the generation of code by the compiler is known as implicit instantiation. (As opposed to explicit instantiation. More on that later.)
Note that often the compiler can deduce the types of the arguments (and hence, the type of T) from the parameters. This is very different from class templates where the user must specify the type of class to instantiate. We'll see that sometimes the compile can't deduce all of the types for a function template.

User-Defined Types in Template Functions

Templates only provide some of the necessary functionality.
  • The idea behind a function template is that any sensible type should be able to be used.
  • The programmer still must ensure that the generated code is valid.
  • Most of the seemingly unintelligible errors generated from templates have nothing to do with the functions themselves.

Suppose we wanted to cube a StopWatch object:

StopWatch sw1(4); // Create a StopWatch set to 4 seconds StopWatch sw2; // Create a StopWatch set to 0 seconds sw2 = cube(sw1); // Cube the first StopWatch and assign to second cout << sw2 << endl; // ???
Will this compile? If so, what will it print out? To understand what's going on, look at what the compiler is generating:
StopWatch cube(StopWatch value) { return value * value * value; }
Will this cube function compile? Why or why not? The answer is: it depends.

StopWatchh
StopWatchcpp

If there is no overloaded , VC++ generates this error message in the template function:

template <typename T> T cube(T value) { return value * value * value; // error C binary '*' : no operator defined }
We need to ensure that there is an overloaded * operator that takes a StopWatch on the left and right side of the operator:
StopWatch StopWatch::operator*(const StopWatch &sw) const { return StopWatch(m_seconds * sw.m_seconds); }
Now, the following will compile:
StopWatch sw1(4); // Create a StopWatch set to 4 seconds StopWatch sw2; // Create a StopWatch set to 0 seconds sw2 = cube(sw1); // Cube the first StopWatch and assign to second cout << sw2 << endl; // Displays (4 * 4 * 4 = 64 seconds)

Examples:

As you look at these, ask yourself what methods need to be present in a user-defined class for these template functions to generate valid code.

Template:

template<typename T> void Display(const T *array, int size) { // Display each element in the array // on one line separated by commasfor (int i = 0; i < size; i++) { if (i > 0) std::cout << ", "; std::cout << array[i]; } std::cout << std::endl; }

Template:

template<typename T> int IndexOf(const T *array, T value, int size) { for (int i = 0; i < size; i++) // for each element in the arrayif (array[i] == value) // if the element matches the supplied valuereturn i; // return the index (we've found it).return -1; // return -1 to indicate we didn't find it. }
Use:
int a[] = {4, 6, 3, 8, 4, 5, 7}; int size = sizeof(a)/sizeof(*a); int i1 = IndexOf(a, 8, size); // 3int i2 = IndexOf(a, 2, size); // -1 StopWatch sw1[] = {StopWatch(60), StopWatch(90), StopWatch(0)}; int i3 = IndexOf(sw1, StopWatch(), 3); // -1int i4 = IndexOf(sw1, StopWatch(0), 3); // 2 StopWatch s1(60), s2(90), s3(0); StopWatch *sw2[] = {&s1, &s2, &s3}; StopWatch s4(60); int i5 = IndexOf(sw2, &s4, 3); // ??? i5 = IndexOf(sw2, &s2, 3); // ???
Of course, we'll need to implement operator== for the class in order for the above to compile. Also, a better way to implement the function:
template<typename T> int IndexOf(const T *array, const T& value, int size)
With integers and other built-in types, there is no big difference. However, with user-defined types, there can be a very big difference. Keep this in mind when implementing templated functions.

Template:

template<typename T> T LargestValue(const T *array, int size) { T largest = array[0]; // assume the first element is the largestfor (int i = 1; i < size; i++) // traverse the array looking for larger onesif (array[i] > largest) // if this element is larger than the largest largest = array[i]; // make it the new largestreturn largest; // return the largest element found }
Use:
int a[] = {4, 6, 3, 8, 4, 5, 7}; StopWatch sw1[] = {StopWatch(60), StopWatch(90), StopWatch(0)}; int i = LargestValue(a, 7); // 8 StopWatch sw = LargestValue(sw1, 3); //
A possibly better implementation:
template<typename T> const T& LargestValue(const T *array, int size) { int index = 0; // assume the first element is the largestfor (int i = 1; i < size; i++) // traverse the array looking for larger onesif (array[i] > array[index]) // if this element is larger than the largest index = i; // make it the new largestreturn array[index]; // return the largest element found }
If we change the first implementation above to this:
template<typename T> T LargestValue(const T *array, int size) { T largest; largest = array[0]; // assume the first element is the largest// . . . }
How will this affect things?

We'll also need to implement operator> for the class in order for the above to compile.


Template:

template<typename T> int Replace(T *array, const T& oldvalue, const T& newvalue, int size) { int replacements = 0; // assume no replacementsfor (int i = 0; i < size; i++) // for each element in the array { if (array[i] == oldvalue) // if it matches the 'oldvalue' { array[i] = newvalue; // change it to the 'newvalue' replacements++; // inc counter } } return replacements; // return number of replacements }
Use:
int a[] = {4, 6, 3, 8, 4, 5, 7}; StopWatch sw1[] = {StopWatch(60), StopWatch(90), StopWatch(0)}; Replace(a, 6, 2, 7); // Before: 4, 6, 3, 8, 4, 5, 7 Replace(sw1, StopWatch(60), StopWatch(), 3); // Before: , , Display(a, 7); // 4, 2, 3, 8, 4, 5, 7 Display(sw1, 3); // , ,

Multiple Template Parameters

Suppose we want a generic Max function:
template <typename T> T Max(T a, T b) { return a > b ? a : b; }
we try to use it like this:
int i = Max(25, 10); // i = 25double d = Max(, ); // d = double e = Max(, 4); // error: no function `Max(double, int)'
We need to specify the type of both parameters in order to mix types:
template <typename T1, typename T2> T1 Max(T1 a, T2 b) { return a > b ? a : b; }
This leads to:
double d1 = Max(, 3); // d2 = double d2 = Max(3, ); // d1 = ???
Finally, add a third type:
template <typename T1, typename T2, typename T3> T1 Max(T2 a, T3 b) { return a > b ? a : b; }
This leads to:
double d1 = Max(3, ); // error: can't deduce template argument for 'T1'
Various solutions:
double d2 = Max<double, int, double>(3, ); // OK, explicitdouble d3 = Max<double, double, int>(, 3); // OK, explicitdouble d4 = Max<double>(3, ); // Ok, explicit, compiler deduces othersdouble d5 = Max<double, int, int>(, 3); // OK, explicit, but truncatingdouble d6 = Max<int, double, int>(, 3); // OK, explicit, but truncating
Suppose I made this subtle change:
template <typename T1, typename T2, typename T3> T3 Max(T1 a, T2 b) { return a > b ? a : b; }
How does that affect the code above? Other calls to ?

Overloaded Template Functions

Suppose we have this "universal" swap function:
template <typename T> void Swap(T &a, T &b) { T temp = a; a = b; b = temp; }
and a trivial use case:
int i = 10, j = 20; double d = , e = ; Swap(i, j); // i=20, j=10 Swap(d, e); // d=, e=
What is the result of this "Swap"?
int a[] = {1, 3, 5, 7, 9, 11}; int b[] = {2, 4, 6, 8, 10, 12}; Swap(a, b); // ???
  • Will this compile?
  • If so, what is actually swapped?

To get an idea of what's going on, look at what the instantiated function might look like:

void Swap(int a[6], int b[6]) { int temp[6] = a; // initializing an array with array a = b; // assigning an array b = temp; // assigning an array }
This produces errors because instantiates to :

g++:

sprers.eu: In function `void Swap(T&, T&) [with T = int[6]]': sprers.eu instantiated from here sprers.eu error: invalid initializer sprers.eu error: ISO C++ forbids assignment of arrays sprers.eu error: ISO C++ forbids assignment of arrays
Borland:
Error E sprers.eu Cannot convert 'int *' to 'int[6]' in function void Swap
We need a function that can deal with arrays, so we overload the template:
// overloaded template functiontemplate <typename T> void Swap(T *a, T *b, int size) { T temp; for (int i = 0; i < size; i++) { temp = a[i]; a[i] = b[i]; b[i] = temp; } }
// original template functiontemplate <typename T> void Swap(T &a, T &b) { T temp = a; a = b; b = temp; }
Use:
int a[] = {1, 3, 5, 7, 9, 11}; int b[] = {2, 4, 6, 8, 10, 12}; int size = sizeof(a)/sizeof(*a); Swap(a, b, size); // calls Swap(int *, int *, int) [with T = int] Display(a, size); // 2, 4, 6, 8, 10, 12 Display(b, size); // 1, 3, 5, 7, 9, 11
  • In this example, to handle C++ arrays, we need to specify the size as the third parameter.

Explicit Template Specialization

Up until now, all of our templated functions have been generated from the template. (Which was the whole point.)

What if we (the programmer) implement a function that matches an instantiated function?

  • Just as with overloaded functions, the compiler has elaborate rules to determine which version of a function to call or to generate.
  • This process is called overload resolution.
  • If you create a version of the function "manually", the compiler will use it (if it can) before generating a template function.
  • This "manually" created version is explicit template specialization.
  • The syntax is similar to a regular function except you preface the function with empty angle brackets:
    template <>

Example:

When the compiler must choose a function, the order of choice is:
  1. Regular functions
  2. Explicit specializations
  3. Template generated
You can force the compiler to choose a particular function by explicitly stating which function to use.

Also note that you cannot create an explicit specialization for a function after an implicit instantiation for that same function has been generated. For example, if we had this code before the explicit specialization for the function taking an :

void foo(void) { // implicitly instantiates cube for integersint i = cube<int>(25); }
The explicit specialization following this will generate a compiler error along these lines:
specialization of T cube(T) [with T = int] after instantiation (GNU) Template instance 'int cube

Another example. Given the templated Max function below:

template <typename T> T Max(T a, T b) { return a > b ? a : b; }
What is printed here?
int main(void) { cout << Max(5, 2) << endl; cout << Max(, ) << endl; cout << Max('A', 'a') << endl; cout << Max("banana", "apple") << endl; return 0; }
Output:
5 a apple
The fix:
// Explicit specialization for const char *// to sort lexicographicallytemplate <> constchar* Max(constchar *a, constchar *b) { return strcmp(a, b) >= 0 ? a : b; }

Explicit Template Instantiation

Up until now, all of our templated functions have been generated "on-demand", meaning that the code for a templated function wasn't actually generated until we needed (called) it. (Which, again, was the whole point.)

We can instruct the compiler to instantiate a template at any time during the compilation process.

The easiest way to show this is to disable the implicit template instantiations using the GNU compiler with the command line switch .

int main(void) { cube(5); // Ok, uses regular function cube<double>(10L); // Link error: Undefined reference cube(F); // Link error: Undefined reference cube<int>(); // Ok, uses specialization cube<char>(5); // Link error: Undefined referencereturn 0; }
we'll get linker errors like this:
sprers.eu: undefined reference to `double cube
We need to declare these "missing" functions before :
// explicit instantiations, declaration onlytemplatedouble cube(double); // Deduces cube<double>templatefloat cube(float); // Deduces cube<float>templatechar cube(char); // Deduces cube<char>
Note that the syntax is very similar to explicit specialization:
template <> double cube(double); // Explicit specializationtemplatedouble cube(double); // Explicit instantiation

Example:

Given these files: Cube files The default compilation mode for Borland (and all probably all other compilers) is to instantiate and compile templates in each file where they are needed. So, using Borland's compiler, these commands:
bcc32 -c sprers.eu bcc32 -c sprers.eu bcc32 -c sprers.eu bcc32 -c sprers.eu
generate these object files (partial directory listing):
03/02/ p 17, sprers.eu 03/02/ p 17, sprers.eu 03/02/ p 17, sprers.eu 03/02/ p 17, sprers.eu 4 File(s) 68, bytes
Modify the command to include (Generate external references for templates):
bcc32 -c -Jgx sprers.eu bcc32 -c -Jgx sprers.eu bcc32 -c -Jgx sprers.eu bcc32 -c sprers.eu
generate these object files (partial directory listing):
03/02/ p 17, sprers.eu 03/02/ p 9, sprers.eu 03/02/ p 9, sprers.eu 03/02/ p 9, sprers.eu 4 File(s) 45, bytes
Note that the size of the executable doesn't change. Why not?

To link the program into a file called sprers.eu, you would do this:

bcc32 sprers.eu sprers.eu sprers.eu sprers.eu sprers.eu

More information on template instantiation and how to control it.


Resolving Function Calls

With overloading, templates, and specializations, there can be several functions that might resolve to a specific call.
  • Exact matches vs. best match
    • Exact match or trivial conversions (e.g. to )
    • Promotions (e.g. to , to )
    • Standard (built-in) conversions (e.g. to , to , to )
    • User-defined conversions (e.g. conversion operators, constructors: to )
  1. Find all of the regular functions that have the same name as the function and template functions that can be instantiated from the parameters. (Candidate functions)
  2. Now consider the parameters of the functions and keep the ones that are applicable. (Viable functions)
  3. Consider the type conversions, if any: No conversion (exact match/trivial), promotions, built-in, user-defined. Rank them from more specialized to less specialized. (Stronger match to weaker match).
    1. If there is only one function, choose it.
    2. If there are more than one equally viable functions, the call is ambiguous. Remove the template instantiations from the set.
  4. Consider only ordinary functions (non-template) from the viable set.
    1. If there is only one function, choose it.
    2. If there are more than one equally viable functions, it's ambiguous and is an error.
Example: Output:
Min int (template): (0, 5) Min double (regular): (, ) Min double (regular): (0, )
Suppose we add an regular function so we have this:
TemplateRegular (double)Regular (int)
T Min(T a, T b) { return a < b ? a : b; } double Min(double a, double b) { return a < b ? a : b; } int Min(int a, int b) { return a < b ? a : b; }
How does this affect the last call in main?
Min(0, f); // Which function will be called?
Following the rules:
  • The parameters are and .
  • There's only one template parameter in the template function. No good.
  • For the first parameter, an , the regular function is an exact match, and the other function requires a conversion ( to ). The regular function is better. Score - function: 1, function: 0
  • For the second parameter, the function requires a conversion ( to ) and the function requires a promotion ( to ). The function is better. Score - function: 1, function: 1
  • Neither function is better for both arguments, so the call is ambiguous.
  • One could certainly argue a case for the regular function.
These are the error messages from 3 different compilers. Note that even without the regular function, MSVC++ won't compile.

MSVC++

sprers.eu(40) : error C 'Min' : 2 overloads have similar conversions sprers.eu(26): could be 'int Min(int,int)' sprers.eu(19): or 'double Min(double,double)' while trying to match the argument list '(int, float)'
GNU g++
sprers.eu: In function `int main()': sprers.eu call of overloaded `Min(int, float&)' is ambiguous sprers.eu candidates are: double Min(double, double) sprers.eu int Min(int, int)
Borland C++
sprers.eu: Error E sprers.eu Ambiguity between 'Min(double,double)' and 'Min(int,int)' in function main()

What happens if we define a version instead of the version so we have these now?

double Min(double a, double b); float Min(float a, float b);
How does this affect the last call in main?
Min(0, f); // Which function will be called?

Self-check

Template Specialization and Overloading Additional examples of function template overloading.
Why Not Specialize Function Templates? More advanced information about function templates.


Compiling Templates

How does the compiler and linker deal with the following code?
// In sprers.eu#include <iostream> int min(int, int); // prototypeint main(void) { std::cout << min(5, 6) << std::endl; return 0; }
// In sprers.euint min(int a, int b) { return a < b ? a : b; }

How about this code:

// In sprers.eu#include <iostream> // declaration onlytemplate <class T> T const& min(T const&, T const&); int main(void) { std::cout << min(5, 6) << std::endl; return 0; }
// Definition in sprers.eutemplate <class T> T const& min(T const &a, T const &b) { return a < b ? a : b; }

The ideal solution is to export the template defintions. According to Greg Comeau, "An exported template is special because its definition does not need to be present in a translation unit that uses that template."

Using the keyword:

// In sprers.eu#include <iostream> // declaration onlyexport template <class T> const T & min(const T &, const T &); int main(void) { std::cout << min(5, 6) << std::endl; return 0; }
// Definition in sprers.euexport template <class T> const T & min(const T &a, const T &b) { return a < b ? a : b; }
If it were only that easy

Most compilers do not implement , so this solution will not work (yet).

One solution is to explicitly instatiate the function:

// In sprers.eu#include <iostream> // declaration onlytemplate <class T> const T & min(const T &, const T &); int main(void) { std::cout << min(5, 6) << std::endl; return 0; }
// Definition in sprers.eutemplate <class T> const T & min(const T &a, const T &b) { return a < b ? a : b; } // explicit instantiationtemplateconstint& min(intconst &a, intconst &b);
An alternative solution is to put the definition in the header file and include it in all files that need the template:
// In sprers.eu#include <iostream> #include"min.h"int main(void) { std::cout << min(5, 6) << std::endl; return 0; }
// Definition in min.h#ifndef _MIN_H #define _MIN_H template <class T> const T & min(const T &a, const T &b) { return a < b ? a : b; } #endif//_MIN_H
  • A function template needs to be defined before it can be called. (Just like regular functions.)
  • Templates are usually defined in every source file. (Unlike regular functions that are defined only once.)
  • This allows the "source" to be seen by all code that calls the template function.
  • Several copies of the function will be present in the compiled code.
  • The compiler and linker must ensure that there is only one function for each type of instantiation.
  • There are several methods for dealing with this issue.
More information on template instantiation and how to control it.
A good overview of the keyword is here: Comeau C++ Export Overview

An article describing the difficulty in implementing exported templates.

Thread: C++ Error when trying to find palindrome

  1. March 26th, ,  PM#1

    C++ Error when trying to find palindrome

    Hi guys, trying to use Borland C++ to create a program to read in a string value to find out if it's a palindrome. Lots of examples online but none that are too useful. My code is as below but i keep getting the errors (as seen at the bottom of the page).
    I also need the code to ignore the case of the text entered so wether upper or lower case, it would still recognize the text as a palindrome.
    On the code i have marked out which lines the errors refer to.
    I'm a newbie can you help, what am i doing wrong?

    //
    int main(int argc, char* argv[])
    {
    AnsiString Line;
    int Front;
    int Back ;
    bool Palindrome(char *, int, int);
    //Pseudo-Code
    WriteString("Enter the string to be tested");
    Line = ReadStringPr("Enter the string to be tested");
    Front = 1;
    Back = Length(Line);
    Palindrome = true; <ERROR (1)
    while ((Front < Back) & (Palindrome = true)); <ERROR (2 & 3)
    {
    if (Line[Front] == Line[Back])
    {
    Palindrome = false; <ERROR (4)
    }
    else
    {
    Front = Front + 1;
    Back = Back - 1;
    } //ifend
    } //loopend
    WriteString("Line");
    if (Palindrome = true) <ERROR (5)
    {
    WriteString("is a palindrome");
    }
    else
    {
    WriteString("is not a palindrome");
    }
    getchar();
    return 0;
    }

    ERROR MESSAGES

    1. [C++ Error] sprers.eu(25): E Lvalue required
    2. [C++ Error] sprers.eu(26): E Lvalue required
    3. [C++ Warning] sprers.eu(26): W Code has no effect
    4. [C++ Error] sprers.eu(30): E Lvalue required
    5. [C++ Error] sprers.eu(39): E Lvalue required


  2. Re: C++ Error when trying to find palindrome

    Please use [ code ] tags to post code.

    Code:

    bool Palindrome(char *, int, int);
    this is a function declaration. Not a variable.
    Now it's a variable declaration.

    Code:

    if (Palindrome = true)
    To compare you need a double =

    Code:

    if (Palindrome == true)
    Last edited by Skizmo; March 26th, at PM.

  3. Re: C++ Error when trying to find palindrome

    Please use code tags by wrapping your code with [ C ODE] [ / C ODE] tags minus the spaces. Then format your code to make it easier to read.

    As far as the errors.

    Palindrome is declared as a function, but you are treating it as a variable and attempting to assign to it (Palindrome = true. You can't assign functions.

    Don't confuse the bitwise AND ('&') with the logical AND ('&&'). Use the logical AND.

    Don't confuse the assignment '=' operator with the comparison '==' operator. Use the '==' operator for comparison tasks.

    My Code Guru Articles


  4. Re: C++ Error when trying to find palindrome

    Also, this is a Visual C++ forum. If you're using Borland, you're in the wrong place.


  5. March 26th, ,  PM#5

    Re: C++ Error when trying to find palindrome


«Previous Thread does not exist in my mental image of an array. It's just a
> constant that gets plugged in when we need to calculate the position
> of one/all array-element(s).

> > > At least that's the way I would code it in assembler.  Am I missing a
> > > pair of dimes some where?

> > No, an array is not a pointer, not even a pointer constant.  It decays
> > to a pointer to its first element in most contexts, such as when
> > passing as an argument to a function.  But an array is an object.

> > In the definition:

> > int x;
> > int ar [3];

> > ar is no more a pointer than x is.

> I *think* this is what I tried to say above. Somehow saying that the
> name of an array is an lvalue seems a bit iffy here. Is that the
> language of the standard ? The lvalue we're talking about is *not* the
> address of array as such (on the left of the drawing above), because
> that is defined at compile/link time and is not stored as data,
> i.e. it does not exist. The lvalue we're talking about is the
> collection of array elements, right ?

Let's back up and get a little more basic and talk about low level
details (as low as C goes).

There are only two "real" things in C (I'll define "real" shortly),
functions and objects.  The two are totally incompatible, there is no
way to treat a function as an object or vice/versa.  You can create an
object that is a pointer to a function, though, but that is not
relevant here.

Here's the first definition (all quotes from the N final draft of
the C99 standard update):

       
       [#1] object
       region of data storage in  the  execution  environment,  the
       contents of which can represent values

       [#2]  NOTE  When referenced, an object may be interpreted as
       having a particular type; see

And here is a hunk of that the above quote cites, reformatted
to remove the page break in the middle and move the footnotes to the
end:

         Lvalues and function designators

       [#1] An lvalue is an expression with an object  type  or  an
       incomplete type other than void;46) if an  lvalue  does  not
       designate  an  object  when it is evaluated, the behavior is
       undefined.  When an object is  said  to  have  a  particular
       type,  the type is specified by the lvalue used to designate
       the object.  A modifiable lvalue is an lvalue that does  not
       have  array type, does not have an incomplete type, does not
       have a const-qualified type, and if it  is  a  structure  or
       union, does not have any member (including, recursively, any
       member or element of all  contained  aggregates  or  unions)
       with a const-qualified type.

       [#2]  Except  when it is the operand of the sizeof operator,
       the unary & operator, the ++ operator, the --  operator,  or
       the  left  operand  of  the  .  operator  or  an  assignment
       operator, an  lvalue  that  does  not  have  array  type  is
       converted  to the value stored in the designated object (and
       is no longer an lvalue).  If the lvalue has qualified  type,
       the  value  has  the  unqualified version of the type of the
       lvalue; otherwise, the value has the type of the lvalue.  If
       the  lvalue  has  an incomplete type and does not have array
       type, the behavior is undefined.

       [#3] Except when it is the operand of the sizeof operator or
       the  unary  &  operator,  or  is  a  string  literal used to
       initialize an array, an expression that has type ``array  of
       type''  is converted to an expression with type ``pointer to
       type'' that points to  the  initial  element  of  the  array
       object  and  is  not  an  lvalue.   If  the array object has
       register storage class, the behavior is undefined.

       _____________________

       46)The  name ``lvalue'' comes originally from the assignment
          expression E1 = E2, in  which  the  left  operand  E1  is
          required  to  be  a  (modifiable)  lvalue.  It is perhaps
          better considered as  representing  an  object  ``locator
          value''.   What is sometimes called ``rvalue'' is in this
          International Standard described as  the  ``value  of  an
          expression''.

          An  obvious  example  of an lvalue is an identifier of an
          object.  As a further example, if E is a unary expression
          that  is  a  pointer  to  an object, *E is an lvalue that
          designates the object to which E points.

And finally here is the description of the cast unary operator:

         Cast operators

       Syntax

       [#1]

               cast-expr:
                       unary-expr
                       ( type-name ) cast-expr

       Constraints

       [#2] Unless the type name specifies a void  type,  the  type
       name  shall specify qualified or unqualified scalar type and
       the operand shall have scalar type.

       [#3] Conversions that involve  pointers,  other  than  where
       permitted by the constraints of , shall be specified
       by means of an explicit cast.

       Semantics

       [#4] Preceding an expression by a  parenthesized  type  name
       converts  the  value  of  the  expression to the named type.
       This  construction  is  called  a  cast)   A  cast   that
       specifies  no  conversion has no effect on the type or value
       of an expression.

       _____________________

       76)A cast does not yield an  lvalue.   Thus,  a  cast  to  a
          qualified  type  has  the  same  effect  as a cast to the
          unqualified version of the type.

A cast creates what used to be called an rvalue.  All you can do with
an rvalue is use it in an expression, you can't actually modify it.

Consider the following:

(2 + 2)++;

Does this result in 5 (answer: no).  If it did, where would that 5 be?
The expression 2 + 2 results in the "rvalue" or "value of expression"
of 4.  It is highly temporary, lasting at most to the end of the
current statement.  It is not an lvalue of any kind, you can't post
increment it or take its address.  But, if you leave off the post
increment, you can assign that value to an object before it ceases to
exist:

int a = (2 + 2);

Now:

int x = 2, y = 2;

(x + y);

You can't increment this expression, either.  Neither x nor y is
changed, instead a temporary value of 4 comes into existence inside
the parentheses and disappears at the semicolon.  So you can see that
it makes no sense to write:

(x + y)++; because there is no object containing the value 4 to be
incremented.

So given:

int x = 5, *iptr = &x;

You can do iptr++ (although it might not point to something you can
legally reference).

But you can't do ((char *)iptr)++; because just like adding 2 and 2,
or variables x and y, the current value of iptr is read, converted
from a pointer to int into a pointer to char (only a conceptual change
on many systems, but an actual change of the binary representation on
others), and this temporary converted pointer will again disappear at
the semicolon.

Jack Klein
--
Home: sprers.eu



Sat, 04 Jan GMT 

Kaz Kylhe
#10 / 14

 ANSI C - compiler bug or not

Quote:



>wrote in sprers.euc:

>> /* Would this question be better placed in sprers.euc  ? */

>> /*Condsider the following toy program.*/

>> #include <stdio.h>

>> /*

>> Compile with either:
>> BCC32 -w -c -A -If:\wherever\include file.c
>> or
>> gcc -c -ansi -o file file.c
>> or your fav with
>> warnings on
>> compile to object only
>> ansi compile mode ie  __STDC__ is defined.

>> */

>> /* declare array as array of int         */
>> int array[] = {1,2,3};

>> void K(int *const AA)
>> {
>>     ((int *)AA)++;    /* Lvalue required in function K */

>As Ben said, the result of a cast is never an lvalue.

>>     printf("%d",AA[0]); /* gcc warns changing read-only loc */
>> }  /* Damn it! this cast ought to work!  */
>>   /* This is C. I'm supposed to know what I'm doing.  */

>> void L(int *const AA)
>> {
>>     int *AAx;   /* taking the hard way around the problem */
>>     AAx = ((int *)AA);
>>     AAx++;
>>     printf("%d",AAx[0]);

>>     /* Looks more like Ada than C */
>> }

>> int main()

>> {
>>     K(array);
>>     L(array);
>>     array++;         /* why is this not an Lvalue? */

>The name of an array is an lvalue.  It is not a modifiable lvalue.  I
>have yet to see a compiler that gives actual correct text for this
>error message, although there must be one someplace.

An array is only an lvalue when it is the subject of a sizeof operator, or the
address-of operator. In other contexts, it is converted to a pointer to the
first element and thereby robbed of its lvalue attribute.

So these compilers are accurate in their wording of the diagnostic;

--
#exclude <windows.h>



Sat, 04 Jan GMT 

Kaz Kylhe
#11 / 14

 ANSI C - compiler bug or not

Quote:


>       [#3] Except when it is the operand of the sizeof operator or
>       the  unary  &  operator,  or  is  a  string  literal used to
>       initialize an array, an expression that has type ``array  of
>       type''  is converted to an expression with type ``pointer to
>       type'' that points to  the  initial  element  of  the  array
>       object  and  is  not  an  lvalue.   If  the array object has

                ^^^^^^^^^^^^^^^^^^^^^^^^^

Quote:

>       register storage class, the behavior is undefined.

This text is relevant to my other reply in this thread. It is why
compilers are right in diagnosing assignments to arrays as lacking
an lvalue, or words to that effect.


Sat, 04 Jan GMT 

Jack Klei
#12 / 14

 ANSI C - compiler bug or not

Kylheku) wrote in sprers.euc:

Quote:


> >       [#3] Except when it is the operand of the sizeof operator or
> >       the  unary  &  operator,  or  is  a  string  literal used to
> >       initialize an array, an expression that has type ``array  of
> >       type''  is converted to an expression with type ``pointer to
> >       type'' that points to  the  initial  element  of  the  array
> >       object  and  is  not  an  lvalue.   If  the array object has
>                 ^^^^^^^^^^^^^^^^^^^^^^^^^
> >       register storage class, the behavior is undefined.

> This text is relevant to my other reply in this thread. It is why
> compilers are right in diagnosing assignments to arrays as lacking
> an lvalue, or words to that effect.

Yes, but this part is relevant as well:

Quote:

>                     A modifiable lvalue is an lvalue that does  not
>        have  array type, does not have an incomplete type, does not
>        have a const-qualified type, and if it  is  a  structure  or
>        union, does not have any member (including, recursively, any
>        member or element of all  contained  aggregates  or  unions)
>        with a const-qualified type.

An array name is indeed an lvalue, and specifically non-modifiable
one.  So an array name is a non-modifiable lvalue in some contexts,
and not an lvalue when it decays to a pointer in other contexts.

I can't make up my mind if this is contradictory or not, but since
sizeof can only be applied to objects and types, the name of an array
_must_ be an lvalue in that context.

sprers.euc anyone?  You take it there, Kaz, I always seem to get my
{*filter*}kicked there lately! :)

Jack Klein
--
Home: http://www.*-*-*.com/



Sat, 04 Jan GMT 

Jack Klei
#13 / 14

 ANSI C - compiler bug or not

Kylheku) wrote in sprers.euc:

Quote:



> >wrote in sprers.euc:

> >> /* Would this question be better placed in sprers.euc  ? */

> >> /*Condsider the following toy program.*/

> >> #include <stdio.h>

> >> /*

> >> Compile with either:
> >> BCC32 -w -c -A -If:\wherever\include file.c
> >> or
> >> gcc -c -ansi -o file file.c
> >> or your fav with
> >> warnings on
> >> compile to object only
> >> ansi compile mode ie  __STDC__ is defined.

> >> */

> >> /* declare array as array of int         */
> >> int array[] = {1,2,3};

> >> void K(int *const AA)
> >> {
> >>     ((int *)AA)++;    /* Lvalue required in function K */

> >As Ben said, the result of a cast is never an lvalue.

> >>     printf("%d",AA[0]); /* gcc warns changing read-only loc */
> >> }  /* Damn it! this cast ought to work!  */
> >>   /* This is C. I'm supposed to know what I'm doing.  */

> >> void L(int *const AA)
> >> {
> >>     int *AAx;   /* taking the hard way around the problem */
> >>     AAx = ((int *)AA);
> >>     AAx++;
> >>     printf("%d",AAx[0]);

> >>     /* Looks more like Ada than C */
> >> }

> >> int main()

> >> {
> >>     K(array);
> >>     L(array);
> >>     array++;         /* why is this not an Lvalue? */

> >The name of an array is an lvalue.  It is not a modifiable lvalue.  I
> >have yet to see a compiler that gives actual correct text for this
> >error message, although there must be one someplace.

> An array is only an lvalue when it is the subject of a sizeof operator, or the
> address-of operator. In other contexts, it is converted to a pointer to the
> first element and thereby robbed of its lvalue attribute.

> So these compilers are accurate in their wording of the diagnostic;

Yes, agreed.  Consider a {*filter*} reference to an array:

int array [5];

array;

Which is it in this case?  Non-modifiable lvalue, or non-lvalue
pointer?

Who cares, and why?

Jack Klein
--
Home: http://www.*-*-*.com/



Sat, 04 Jan GMT 

gdem..
#14 / 14

 ANSI C - compiler bug or not

()

Quote:

> void L(int *const AA)
> {
>     int *AAx;   /* taking the hard way around the problem */
>     AAx = ((int *)AA);
>     AAx++;
>     printf("%d",AAx[0]);

>     /* Looks more like Ada than C */
> }

Are you joking ?!

You are trying to work out what is the data type structure
in the middle of the code, doing pointer arithmetics;
the result is hardly readable even for a simple operation
and you say it looks more like Ada than C ?!!

You must be joking!

G.

Sent via sprers.eu sprers.eu
Before you buy.



Sat, 11 Jan GMT  
 Page 1 of 1
 [ 14 post ] 

 Relevant Pages