What is ++argv? & confusing compiler error - c++

This code was provided by my c++ instructor on a sample midterm. We covered argc and argv on a very basic level, so I understand what those expressions alone mean. However, any additions to those like the dereference operator and '++' confuse me and what I've googled hasn't been clear enough for me to apply it to this specific example. Also, when I try to compile, it provides this error:
In function 'int summer(int*, char**)':
Line 9: error: cannot convert 'char**' to 'const char*' for argument '1' to 'int atoi(const char*)'
compilation terminated due to -Wfatal-errors.
We haven't officially covered pointers, but I think I understand them well enough. char** means you're dereferencing twice, so the value of the pointer char, which is a pointer to something else, so the second * means we want the value of that. Is it constant because we are relying on something input in the command line and that can't be changed? I don't understand this.
#include <iostream>
using namespace std;
int summer(int *acc, char * ptr[])
{
register int n;
n = atoi(ptr);
*acc = *acc + n;
}
main(int argc, char * argv [])
{
int sum = 0;
while (--argc)
summer(&sum, *++argv);
cout << "sum is " << sum << endl;
}
Another question: When passing *++argv to summer(), does that mean the value of (argv[] + 1)? What would that even be? Adding the value one to the entire vector? I know that atoi(array) means changing the string into a numerical value, and then we are storing that in register int 'n', and then adding that to the sum which is directly changed in main. And that's about the only part of this code I really understand. Sorry if my questions/this post is kind of a mess.
To sum up what my questions are:
What does the compiling error message mean?
What does ++argv do?
Thank you.
Edited:
Okay, I've made the changes you guys have suggested (thank you!!):
#include <iostream>
using namespace std;
int summer(int *acc, char * ptr)
{
register int n;
n = atoi(ptr);
*acc = *acc + n;
return 0;
}
int main(int argc, char * argv[])
{
int sum = 0;
while (--argc)
summer(&sum, *++argv);
cout << "sum is " << sum << endl;
return 0;
}
I also added the returns because the compiler gave a new error due to no return value for the function type. On Codepad it compiles and prints "the sum is zero," however, it still does not compile on Dev C++ 5.7.1 which is what I am using. It doesn't display error messages (the ones I have been reading are from Codepad online compiler), or at least I can't find where to turn them on. It just highlights the lines it seems to have a problem with, and that is still the line with atoi(ptr).

*argv is equivalent to argv[0], so ++argv; would make *argv equivalent to what was originally argv[1]. So *++argv evaluates to the same thing as argv[1] (the first command line argument, after the name of the program itself), except it increments argv whereas argv[1] obviously does not.
For your error, you should change:
int summer(int *acc, char * ptr[])
{
to:
int summer(int *acc, char * ptr)
{
When you dereference a char **, which is the type of argv, you get a char *. char * is what atoi() is expecting.
Your program is essentially equivalent to this:
#include <iostream>
#include <cstdlib>
using namespace std;
void summer(int * acc, char * ptr)
{
*acc += atoi(ptr);
}
int main(int argc, char * argv [])
{
int sum = 0;
for ( int i = 1; i < argc; ++i ) {
summer(&sum, argv[i]);
}
cout << "sum is " << sum << endl;
}
except that your's doesn't check for troublesome values of argc.

The ++ is called the increment operator. It usually adds 1 to something.
The argv decays to a pointer that points to a container of arguments to your program.
The expression ++argv points to next parameter, if there is one.

Incrementing a pointer means to make it point to the next item in the list.
argv has type char ** (don't be distracted by the square brackets), meaning it points to a char * which is in a list of adjacent char *s. The argc parameter lets us know where to find the end of said list.
So, doing ++argv means that argv will now point to the next char * in the list. Applying * to that (and passing the result to a function) means that we send the value of the next char * in the list to that function.
The result of all this is that the function is called once for each argument, excluding the first one (since we ++'d before dereferencing the first time).
NB. This code actually has a bug; if argc == 0 it will go off into la-la land. It should check for this case before entering the loop.

Is it constant because we are relying on something input in the command line and that can't be changed?
No, the const char * bit refers to the type of argument that atoi() accepts. It is a pointer to a constant char. Note that char * is implicitly convertible to const char *, but not the opposite.
All this means is that atoi() accepts a pointer to a character, and it promises that it will not modify the target of the pointer.
My guess is that the ptr argument should have been char * ptr instead of char * ptr[]. If you change that, the code should compile.

Related

Confusion about argc argv and env

I just don't know how to explain my question precisely. So I wrote that title above.
Here is my confusion about a very simple program. Exactly, the results.
#include <iostream>
using namespace std;
char * tmp[]={"aaa", "bbb", "ccc"};//there are 3 members
int main(int argc, char* argv[], char* env[])
{
cout << sizeof(env)/sizeof(char*) << endl;
cout << sizeof(tmp)/sizeof(char*) << endl;
}
Results:1 3
What I want is the length of env[]. How can this possible that I got number 1 of env[], while the length of 'tmp'(3) is absolutely rigth.
There's no way that the length of env is 1, cause I tested it and the number is 47.
Why this happened?
Thanks!
The difference is that tmp is an array, whereas env is a pointer. Arrays and pointers are different. It's a bit confusing because array syntax in a function formal parameter list is actually a pointer in disguise.
There is no way to get the number of elements pointed to by env using sizeof. You have to go through them until you find the NULL element terminating the list.
The important thing to remember is that argv and env are pointers, not arrays. In the context of a function parameter declaration, T a[] and T a[N] are interpreted as T *a, so the types of both argv and env are char **, not char *[N].
The only way to determine how many elements each points to is by iterating through them until you find a NULL pointer:
size_t i;
for ( i = 0; argv[i] != NULL; i++ )
; // empty loop body
printf( "There are %zu elements in argv\n", i );
for ( i = 0; env[i] != NULL; i++ )
; // empty loop body
printf( "There are %zu elements in env\n", i );
The language C has certain syntactical features that are convenient for the experienced programmer, but confusing for the new learner. While the syntax T * X[] looks the same in your two cases, they actually mean two very different things:
int a[] = { 1, 2, 3}; is the same as int a[3] = { 1, 2, 3 };. Here a is an array, and the size of the array is deduced from the initializer.
void f(int b[]) is the same as void f(int * b). Here b is a pointer, and the notation is merely suggestive of the fact that you should call f with a pointer to an element of an array. For instance, you could call f(a), or equivalently f(&a[0]), or even f(a + 2). But the declarator syntax is purely a cosmetic convenience, and b is not an array, but a pointer.
This has little to do with these being parameters to main, sizeof anything is a compile time expression. Obviously, the environment and arguments passed to your program aren't. sizeof(array)/sizeof(element_type) is only the length of the array if that array was declared with a static size. Otherwise, sizeof(array) will be equivalent to sizeof(element*) (and because sizeof(char**) == sizeof(char*) you get 1 in your program).

c++ pointers:-why is this code not running

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.

Inconsistent lvalue required error when incrementing array variable

I am getting "lvalue required as increment operand" while doing *++a. Where I am going wrong? I thought it will be equivalent to *(a+1). This behaviour is weird as *++argv is working fine. Please help.
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("Arg is: = %s\n", *++argv);
int a1[] = {1,2,3,4,5,6};
int a2[] = {7,8,9,10,11,12};
int *a[2];
a[0] = a1;
a[1] = a2;
printf("ptr = %d\n", *++a);
return 0;
}
a is a constant(array name) you can't change its value by doing ++a, that is equal to a = a + 1.
You might wanted to do *a = *a + 1 (increment 0 indexed value), for this try *a++ instead. notice I have changed ++ from prefix to postfix.
Note: char* argv[] is defined as function parameter and argv is pointer variable of char** type ( char*[]).
Whereas in your code int* a[] is not a function parameter. Here a is an array of int type (int*)(array names are constant). Remember declaration in function parameter and normal declarations are different.
Further, by using ++ with argv and a you just found one difference, one more interesting difference you can observe if you use sizeof operator to print there size. for example check this working code Codepade
int main(int argc, char* argv[]){
int* a[2];
printf(" a: %u, argv: %u", sizeof(a), sizeof(argv));
return 1;
}
Output is:
a: 8, argv: 4
Address size in system is four bytes. output 8 is size of array a consists of two elements of type int addresses (because a is array), whereas 4 is size of argv (because argv is pointer).
++a and a + 1 are not equivalent. ++a is the same as a = a + 1, i.e it attempts to modify a and requires a to be a modifiable lvalue. Arrays are not modifiable lvalues, which is why you cannot do ++a with an array.
argv declaration in main parameter list is not an array, which is why you can do ++argv. When you use a[] declaration in function parameter list, this is just syntactic sugar for pointer declaration. I.e.
int main(int argc, char *argv[])
is equivalent to
int main(int argc, char **argv)
But when you declare an array as a local variable (as in your question), it is indeed a true array, not a pointer. It cannot be "incremented".
Arrays are not pointers. a is an array; you cannot increment an array.

Compilation error while passing double pointer in cpp

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
void f(char **x)
{
(*x)++;
**x = 'a';
}
int main()
{
char str[]="hello";
f(&str);
cout << str << endl;
return 0;
}
Please tell me why this program is giving compilation Error.I am using the g++ compiler
Error :temp1.cpp:16:8: error: cannot convert ‘char (*)[6]’ to ‘char**’ for
argument ‘1’ to ‘void f(char**)’
Arrays can be implicitly converted to pointers, but that doesn't mean that the implicit "pointer equivalent" already exists.
You are hoping that f(&str); will implicitly create both a pointer to str and a pointer to that pointer.
This small (working) change illustrates this point:
int main()
{
char str[]="hello";
char *pstr = str; // Now the pointer extists...
f(&pstr); // ...and can have an address
cout << str << endl;
return 0;
}
You are passing pointer of constant char to the function but in function you are taking it as pointer of pointers. That is the problem. I commented out below where the problem lies.
[Off topic but N. B. : Arrays and pointers are different concept.]
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
void f(char **x) //**x is pointer of pointer
{
(*x)++;
**x = 'a';
}
int main()
{
char str[]="hello";
f(&str); //You are passing pointer of constant char.
cout << str << endl;
return 0;
}
You're going to run into a serious problem with your function f since &str and &str[0] both evaluate to the same value ... as other posters have pointed out, these operations point to different types, but the actual pointer r-value will be the same. Thus in f when you attempt to double-dereference the char** pointer x, you're going to get a segfault even if you attempted something like a cast to massage the type differences and allow compilation to happen with errors. This is because you are never getting a pointer-to-pointer ... the fact that &str and &str[0] evaluate to the same pointer value means that a double-dereference acually attempts to use the char value in str[0] as a pointer value, which won't work.
Your problem is that you're treating arrays as pointers, when they're not. Arrays decay into pointers, and in this case, it doesn't. What you're passing in is a char (*)[6] when it expects a char **. Those are obviously not the same.
Change your parameter to char (*x)[6] (or use a template with a size parameter):
template <std::size_t N>
void f(char (*x)[N])
Once inside, you try to increment what x is pointing to. You can't increment an array, so use an actual pointer instead:
char *p = *x;
p++;
*p = 'a';
All put together, (sample)
template <std::size_t N>
void f(char(*x)[N])
{
if (N < 2) //so we don't run out of bounds
return;
char *p = *x;
p++;
*p = 'a';
}

Incompatible types in assignment char ** to char *

I have the following bit of code:
As a global variable:
char *orderFiles[10];
And then my main method:
int main(int argc, char *argv[])
{
orderFiles = argv;
}
However it keeps giving me an error. What am I doing wrong?
It's giving you an error because char *x[10] gives you an array of ten char pointers which is non-modifiable. In other words, you cannot assign to x, nor change it in any way. The equivalent changeable version would be char **orderFiles - you can assign argv to that just fine.
As an aside, you could transfer individual arguments to your array thus:
for (i = 0; i <= argc && i < sizeof(orderFiles)/(sizeof(*orderFiles); i++)
orderFiles[i] = argv[i];
but that seems rather convoluted. It will either fill up orderFiles with the first N arguments or partially fill it, making the next one NULL.
If your intent is simply to stash away the arguments into a global so that you can reference them anywhere, you should do something like:
#include <stdio.h>
char **orderFiles;
int orderCount;
static void someFn (void) {
int i;
printf ("Count = %d\n", orderCount);
for (i = 0; i < orderCount; i++)
printf ("%3d: [%s]\n", i, orderFiles[i]);
// or, without orderCount:
// for (i = 0; orderFiles[i] != NULL; i++)
// printf ("%3d: [%s]\n", i, orderFiles[i]);
// printf ("Count was %d\n", i);
}
int main (int argc, char **argv) {
orderCount = argc;
orderFiles = argv;
someFn();
return 0;
}
That code saves the arguments into globals so they can be accessed in a different function.
You should save both arguments to main if you want to use argc as well although, technically, it's not necessary since argv[argc] is guaranteed to be NULL for hosted environments - you could use that to detect the end of the argument array.
orderFiles is a const char **, you can't modify it (the array pointer itself).
You could try assigning the array members (i.e. orderFiles[0] = argv[0] and so on).
The problem is that there is a difference between arrays initialized without a length, and those initialized with one. Remove the 10 from the global variables declaration, and then it should work
The reason for this is that argv is really just a char**, but orderFiles is an array of 10 char*.
There is an implicit char** for orderFiles yes, but it's constant because you initialized it to a link time allocated block of memory by specifying the size [10]. You should create a non-constant char** or maybe memcpy from argv to your array.
Like they all said two different data types. In other terms think of it this way: argv is an array of c-strings, and your orderFiles is declared as a single c-string.
So how to assign orderFiles depends on what you're trying to do. I typically iterate through argv to get the arguments passed to the application. Note that argv[0] is the application name.