So i recently stumbled on this code somewhere where it is copying one string to another using just one line of code with the help of a while loop , however , I am not able to understand as to how and why it happens-:
int main()
{
char arr1[100];
cin.getline(arr1 , 100);
char arr2[100];
int i = -1;
while(arr2[i] = arr1[++i]);
cout<<arr1<<endl<<arr2<<endl;
return 0;
}
Can somebody explain me what is happening in the backdrop?
and moreover if the above code works fine then why dont the below ones?
int main()
{
char arr1[100];
cin.getline(arr1 , 100);
char arr2[100];
int i = 0;
while(arr2[i++] = arr1[i]);
cout<<arr1<<endl<<arr2<<endl;
return 0;
}
another one-:
int main()
{
char arr1[100];
cin.getline(arr1 , 100);
char arr2[100];
int i = 0;
while(arr2[++i] = arr1[i]);
cout<<arr1<<endl<<arr2<<endl;
return 0;
}
The code snippet is relying on an order-of-evaluation guarantee that was added in C++17.
Since C++17 it is guaranteed that the right-hand side of a = operator is evaluated first. As a consequence the loop is equivalent to
int i = -1;
while(true) {
i++;
arr2[i] = arr1[i];
if(!arr2[i])
break;
};
Except that one would normally start at i = 0; and put i++; at the end of the loop iteration, I think it should be clearer what is happening now. The loop breaks when a null character is encountered, so it expects that arr1 is a null-terminated string and won't copy the whole array.
Before C++17 the order of evaluation was not specified and the code had undefined behavior as a consequence.
If you change the loop to int i=0; while(arr2[++i] = arr1[i]);, then (since C++17) you execute ++i only after indexing arr1[i], but before indexing arr2. As a consequence you are not copying to the beginning of arr2. Again, before C++17 this is undefined behavior.
int i=0; while(arr2[i++] = arr1[i]); should work correctly since C++17 as well. It does the increment only after indexing both arrays. Again, before C++17 it has undefined behavior.
You shouldn't use either of these, since they are hard to reason about and have undefined behavior if the user happens to set the C++ version switch to something before C++17 or tries to use it in C, where it is undefined behavior in all versions.
Also int may be too small to hold all indices of a string. Prefer std::size_t (which however is unsigned and so the first variant won't work).
Utilities for things like copying strings should be written in functions, not inline every place they're used. That makes it simpler to avoid the complexities of incrementing the same variable twice:
void copy_string(char* dest, const char *src) {
while (*dest++ = *src++)
;
}
Yes, I know, some people like to have their compiler ignore the rules and refuse to compile valid, well-defined code like this. If your compiler is set that way, figure out how to rewrite that code to make your compiler happy, and perhaps think about who's the boss: you or your compiler.
Related
I understand how arrays can be passed to functions in c++, but I don't understand what is the point of including array's size in a function declaration when this size is ignored anyway, because what we are really passing to function is a pointer to the first element of the array.
For example if we have following code:
void ArrayTest1(int(&ints)[3])
{
for (int i = 0; i < 3; i++) std::cout << ints[i];
}
void ArrayTest2(int ints[3])
{
for (int i = 0; i < 3; i++) std::cout << ints[i];
}
void ArrayTest3(int ints[])
{
for (int i = 0; i < 3; i++) std::cout << ints[i];
}
int main()
{
int ints[] = { 1, 2 };
//ArrayTest1(ints); //won't compile
ArrayTest2(ints); //ok
ArrayTest3(ints); //ok
return 0;
}
Then from what I understand functions ArrayTest2 and ArrayTest3 are identical.
Is syntax used in ArrayTest2 only meant to make it clear that this function expects an array with 3 elements and passing array with a different size can cause errors? I'm not new to programming, but I'm new to c++ so I'd like to know what is the point of such syntax and when do people use it.
One of C++’s major design goals was far reaching compatibility with C. Not supporting all aspects of C-array usage would have been seriously detrimental to this goal.
Your examples even give a good indication that compatibility is indeed the intent, and that C++ would work differently if that hadn’t been the case. Consider your ArrayTest1(). It takes a C-array by reference, something that does not exist in C. And in this case the array dimension is significant.
// C-array of length 3 taken by reference
void foo(int (&x)[3]);
void bar() {
int a[4] = {1,2,3,4};
foo(a); // Does not compile. Wrong array size.
}
Only ArrayTest2() and ArrayTest3() are exactly equivalent to:
void foo(int*);
Btw: Bjarne Stroustrup’s The Design and Evolution of C++ is a good read if you’re interested in why C++ is designed the way it is.
I don't exactly know why C permits this (yes, this comes from C), but it is indeed pointless.
Worse than that, the "wrong" dimension would be very misleading to readers of your code.
We might argue that it was to keep the language grammar simple, as the grammar of a declarator already makes the dimension optional (consider int x[] = {1,2,3} vs int x[3] = {1,2,3}).
Maybe some people use it to document intent; I certainly never would.
I solved this introduction problem on hackerrank.
Here is something strange when I try to solve this problem.
the input is
4
1 4 3 2
I want to read the numbers into an array.
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int a;
int arr[a];
scanf("%d",&a);
for(int i=0; i<=a-1; i++){
scanf("%d",&arr[i]);
printf("i = %d, a = %d\n", i, a);
}
return 0;
}
I got the output:
i = 0, a = 4
i = 1, a = 4
i = 2, a = 4
i = 3, a = 2
The array is correct.
My question is why the value in int a is changed? Why it is changed to 2 instead of 3?
if I rearrange following lines:
int a;
scanf("%d",&a);
int arr[a];
the value in int a is not changed,
i = 0, a = 4
i = 1, a = 4
i = 2, a = 4
i = 3, a = 4
This is wrong:
int a;
int arr[a];
scanf("%d",&a);
Two problems: You are using a before you read the value from the user. Using a unitinitalized is undefined behavior. The output of your code could be anything or nothing.
Then you cannot have a static array with a run-time size. Some compilers support variable length arrays as an extension, but they are not standard c++ (see here).
If you want to write C++, then you should actually use C++. Dynamically sized arrays are std::vector. Your code could look like this:
#include <vector>
#include <iostream>
int main() {
int a;
std::cin >> a; // read user input before you use the value
std::vector<int> x(a); // create vector with a elements
for (size_t i=0; i < x.size(); ++i) {
std::cin >> x[i];
std::cout << "i = " << i << " a = " << a << "\n";
}
}
My question is why the value in int a is changed? Why it is changed to 2 instead of 3?
Undefined behavior means just that, the behavior of your program is undefined. Compilers are not made to compile invalid code. If you do compile invalid code then strange things can happen. Accessing arr[i] is accessing some completely bogus memory address and it can happen that writing to that overwrites the value of a. However, it is important to note that what happens here has little to do with C++, but rather your compiler and the output of the compiler. If you really want to understand the details you need to look at the assembly, but that wont tell you anything about how C++ "works". You can do that with https://godbolt.org/, but maybe the better would be to pay attention to your compilers warnings and try to write correct code.
int a;
int arr[a];
scanf("%d",&a);
This means:
Declare an uninitialised a, with some unspecified value that is not permitted to be used
Declare an array with runtime bounds a (which doesn't exist), which is not permitted
Read user input into a.
Even if these steps were performed in the correct order, you cannot have runtime bounds in C++. Some compilers permit it as an extension, though I've found these to work haphazardly, and it's certainly going to result in strange effects when you use an uninitialised value to do it!
In this case, in practice, you probably have all sorts of weirdness going on in your stack, since you're accessing "non-existent" elements of arr, overwriting the variables on the stack that are "below" it, such as a. Though I caution that trying to analyse the results of undefined behaviour is kind of pointlesss, as they can change at any time for various black-boxed reasons.
Make a nice vector instead.
My friends and I were playing with the C++ language. While doing so, we encountered something we couldn't understand.
Here is the code:
#include <vector>
#include <iostream>
void print(std::vector<char> const &input)
{
std::cout << input.size();
for (int i = 0; i < input.size(); i++)
{
std::cout << input.at(i) << " - ";
}
}
int main()
{
char cha = 'A';
char chb = 'B';
char * pcha = &cha;
char * pchb = &chb;
try
{
std::vector<char> a = {pcha, pchb};
//std::vector<char> a = {pchb, pcha};
print(a);
}
catch(std::exception e)
{
std::cout << e.what();
}
}
Output for this code:
A
When I comment out this first line try block and uncomment the second line, which comes to this:
try
{
// std::vector<char> a = {pcha, pchb};
std::vector<char> a = {pchb, pcha};
print(a);
}
Output becomes:
std:exception
I thought maybe the this occurs because of the different padding and alignments of the declared variables (char, char*), yet still didn't understand. You can find the code here to play around.
Thanks in advance.
std::vector<char> a = {pcha, pchb};
Here, you use the constructor of vector that accepts two iterators to a range. Unless the end iterator is reachable from the begin one, the behaviour of the program is undefined. Your two pointers are not iterators to the same range (i.e. elements of an array), so one is not reachable from the other. Therefore the behaviour of the program is undefined.
These would be correct:
std::vector<char> a = {cha, chb}; // uses initializer_list constructor
// or
char arr[] {cha, chb};
char * pcha = std::begin(arr);
char * pchb = std::end(arr);
std::vector<char> a = {pcha, pchb}; // uses the iterator constructor
#eerorika's answer explains your mistake.
However, I would like to dissuade you, and other readers, from using the second part of the his(?) corrected code snippet - not because it's incorrect, but because it's problematic coding practice:
I accept Nicolai Jossutis' suggestion of trying to uniformly initialize variables with curly brackets and no equals since (e.g.. mytype myvar {my_initializer};).
Freestanding pointers are dangerous beasts. Try to avoid them altogether, or minimize their existence to where you really need them. After all, you were "tempted" to use those pointers in an inappropriate way... so,
char arr[] {cha, chb};
std::vector<char> a = {std::begin(arr), std::end(arr)};
Don't create a dummy container just to create the one you really want. Just stick with the first line in #eerorika's suggestion (without the equals sign):
std::vector<char> a {cha, chb};
In fact, unless you really need it - you probably don't even want to create a variable-length container. So perhaps just
std::array<char, 2> a {cha, chb};
or with C++17's template argument deduction:
std::array a {cha, chb};
use of a function returning an integer and (possibly) modifying a base pointer causes undesired behavior in returning the intended member of an array.
this concerns quite old legacy code; modern options using containers will solve the problem (No need to tell me, please!), but I want to point this out and ask whether the behavior is intentional under C++17, as contrasted with up to and including C++14.
The code looks like:
// dosomething() returns an integer.
// ArrayPointer is a class member
somememberfunction(args)
{
...
return ArrayPointer[dosomething()];
}
The problem arises from the fact that dosomething() will change ArrayPointer and the old value of ArrayPointer is used instead of the new (under C++17, not under C++14).
The workaround is to define an intermediate variable:
const int index=dosomething();
return ArrayPointer[index];
The question is: is there an explanation (depending on the standard), or is this to be regarded as a bug, rather than just undesired behavior from my point of view?
PS: complet(er C) code, as requested, mind the non-essential parts, variable declarations omitted:
struct BKPRArray // struct for applying meteocorrection
{
double *findprarray(const char *bks)
{
// check earlier allocations (NR)
for (int n = 0; n < arrays; n++)
if (!_stricmp(this->bk[n], bks))
{
if (!PRarray[n]) PRarray[n] = (NUMTYPE*)calloc(FL_alloced, sizeof(NUMTYPE));
return PRarray[n];
}
// combined return statement will fail under C++17 by using the old value of PRarray.
// This MAY be caused by order of evaluation, rule 17: https://en.cppreference.com/w/cpp/language/eval_order
// allocate and prepare (preparation code omitted) a new array.
//return PRarray[addarray(bks, defaultMeteoCorrection)];
const auto index = addarray(bks, defaultMeteoCorrection);
return PRarray[index]; // zet factor op +20%, want bk niet specifiek vermeld bij input.
}
....
int addarray(const char *bks, const double fact = defaultMeteoCorrection)
{
PRarray = (double**)realloc(PRarray, (arrays + 1) * sizeof(*PRarray));
if (FL_alloced) PRarray[arrays] = (NUMTYPE*)calloc(FL_alloced, sizeof(NUMTYPE));
else PRarray[arrays] = NULL;
...
return arrays++;
}
....
private:
double **PRarray; // etc.
};
Order of evaluation between ArrayPointer and dosomething() is unspefified before C++17. (So the fact that it "works" in C++14 is by "chance").
In C++17, ArrayPointer should be evaluated before dosomething().
see Order of evaluation, Rule 17.
Why is this code returning -1?
memcmp() compares block of memory and takes 3 parameters in constructor but what happens when I miss the third parameter?
int main()
{
char ptr[] = "hello";
char ptr1[] = "hello";
int a = memcmp(ptr,ptr1);
printf("%d",a);
return 0;
}
Also the following program abruptly terminates without the third parameter:
int main()
{
char *ptr = "hello";
char *ptr1 = "hello";
int a = memcmp(ptr,ptr1);
printf("%d",a);
return 0;
}
For starters, memcmp() takes three arguments: the pointers to the memory segments to be compared and the size. Although the code may compile in C (I don't think it should) it certainly doesn't compile using C++. If the code compiled, the third argument is a pretty random value and it is unlikely that the memory after these strings is the same.
It will compile neither in C nor in C++. In C, first one does compile only when you do not include <stdlib.h> and it simply invokes undefined behavior because passing arguments to a function less than that of its parameter invokes UB.
Here is the output: