sort (arr, arr + n)
Why do we write arr + n in sort function in (function in algorithm library) C++. What does it mean arr + n?
std::sort accepts iterators to beginning and end of some range (end points to first element beyond range).
A pointer can be an iterator
In C an array of type sometype[n] decays to a pointer of type: sometype*. So arr is treated as a pointer and arr + n advances this pointer by n elements (so it point to first element beyond array).
Now alternative ways to write this code to make it more clear and less bug prone:
std::sort(std::begin(arr), std::end(arr));
// or using C++20 ranges:
std::ranges::sort(arr);
Given the declaration of type arr[], the expression arr + n is evaluated as the address of the nth element in arr.
In other words (hoping that it helps clarifying), arr + n is equivalent to &arr[n].
std::sort takes a pair of iterators and them uses std::swap to sort the elements in that range.
The iterators must provide implementations for operator* and operator++. These requirements are defined as "named requirements" here.
If you thing about it, any pointer fulfills these criteria.
In other words, you can think of iterators as a generalization of pointers. By passing a pointer to the first &arr[0] and a pointer to one past the last element &arr[n] you are providing the begin and end iterators. arr and arr + n are fancy abbreviations for &arr[0] and &arr[n].
Related
(Here a is an array, asize is the size of array.)
My question is that can someone explain why sort requires 'a' and 'a + asize' in it and what it does with it?
a is an array. In various contexts, when an array is referring to by just its name, it will decay into a pointer to its 1st element.
Adding asize to such a pointer will perform pointer arithmetic to advance the pointer by asize number of elements.
std::sort() takes 2 iterators as input, denoting a range of values [start, end). Meaning it will loop through and sort the values including start up to, but not including, end.
Pointers to elements in an array are valid iterators. So, a statement like sort(a, a + asize) is the same as sort(&a[0], &a[asize]).
Credit: Branko Dimitrijevic
So the a is a pointer that points towards the beginning of the array and a+asize would be the pointer to the "imaginary" that exists immediately after the last real element of array.
int[] a = {1,2,3};
int* b = new int[3]{4,5,6};
I want to get the begin and end of b. std::begin()works in abut not in b. Is there an suitable way to get the two positions of b?
If you see a reference to std::begin and std::end you will see that beginning and end iterators are in way pointers to the first and one-beyond-last elements of the container. For arrays this is doubly-true because then you can use actual pointers for the iterators.
For an array like a the std::begin function returns a pointer to the first element, and the std::end function returns a pointer to one beyond the last element.
Now when you know that information it's easy to figure out what "begin" and "end" iterators for a pointer is, because it's exactly the same as for an array. I.e. pointers to the first and one-beyond-last elements.
In the case of your example, the "begin iterator" is simply b (because it points to the first element), and the "end iterator" is b + 3 which is a pointer to one beyond the last element.
I'm learning vectors and am confused on how the array is copying to thevector here
double p[] = {1, 2, 3, 4, 5};
std::vector<double> a(p, p+5);
I also know std::vector<double> a(3,5); means `make room for 3 and initialize them with 5. How does the above code work?
The second point is I read the paragraph from where I copied the above code.
Understanding the second point is crucial when working with vectors or
any other standard containers. The controlled sequence is always
expressed in terms of [first, one-past-last)—not only for ctors, but
also for every function that operates on a range of elements.
I don't know what is the meant by [first, one-past-last) ?
I know mathematically but don't know why/how vector copy the array in this way?
Edited
another related question
The member function end() returns an iterator that "points" to
one-past-the-last-element in the sequence. Note that dereferencing the
iterator returned by end() is illegal and has undefined results.
Can you explain this one-past-the-last-element what is it? and why?
Never dereference end() or rend() from STL containers, as they do not point to valid elements.
This picture below can help you visualize this.
The advantage of an half open range is:
1. Handling of empty ranges occur when begin() == end()
2. Iterating over the elements can be intuitively done by checking until the iterator equals end().
Strongly coupled with containers (e.g. vector, list, map) is the concept of iterators. An iterator is a C++ abstraction of a pointer. I.e. an iterator points to an object inside the container (or to one past the last element), and dereferencing the iterator means accessing that element.
Lets take for instance a vector of 4 elements:
| 0 | 1 | 2 | 3 | |
^ ^ ^
| | |
| | one past the end (outside of the container elements)
| last element
first element
The (algorithms in the) standard template library operate on ranges, rather than on containers. This way you can apply operations, not only to the entire container, but also to ranges (consecutive elements of the container).
A range is specified by [first, last) (inclusive first, exclusive last). That's why you need an iterator to one past the end: to specify a range equal to the entire contents of the container. But as that iterator points outside of it, it is illegal to dereference it.
The constructor of std::vector has several overloads.
For std::vector<double> a(3,5); the fill constructor is used :
explicit vector (size_type n);
vector (size_type n, const value_type& val,
const allocator_type& alloc = allocator_type());
This takes a size parameter as it's first parameter and an optional and third parameter, the second parameter specifies the value you want to give the newly created objects.
double p[] = {1, 2, 3, 4, 5};
std::vector<double> a(p, p+5);
Uses another overload of the constructor, namely the range constructor:
template <class InputIterator>
vector (InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
This takes an iterator to the start of a collection and the end() iterator and traverses and adds to the vector until first == last.
The reason why end() is implemented as one-past-the-last-element is because this allows for implementations to check for equality like:
while(first != last)
{
//savely add value of first to vector
++first;
}
Iterators are an abstraction of pointers.
A half-open interval [a,b) is defined as all elements x>=a and x<b. The advantage of it is that [a,a) is well defined and empty for any a.
Anything that can be incremented and compared equal can define a half open interval. So [ptr1,ptr2) is the element ptr1 then ptr1+1 then ptr1+2 until you reach ptr2, but not including ptr2.
For iterators, it is similar -- except we do not always have random access. So we talk about next instead of +1.
Pointers still count as a kind-of iterator.
A range of iterators or pointers "talks about" the elements pointed to. So when a vector takes a pair of iterators (first, and one-past-the-end), this defines a half-open interval of iterators, which also defines a collection of values they point to.
You can construct the vector from such a half-open range. It copies the elements poimtrd to into the vector.
we can initialize a vector using array. suppose,
int a[]={1,2,3}
vector<int>x(a,a+3)
this is a way . My question is, what is a and a+3 here, are they pointer?
and someone could explain this:
for the above array
vector<int>x(a,&a[3])
also gives no error and do the same as above code. If we write a[3], it should be outside of the array? can someone tell me the inner mechanism?
Yes, a and a+3 are pointers. The plain a is an array that gets converted implicitly to a pointer at the drop of a hat.
The construct &a[3] is identical in meaning to a+3 for plain C arrays. Yes, a[3] is outside the array, but one-past is allowed to point to, if you don't dereference it.
A vector's range constructor looks like this:
template <class InputIterator>
vector (InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
It will construct elements in the range [first, last), meaning that the last element is not included. &a[3] points to an element outside the bounds of the array, similar to how std::end(a) will point one past the end of a. Compare it to:
std::vector<int> x(std::begin(a), std::end(a));
Also, *(a + 3) is equivalent to a[3].
int a[]={1,2,3}
vectorx(a,a+3)
a is an array so it is always pointing to its base address.
a+3 mean base address+(sizeof(int) *3)
suppose base address is 100
a=100;
a+3=106;
vectorx(a,&a[3])
Here &a[3] is equivalent to a+3
Let's say I have an array of QStrings and a QString pointer. I want to use the pointer to iterate through the entire array; could I do this?
QString * strPointer;
QString data[100];
strPointer = & data[0]; //address to first element
strPointer ++; //address to second element
Would this be valid or am I doing something wrong?
You're on the right lines. Here's one way
QString data[100];
for (QString* strPointer = &data[0]; strPointer != &data[100]; ++strPointer)
{
...
}
Yes, this is fine so long as the type of the pointer matches what's actually being pointed to in the array. By incrementing a pointer you are performing pointer arithmetic.
It may be interesting to note that because iterators in the Standard Library are written to look & feel like pointers in many ways, and all the Standard Library algorithms take iterators specified as template parameters, it is legal and well-defined to use these algorithms with raw pointers as well. For example, this is perfectly legitimate, even with your pointers:
const size_t num_data = sizeof(data)/sizeof(data[0]);
std::copy( &data[0], &data[num_data], ostream_iterator<QString>(cout,"\n") );
...assuming of course you have implemented operator<< for a QString object.
Now, all this being said, take a look at this:
QString data[100];
The 100 here is what's called a Magic Number. The use of Magic Numbers is widely considered to be an anti-pattern, or a bad practice. Ask yourself a couple questions:
How do you know that 100 elements will be enough?
If you don't need 100 elements, are you being wasteful?
If you need more than 100 elements, will your program crash?
It's best to avoid using magic numbers wherever you can. Your choice of 100 here is arbitrary. It would be better to use a collection type that grows and shrinks as you add and remove objects. std::vector is a good place to start:
std::vector<QString> data;
Now you can add items:
data.push_back( ... );
...remove them, and iterate easily, using iterators:
std::copy( data.begin(), data.end(), ostream_iterator<QString>(cout,"\n") );
Yes, it is. Remember about checking index - operator++ can go "too far" - beyond the array.
Yes, incrementing a pointer to an element of an array will produce a pointer to the next element or to a position one past the end of the array.
When an expression that has integral type is added to or subtracted
from a pointer, the result has the type of the pointer operand. If the
pointer operand points to an element of an array object, and the array
is large enough, the result points to an element offset from the
original element such that the difference of the subscripts of the
resulting and original array elements equals the integral expression.
In other words, ifthe expression P points to the i-th element of an
array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N
(where N has the value n) point to, respectively, the i + n-th and i −
n-th elements of the array object, provided they exist. Moreover, if
the expression P points to the last element of an array object, the
expression (P)+1 points one past the last element of the array object,
and if the expression Q points one past the last element of an array
object, the expression (Q)-1 points to the last element of the array
object. If both the pointer operand and the result point to elements
of the same array object, or one past the last element of the array
object, the evaluation shall not produce an overflow; otherwise, the
behavior is undefined. — [expr.add] 5.7 /5