int main(int argc, const char** argv) {
std::cout << "Hello" << std::endl;
char arr2d[][4] = {"ABC", "DEF"};
for (char *i : arr2d)
{
std::cout << i << std::endl;
}
In here, I evaluate the job of forrange as this: "For each character array in arr2d, print it it to console". And this works, so, my understanding, at least, should be correct. The output to the above code snippet is,
muyustan#mint:~/Desktop/C_Files/oop$ g++ main.cpp -o main && ./main
Hello
ABC
DEF
as expected.
However, if I try this one,
int main(int argc, const char** argv) {
std::cout << "Hello" << std::endl;
char arr2d[][4] = {"ABC", "DEF"};
for (const char *i : argv)
{
std::cout << i << std::endl;
}
First the IDE warns me with,
this range-based 'for' statement requires a suitable "begin" function and none was found
And if I try to compile, I get:
muyustan#mint:~/Desktop/C_Files/oop$ g++ main.cpp -o main && ./main
main.cpp: In function ‘int main(int, const char**)’:
main.cpp:30:26: error: ‘begin’ was not declared in this scope
for (const char *i : argv)
^~~~
main.cpp:30:26: note: suggested alternative:
In file included from /usr/include/c++/7/string:51:0,
from /usr/include/c++/7/bits/locale_classes.h:40,
from /usr/include/c++/7/bits/ios_base.h:41,
from /usr/include/c++/7/ios:42,
from /usr/include/c++/7/ostream:38,
from /usr/include/c++/7/iostream:39,
from main.cpp:1:
/usr/include/c++/7/bits/range_access.h:105:37: note: ‘std::begin’
template<typename _Tp> const _Tp* begin(const valarray<_Tp>&);
^~~~~
main.cpp:30:26: error: ‘end’ was not declared in this scope
for (const char *i : argv)
^~~~
main.cpp:30:26: note: suggested alternative:
In file included from /usr/include/c++/7/string:51:0,
from /usr/include/c++/7/bits/locale_classes.h:40,
from /usr/include/c++/7/bits/ios_base.h:41,
from /usr/include/c++/7/ios:42,
from /usr/include/c++/7/ostream:38,
from /usr/include/c++/7/iostream:39,
from main.cpp:1:
/usr/include/c++/7/bits/range_access.h:107:37: note: ‘std::end’
template<typename _Tp> const _Tp* end(const valarray<_Tp>&);
So, why argv behaves differently than my arr2d[][4] ? Aren't both of them pointers of char pointers(char arrays or strings(?)) ?
And if something wrong with my understanding, what should be the structre of printing ingreditens of argv with a forrange?
this is something I have been told wrong then, when I was dealing with C back then, at somewhere I have read that "arrays are also pointers!".
There are couple of finer points one must understand regarding that statement.
Arrays decay to pointers in most contexts and but arrays are still different than pointers.
When used as argument to sizeof, the following two will result in different answers.
char const* ptr = "Some text.";
char array[] = "some text.";
std::cout << sizeof(ptr) << std::endl; // prints sizeof the pointer.
std::cout << sizeof(array) << std::endl; // prints sizeof the array.
When used as an argument to the addressof operator.
char const* ptr1 = "Some text.";
char array[] = "some text.";
char const** ptr2 = &ptr1; // OK.
char** ptr3 = &array; // Error. Type mismatch.
char (*ptr4}[11] = &array; // OK.
2D arrays can decay to pointers to 1D arrays but they don't decay to pointers to pointers.
int array1[10];
int* ptr1 = array1; // OK. Array decays to a pointer
int array2[10][20];
int (*ptr2)[20] = array2; // OK. 2D array decays to a pointer to 1D array.
int** ptr3 = array2; // Error. 2D array does not decay to a pointer to a pointer.
They behave differently because they have different types. This is confusing for beginners, but:
char **
is a pointer to pointer to char. In fact, in the case of argv, it is pointing to a sequence of pointers, each pointing to a nul-terminated string (which are sequences of characters).
The problem iterating those is that the size of those sequences is not known. The compilers cannot know argc is related to the first sequence mentioned above.
However:
char arr2d[][4] = {"ABC", "DEF"};
resolves to the type:
char [2][4]
Which is an array of arrays of char. In this case, the size is known (2), so you can iterate over it.
Finally, the compiler complains about std::begin because the range-based for loop is transformed into different equivalent code which uses std::begin etc. to do the iteration.
If you want to apply a range-based for to argv, it's probably easiest if you start by creating a vector containing the arguments:
#include <iostream>
#include <vector>
int main(int argc, char **argv){
std::vector<std::string> args(argv, argv+argc);
for (auto const &arg : args) {
std::cout << arg << "\n"; // don't use `endl`.
}
}
As far as argv compared to a 2D array, the difference is fairly simple. When you have an array declaration, the array can be passed by reference to a template, which can figure out its size:
template <class T, size_t N>
size_t array_size(T (&array)[N]) {
return N;
}
int foo[2][3];
std::cout << array_size(foo) << "\n";
char bar[12][13][14];
std::cout << array_size(bar) << "\n";
...but, argv doesn't have a statically visible definition like that, from which the compiler would be able to deduce its size. In a typical case, there's code that runs outside main that inspects the command line, and allocates it dynamically instead.
The range-for expression works with iterators (pointers are a type of iterator), and it requires an iterator to the beginning and end of the range. It obtains those by passing the range to std::begin and std::end.
The type of arr2d is char[2][4]. As an array, it carries information about its size as part of its type. There are template overloads for std::begin and std::end that accept a reference to an array and return a pointer to its first and one-past-the-last element, respectively.
The type of argv is char**. It's just a pointer to a pointer to a char. The compiler has no idea either of those pointers point to the first element of an array, and these pointers carry no information about the length of the array that they point to. As such, there are no overloads of std::begin and std::end that accept a pointer since there's no way for std::end to figure out where the end of the array is in relation to the beginning from a pointer alone.
To use pointers with a range-for, you must provide the information about the array's length. In this case, you could construct a simple view over the array, since you know its length from argc:
template <typename T>
class PointerRange
{
private:
T* ptr_;
std::size_t length_;
public:
PointerRange(T* ptr, std::size_t length)
: ptr_{ptr},
length_{length}
{
}
T* begin() const { return ptr_; }
T* end() const { return ptr_ + length_; }
};
int main(int argc, char** argv)
{
for (char* arg : PointerRange(argv, argc)) {
std::cout << arg << "\n";
}
}
Live Demo
Once C++20 becomes available, std::span can take the place of the PointerRange defined above:
int main(int argc, char** argv)
{
for (std::string_view arg : std::span{argv, argc}) {
std::cout << arg << "\n";
}
}
If your compiler has the span header (not a lot of them do right now) I imagine this would work.
// Example program
#include <iostream>
#include <string_view>
#include <span>
int main(int argc, char **argv)
{
for (std::string_view s : std::span{argv, argc})
{
std::cout << s << std::endl;
}
return 0;
}
The only overhead with my example is having string_view find the null terminal. I tried it goldbolt.org but it seems that none of the compilers can find the span header. So take my advice lightly.
Related
int main(int argc, char** argv) {
char a[2][5]={"hell","worl"};
char **p;
p=a; // error here
cout<<*(*(a+1)+1);
cout<<endl;
cout<<(*a)[2];
return 0;
}
error:
C:\Dev-Cpp\main.cpp [Error] initializer-string for array of chars is too long [-fpermissive]
Why would you expect it to work? You declare p as char**,
and you try to assign a char[2][5] to it. The char[2][5]
will convert implicitly to a char (*)[5], but afterwards, you
have a pointer, and no further implicit conversions. (EDIT: except to void*.)
If you think about it, it should be obvious. If you dereference
a char**, you get a char*. And this char* must reside
somewhere in memory, since you have a pointer to it. So where
is it?
If you want to iterate over the outer array in your example:
char (*p)[5] = a;
std::cout << *p[0] << sdt::endl;
std::cout << *p[1] << sdt::endl;
Note that your expression *(*(a+1)+1) also supposes that you
have an array of pointers somewhere.
Or you can use the usual solution when working with C style
strings:
char const* const a[] = { "hell", "worl" };
and
char const* const* p = a;
In this case, you do have an array of pointers, which does
implicitly convert to a pointer to a pointer (the first element
of the array).
(Of course, the only time you'll really want to use C style
strings is with const variables with static lifetimes. In
most other cases, std::string is preferable.)
Other way to access the a[2][5] is,
char **p=(char**)a;
to get a[0]
printf("\n a[0] is [%s]", ((char*)p));
to get a[1]
printf("\n a[1] is [%s]", (((char*)p) + strlen(a[0])+1));
hope this helps.
I have defined some structure above main as:
struct arguments
{
int c;
char *v[];
};
now I want to do this in main:
int main(int argc, char *argv[])
{
arguments arg;
arg.c = argc;
arg.v = argv; /* error: incompatible types in assignment of 'char** to char* [0]' */
}
So, I do not fully understand what I am doing, but instinctively I remade the structure such that the line char *v[]; is instead char **v[]; however, this means when I pass by reference my structure arg, if I want to dereference and get the value of arg.v[0], which will be the program name, I now have to do *arg.v[0] and this no longer works for *arg.v[1].
I have a function such as:
void argument_reader(arguments &arg)
{
cout << "Number of arguments: " << arg.c << endl << endl;
cout << "Array\t\tValue\n";
cout_bar(40);
for (int i = 0; i < arg.c; i++)
{
cout << "argv[" << i << "]\t\t" << *arg.v[i] << endl;
}
return;
}
but when I call *arg.v[i] the program ends without printing the value of the second argument or any others for that matter.
So my question is, is there some way I can use the struct to pass between functions such that I can call arg.v[some_index < arg.c] inside another function by somehow making the line arg.v = argv in main work as I had intended (to store the address of argv in arg.v)
All the structure does is make it so that instead of having to pass argc and argv to a function which has a decleration like: void func(int &argc, char *argv[]); I can instead pass my new structure type with variable name arg to a function declared like void func(arguments &arg) and that is basically all I wanted the structure for. So if I cannot do this, then I can go back to the first method.
You need to write:
struct arguments
{
int c;
char **v;
};
Now your main() will work. (And this sort of confusion is why I always use int main(int argc, char **argv), but that's a personal quirk.)
If you were working in C99 or later, what you created with your structure is what is called a 'flexible array member' (FAM), an addition to C99 over C89 (and therefore over C++98). I didn't notice that you're working in C++! There are special rules about FAM in C, and you can't do the assignment as you did with a flexible array member.
However, the fix will work in C++ just as much as in C; it is just not necessarily correct that you're using an FAM. Are you using a C++ compiler that supports FAM as an extension?
In your output line, you have:
cout << "argv[" << i << "]\t\t" << *arg.v[i] << endl;
That would print the first character of the argument; drop the * and you'd get the first argument as a string:
cout << "argv[" << i << "]\t\t" << arg.v[i] << endl;
Change your struct to this:
struct arguments
{
int c;
char **v;
};
C-style array and pointer are very similar, and you can use above struct just like your version or the original argv parameter.
Indeed, often main function is declared like that instead of using []:
int main(int argc, char **argv)
Note that with this answer code v points to the original argv array of pointers to char, pointers are not copied. If you actually want a copy, you need to dynamically allocate array of char pointers and copy.
two things:
struct arguments
{
int c;
char *v[]; /*unknown length, hence structure is incomplete*/
};
You need a double pointer, which holds the address of the argv
struct arguments {
int c;
char **v;
};
From the c++0x Wikipedia site:
int my_array[5] = {1, 2, 3, 4, 5};
for (int &x : my_array) {
x *= 2;
}
So why does this code not work?
int main(int argc, char* argv[])
{
for (char *arg : argv)
{
// Do something.
}
}
Error:
main.cpp:36: error: no matching function for call to ‘begin(char**&)’
I am using Qt with g++ 4.6.1 on Ubuntu 11.10.
Additional Information
Is There a Range Class in C++0x
Range-Based For-Loop Statement Definition Redundance
Usually, the first thing I do with argc and argv is this:
std::vector<std::string> arguments(argv, argv + argc);
Now I have a vector of strings to work with and I can easily use not only the range-based for loops, but also C++ standard library facilities.
for(std::string& s : arguments) {
// do stuff...
}
The wikipedia code works because the type of my_array is a variable of array type.
The original code does not work, because argv is not an array. The syntax char* argv[] may make it look like it is an array, but that's just a sad artifact of C syntax. char* argv[] is exactly the same as char** argv. argv is not an array; it's actually just a pointer.
The range-based for loop works on:
arrays;
any type that has member functions begin() and end() that return iterators;
any type for which exist non-member functions begin and end that can be called like begin(x) and end(x), with x being the thing that you're iterating over.
As you can see, pointers are not part of this list.
You don't, because the system can't tell how long argv is at compile time. Someone can likely find the proper section of the standard to quote you about this.
There is a way around it though, and that's to create a custom class to wrap argv. It's not even that hard.
class argv_range {
public:
argv_range(int argc, const char * const argv[])
: argc_(argc), argv_(argv)
{
}
const char * const *begin() const { return argv_; }
const char * const *end() const { return argv_ + argc_; }
private:
const int argc_;
const char * const *argv_;
};
Here's how you use it:
for (const char *arg: argv_range(argc, argv)) {
// Do something.
}
Yeah, I use a lot of consts. Basically, argv is an array of character pointers, none of which should be modified, each pointing to a string, none of the characters of which should be modified either.
The vector solution proposed copies the array (the pointers only, not the strings1 - but still). Unnessary. The argv_range solution is what I would have tried, too, if I absolutely wanted to enforce a range based loop. But that produces a lot of extra code (admitted, only once, if you write it to a header file and keep it, but still).
The classic loop appears so easy to me that I allow myself just to post it, I don't consider it worth to have all this effort just to have a range based loop...
for (char** a = argv; *a; ++a)
{
// use *a, or if you don't like:
char* arg = *a;
// use arg...
}
Or, if you won't ever need the argv array afterwards any more:
for (++argv; *argv; ++argv)
{
// use *argv, or if you don't like:
char* a = *argv;
// use a...
}
There is a little difference, you might have noticed: In the first variant, I iterate over all the values, in the second, I leave out the first one (which normally is the program name we are not interested in in many cases). The other way round, for each:
for (char** a = argv + 1; *a; ++a);
for (; *argv; ++argv);
Note that all these variants profit from the fact that the strings array itself is null-pointer-terminated as well, just as any of the strings is null-character-terminated, thus the simple checks for *a or *argv.
1This applies only, if using std::vector<char*>; if using std::vector<std::string>, as actually proposed, even the strings themselves are copied!
You can use C++20 std::span() or gsl::span() to make view for argv:
for (auto const arg : std::span(argv, argc)) {
std::cout << arg << '\n';
}
Or similar, using Ranges (std::view::counted()):
for (auto const arg : std::views::counted(argv, argc)) {
std::cout << arg << '\n';
}
or directly using std::ranges::subrange:
for (auto const arg : std::ranges::subrange{argv, argv+argc}) {
std::cout << arg << '\n';
}
argv is a double pointer, argv**
So where &x creates a reference to the element in the array, your *arg does not create a reference but rather is the same type as every element in argv.
I take you'd like the range based loop in order to not have to write that much..
so to annoy c++ purists, while at the same time not exactly answering your question, we'd like to post some nicely unsafe ways to do it without any std::vector or OO-wrapper-crap :)
for (;argc--; argv++) {
printf(" %s \n ", *argv);
}
or
while (argc--) {
printf(" %s \n ", *(argv++));
}
or even
while (*argv) {
printf(" %s \n ", *(argv++));
}
I have the following function in C++ :
char** f()
{
char (*v)[10] = new char[5][10];
return v;
}
Visual studio 2008 says the following:
error C2440: 'return' : cannot convert from 'char (*)[10]' to 'char **'
What exactly should the return type to be, in order for this function to work?
char** is not the same type as char (*)[10]. Both of these are incompatible types and so char (*)[10] cannot be implicitly converted to char**. Hence the compilation error.
The return type of the function looks very ugly. You have to write it as:
char (*f())[10]
{
char (*v)[10] = new char[5][10];
return v;
}
Now it compiles.
Or you can use typedef as:
typedef char carr[10];
carr* f()
{
char (*v)[10] = new char[5][10];
return v;
}
Ideone.
Basically, char (*v)[10] defines a pointer to a char array of size 10. It's the same as the following:
typedef char carr[10]; //carr is a char array of size 10
carr *v; //v is a pointer to array of size 10
So your code becomes equivalent to this:
carr* f()
{
carr *v = new carr[5];
return v;
}
cdecl.org helps here:
char v[10] reads as declare v as array 10 of char
char (*v)[10] reads as declare v as pointer to array 10 of char
A pointer to pointers is not the same as a pointer to arrays.
(In particular, notice how sizeof(*ptr1) is sizeof(char)*6, whereas sizeof(*ptr3) is sizeof(char*) — this has serious ramifications for pointer arithmetic.)
new char[5][10] gives you a char (*)[10] (which has absolutely nothing to do with function pointers, by the way), because the pointers and chars are laid out in that fashion in memory (my second example).
This is not the same as char** (which represents a different layout), so a conversion between the two makes no sense; hence, it is disallowed.
So your function's return type must be char (*)[10]:
char (*f())[10] {
char (*v)[10] = new char[5][10];
return v;
}
Of course, this is really ugly, so you're far better off with a std::vector<std::string>.
This FAQ entry explains it best, under the title "Conversions".
Because char** and char (*)[10] are two different types. char** is a pointer to pointer(to char), while char (*)[10] is a pointer to an array(of size 10) of char. Resultantly, the moving step of char** is sizeof(void *) bytes which is 4 bytes on win32 platform, and the moving step of char (*)[10] is sizeof(char) * 10 bytes.
Example
char *c = NULL;
char **v = &c;
cout << typeid(v).name() << endl;
cout << (void*)v << endl;
v += 1;
cout << (void*)v << endl;
char d[10];
char (*u)[10] = &d;
cout << typeid(u).name() << endl;
cout << (void*)u << endl;
u += 1;
cout << (void*)u << endl;
Output
char * *
0034FB1C
0034FB20
char (*)[10]
001AFC50
001AFC5A
To use char (*)[10] as a function's return type(or as input/output parameter of the function), the easiest and most readable way is to use a typedef:
// read from inside-out: PTRTARR is a pointer, and, it points to an array, of chars.
typedef char (*PTRTARR)[10];
Note that it can easily be mixed up with typedef of an array of pointers, if not careful:
// operator[] has higher precedence than operator*,
// PTRARR is an array of pointers to char.
typedef char *PTRARR[10];
Reference
Arrays and Pointers
If I change the type to const char str[Len], I get the following error:
error: no matching function for call to ‘static_strlen(const char [5])’
Am I correct that static_strlen expects an array of const char references? My understanding is that arrays are passed as pointers anyway, so what need is there for the elements to be references? Or is that interpretation completely off-the-mark?
#include <iostream>
template <size_t Len>
size_t
static_strlen(const char (&str)[Len])
{
return Len - 1;
}
int main() {
std::cout << static_strlen("oyez") << std::endl;
return 0;
}
No, the function parameter is a reference to an array of Len const chars. That's how the function knows the length (assuming the last byte is a NUL terminator, hence the -1). The parentheses are there precisely to stop it being what you think it is.
Actually there's no such thing in C++ as an array of references, so it couldn't be what you think it is even without the parens. I guess (but am not sure) that the need for the parens is just for consistency with other similar type definitions, such as pointers to arrays:
void fn(const char *a[3]); // parameter a is of type const char**, the 3 is ignored.
void fn(const char (*a)[3]; // parameter a is a pointer to an array of 3 const chars.
That example also illustrates why an array is not a pointer. Predict the output of the following program, and then run it:
#include <iostream>
void fn(const char (*a)[3]) {
std::cout << sizeof(a) << "\n" << sizeof(*a) << "\n";
}
void fn2(const char *a[3]) {
std::cout << sizeof(a) << "\n" << sizeof(*a) << "\n";
}
int main() {
const char a[3] = {};
const char **b = 0;
fn(&a);
fn2(b);
}
#if 0
// error: declaration of `a' as array of references
void fn3(const char & a[3]) {
std::cout << sizeof(a) << "\n" << sizeof(*a) << "\n";
}
#endif
This is one of the way that a function can be made such that the size of the array is passed into the function automatically.
static_strlen(const char (&str)[Len])
is a function that takes in an array of const char of exactly Len elements. The array size must be known at compile time. I.e. the array wasn't allocated via new or malloc.
To be more specific, the parameter is a reference to an array of Len elements, rather than an actual array, which is why it doesn't get converted into a pointer when passed.