C++ novice here. I have some basic questions. In int main( int argc, char *argv[] )
How is char *argv[] supposed to be read (or spoken out to humans)?
Is it possible to clear/erase specific content(s), character(s) in this case, of such array? If yes, how?
Can arrays be resized? If yes, how?
How can I copy the entire content of argv[] to a single std::string variable?
Are there other ways of determining the number of words / parameters in argv[] without argc? If yes, how? (*)
I'd appreciate explanations (not code) for numbers 2-5. I'll figure out the code myself (I learn faster this way).
Thanks in advance.
(*) I know that main(char *argv[]) is illegal. What I mean is whether there's at least a way that does not involve argcat all, like in the following expressions:
for( int i = 0; i < argc; ++i ) {
std::cout << argv[i] << std::endl;
}
and
int i = 0;
while( i < argc ) {
std::cout << argv[i] << std::endl;
++i;
}
Or
int i = 0;
do {
std::cout << argv[i] << std::endl;
++i; } while( i < argc );
It's an array of pointers to char.
Sort of - you can overwrite them.
Only by copying to a new array.
Write a loop and append each argv[i] to a C++ string.
Most implementations terminate the array with a NULL pointer. I can't remember if this is standard or not.
char **argv[]
Is wrong. It should be either char **argv or char *argv[], not a mixture of both. And then it becomes a pointer-to-pointer to characters, or rather a pointer to c-strings, i.e., an array of c-strings. :) cdecl.org is also quite helpful at thing like this.
Then, for the access, sure. Just, well, access it. :) argv[0] would be the first string, argv[3] would be the 4th string. But I totally wouldn't recommend replacing stuff in an array that isn't yours or that you know the internals of.
On array resize, since you're writing C++, use std::vector, which does all the complicated allocation stuff for you and is really safe. Generally, it depends on the array type. Dynamically allocated arrays (int* int_arr = new int[20]) can, static arrays (int int_arr[20]) can't.
To copy everything in argv into a single std::string, loop through the array and append every c-string to your std::string. I wouldn't recommend that though, rather have a std::vector<std::string>, i.e., an array of std::strings, each holding one of the arguments.
std::vector<std::string> args;
for(int i=0; i < argc; ++i){
args.push_back(argv[i]);
}
On your last point, since the standard demands argv to be terminated by a NULL pointer, it's quite easy.
int myargc = 0;
char** argv_copy = argv;
while(++argv_copy)
++myargc;
The while(++argv_copy) will first increment the pointer of the array, letting it point to the next element (e.g., after the first iteration it will point at c-string #2 (argv[1])). After that, if the pointer evaluates to false (if it is NULL), then the loop brakes and you have your myargc. :)
Several options: array of pointer to char OR array of C-string.
You can assign to particular characters to clear them, or you can shift the rest of the array forwards to "erase" characters/elements.
Normal C-style arrays cannot be resized. If you need a resizable array in C++ you should use std::vector.
You'll have to iterate over each of the items and append them to a string. This can be accomplished with C++ algorithms such as copy in conjunction with an ostream_iterator used on an ostringstream.
No. If there was such a way, there wouldn't be any need for argc. EDIT: Apparently for argv only the final element of the array is a null pointer.
1) It is supposed to be char **argv or char *argv[] which is a pointer to an array of characters more commonly known as an array of strings
2) CString is the std library to manipulate C strings (arrays of characters). You cannot resize an array without reallocating, but you can change the contents of elements by referencing it by index:
for(int i = 0; i < argc; ++i)
{
//set all the strings to have a null character in the
//first slot so all Cstring operations on this array,
//will consider it a null (empty) string
argv[i] = 0;
}
3) Technically no, however they can be deleted then reallocated:
int *array = new int[15]; //array of size 15
delete[] array;
array = new int[50]; //array of size 50
4) This is one way:
string *myString;
if(argc > 0)
{
myString = new string(argv[0]);
for(int i = 1; i < argc; ++i)
myString->append(argv[i]);
}
5) Yes, according to Cubbi:
POSIX specifies the final null pointer
for argv, see for example "The
application shall ensure that the last
member of this array is a null
pointer." at
pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
Which means you can do:
char *val = NULL;
int i = 0;
do
{
val = argv[i++]; //access argv[i], store it in val, then increment i
//do something with val
} while(val != NULL); //loop until end of argv array
It is spoken as "array of pointers to pointers to character" (note that this is not the signature of the main function, which is either int argc, char **argv or int argc, char *argv[] -- which is equivalent).
The argv array is modifiable (lack of const). It is illegal to write beyond the end of one of the strings though or extend the array; if you need to extend a string, create a copy of it and store a pointer in the array; if you need to extend the array, create a copy of the array.
They cannot be resized per se, but reinterpreted as a smaller array (which sort of explains the answer to the last question).
You will be losing information this way -- argv is an array of arrays, because the individual arguments have already been separated for you. You could create a list of strings using std::list<std::string> args(&argv[1], &argv[argc]);.
Not really. Most systems have argv NULL terminated, but that is not a guarantee.
char *argv[] can be read as: "an array of pointers to char"
char **argv can be read as: "a pointer to a pointer to char"
Yes, you may modify the argv array. For example, argv[0][0] = 'H' will modify the first character of the first parameter. If by "erase/clear" you mean remove a character from the array and everything automatically shift over: there is no automatic way to do that - you will need to copy all the characters one-by-one over to the left (including the NULL termination)
No, arrays cannot be resized. You will need to create a new one and copy the contents
How do you want to represent ALL the parameter strings as 1 std::string? It would make more sense to copy it to an array of std::strings
No, there is no special indication of the last entry in the array. you need to use argc
Array in C/C++ is not an object, but just a pointer to first element of array, so you cannot simply delete or insert values.
Answering your questions:
char *argv[] can be read as 'array of pointers to char'
It's possible, but involves direct manipulations with data in memory, such as copying and/or moving bytes around.
No. But you may allocate new array and copy necesary data
By manually copying each element into std::string object
No.
As a summary: C++ is much more low-level language that you think.
Related
I am attempting to write a Vulkan application from scratch, and I have been having issues with attempting to count the total number of characters in a
const char**
I was wondering how I could iterate through each character of each "c string".
This is for the purpose of comparing existing instance extensions for creating a Vulkan instance.
This is the function I'm having issues with and every problem seems localized to the fact that sizeof gives back the size in bytes, not the number of elements.
void Extensions_Manager::GetInstanceExtensionNames(const char** extNames, const char** glfwNames, bool validation)
{
std::string extension = "";
if (validation)
instanceExtensionNames.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
for (int i = 0; i < sizeof(glfwNames); ++i)
{
if (glfwNames[i] == '\0')
{
instanceExtensionNames.push_back(extension.c_str());
slog("Instance extension to use %s", extension.c_str());
extension = "";
}
else
{
extension += glfwNames[i];
}
}
for (const char* ext : instanceExtensionNames)
{
slog("Available Extension: %s", ext);
}
extNames = instanceExtensionNames.data();
}
instanceExtensionNames
is a static vector of const char*
How to count the number of total chars in const char**
Loop over each string in the pointed array. For each string, count the number of chars. Add the lengths together.
Note that you may want to consider whether you want to count the number of char objects (i.e. code-units), or the number of character symbols which are not necessarily the same number depending on the character encoding.
sizeof(glfwNames)
sizeof has nothing to do with "number of elements". glfwNames is a pointer. sizeof(glfwNames) is the size of a pointer object. All pointers of particular type are always the same size (within a particular system) regardless of the object they point at.
There is no way of finding out the number of elements if given only a pointer to an element of the array and no other information. It is simply not possible because a pointer doesn't contain the necessary information. Without knowing when to stop iterating the array, there is no way to do the iteration correctly.
These are common ways of iterating a range (the range being an array in this case) of iterators (pointers being iterators for arrays):
Pass the length of the range as an argument, and use an indexed loop. This works with randomly accessible iterators such as pointers.
Pass an iterator (pointer in this case) to the end (one past the last element) of the range. This approach is more generic as it works with non-random access iterators as well. Increment the iterator in the loop, and once it equals to the end, you know what you've reached.
Designate certain value as a sentinel element (also known as a terminator). When a sentinel value is encountered, you know that iteration has reached the end of the range. This approach is used with null-terminated strings.
A side note: Interestingly, the command line arguments (argc + argv) provide both ways: argc is the length, but the char** argv is also terminated by a null pointer.
If you receive glfwNames from a library, then consult the documentation of the library for how to find out the length. If you're in control of both calling and defining the function, then make sure that you pass sufficient information into the function.
a string ends with a '\0' .
let's say u have *ptr = "string";
what u can do here is :
int i = 0;
while(ptr[i] != '\0')
i++;
cout << ptr[0];
return 0;
that will give you total characters of the string with '\0', else you can use strlen() which is in the string.h lib .
I have a vector of strings vector<string> args that I need to convert to a c string to in turn run as arguments through an execvp call. I keep getting the error "invalid conversion from const char* to char* for the line in my loop. I don't understand how to fix this while still resulting in something I can feed to execvp. While I have seen similar posts, none of the solutions I have come across seem to fix my issue.
int argsLen = c->args.size();
char **argv = new char* [c->args.size() + 1];
for (int index = 0; index < c->args.size(); ++index)
argv[index] = c->args[index].c_str();//note that c is the structure in which args is contained
argv[c->args.size() + 1] = NULL;
//execvp(argv[0], argv);
If you want to do this with no chance of leaking memory or memory overwrite, you can do the following:
typedef std::vector<char> CharArray;
typedef std::vector<CharArray> ArgumentVector;
//...
ArgumentVector argVec;
std::vector<char *>argv;
for (int index = 0; index < c->args.size(); ++index)
{
// get the command and create copy
argVec.push_back(CharArray(c->args[index].begin(), c->args[index].end()));
// get pointer to newly created entity and place in argv vector
argV.push_back(argVec.back().data());
}
// the last argument is NULL
argV.push_back(NULL);
execvp(argV[0], // the first item in the array
argV.data(), // the char** we are sending
All we did was create a std::vector<char*>, and the way we set the pointers in this vector is to point to data from another vector. Once the std::vector<char*> is populated, then char** is just a pointer to the first item.
BTW, this is one of the legitimate reasons to use a vector<char *> as opposed to a vector<string>. In most other cases, if you want a vector of strings, you use vector<string>.
c_str() returns a const char pointer to its internal buffer, whose contents you are not allowed to change (thats why its const) and obviously cannot assign to your char buffer. You need to allocate memory for each string, and copy it:
argv[index] = new char[c->args[index].length()];
strcpy(argv[index], c->args[index].c_str());
include cstring header for strcpy.
Maybe this can help:
char const **argv = new const char* [c->args.size() + 1];
I have code sample like below..
std::vector<char*> vNameList;//it will be defined globally..
int main(int argc, char* argv[])
{
CollectName();
for(int i = 0; i<(int)vNameList.size(); i++)
{
printf("\n %s" , vNameList[i]);//Here gabage values are getting printed on console
}
return 0;
}
void CollectName()
{
char *Name = new char[sizeof(NAME)+1];//NAME datatype is defined having size of 32 char
//processing for Name is performed , which includes assigning Name variable with value..
//now insert it into vector
vNameList.push_back(Name);
delete[] Name; //at this Name value inserted into vector become garbage
}
I believe if we initialize char * with new it must be deleted to avoid memory leaks. But this is leading to modifying values from a vector.
Please guide me so that I can correct my code which will give me correct values.
I have some restriction to use Char * only , so suggest way to achieve this using char*.
You can correct your code by deleting the pointers only after you're done using them (assuming you actually have code which uses the pointers. Your example code doesn't print anything at all, garbage or otherwise).
But it's usually better design to store character strings in a std::string and when you store std::strings in a std::vector, you no longer need to manage the memory manually.
It is because the name of array is treated as pointer in c++(or c, it is inherted).
Therefore when you do
vNameList.push_back(Name);
It inserts the char * into vector,i.e the pointer to the first char(ans hence the string) to the vector, but you delete the pointer afterwards , therefore you get junk values. However if you don't delete the pointer , it works just fine as the pointer still exists , but this way you will not free up memory. Therefore : DONOT USE THIS
here it is : LIVE EXAMPLE
To avoid this hassle : you should use
std::vector<std::string> vNameList;
instead.
This is "popular" question so I already checked the similar threads but still didnt resolve my issue.
How can I declare 2-D array to hold "strings" - I need array of array of chars AFAIK - and use it as argument to 5 functions one after another where I can pass it by reference and update the content dynamically so following function can compare it. (I might get 10 "strings" or even empty array, so I want to do it correctly with dynamic array coz array content is different from system to system).
"string" => C style string aka array of chars. MAXLEN < 32;
C solution would be more disirable but if vectors can work, why not.
One possible solution in C is as follows:
char **p_strings = calloc(num_strings, sizeof(*p_strings));
for (i = 0; i < num_strings; i++)
{
// Allocate storage for the i-th string (always leave room for '\0')
p_strings[i] = calloc(len_string[i]+1, sizeof(*p_strings[i]));
}
...
// Call a function
my_function(p_strings, num_strings);
You will need to remember to free all this data when you're done with it.
If you need to alter the length of a string, or change the number of strings, you will have to do some fairly painful reallocation. So if you're working in C++, you should probably just be using a std::vector<std::string>.
std::vector<std::string> strings;
strings.push_back("Foo");
strings.push_back("Bar");
...
my_function(strings);
You can even get const pointers to C-style strings for each element, using c_str().
Assuming C++; for this I see no problem with using a vector to string (the string serves as the second dimension):
void foo(vector<string> v) {
cout << v[0]; // Assuming the elements exist!
}
int main(int argc, char *argv[])
{
vector<string> vString; // Make the vector
vString.push_back("something"); // Add a string
foo(vString); // Print out 'something'
}
In your edit you also described that the only thing that will change would be the actual string, so instead of push_backing your strings when they are needed, you can init the vector with the length:
vector<string> vString(10); // Assuming a size of 10
and then use them normally:
vString[4] = "something";
and (in response to the comment), to resize at runtime:
vString.resize(15); // Make it bigger, generates new blank strings
I have found that the easiest way to build my program argument list is as a vector of strings. However, execv expects an array of chars for the second argument. What's the easiest way to get it to accept of vector of strings?
execv() accepts only an array of string pointers. There is no way to get it to accept anything else. It is a standard interface, callable from every hosted language, not just C++.
I have tested compiling this:
std::vector<string> vector;
const char *programname = "abc";
const char **argv = new const char* [vector.size()+2]; // extra room for program name and sentinel
argv [0] = programname; // by convention, argv[0] is program name
for (int j = 0; j < vector.size()+1; ++j) // copy args
argv [j+1] = vector[j] .c_str();
argv [vector.size()+1] = NULL; // end of arguments sentinel is NULL
execv (programname, (char **)argv);
The prototype for execv is:
int execv(const char *path, char *const argv[]);
That means the argument list is an array of pointers to null-terminated c strings.
You have vector<string>. Find out the size of that vector and make an array of pointers to char. Then loop through the vector and for each string in the vector set the corresponding element of the array to point to it.
I stumbled over the same problem a while ago.
I ended up building the argument list in a std::basic_string<char const*>. Then I called the c_str() method and did a const_cast<char* const*> on the result to obtain the list in a format that execv accepts.
For composed arguments, I newed strings (ordinary strings made of ordinary chars ;) ), took their c_str() and let them leak.
The const_cast is necessary to remove an additional const as the c_str() method of the given string type returns a char const* const* iirc. Typing this, I think I could have used std::basic_string<char*> but I guess I had a reason...
I am well aware that the const-casting and memory leaking looks a bit rude and is indeed bad practise, but since execv replaces the whole process it won't matter anyway.
Yes, it can be done pretty cleanly by taking advantage of the internal array that vectors use. Best to not use C++ strings in the vector, and const_cast string literals and string.c_str()'s to char*.
This will work, since the standard guarantees its elements are stored contiguously (see https://stackoverflow.com/a/2923290/383983)
#include <unistd.h>
#include <vector>
using std::vector;
int main() {
vector<const char*> command;
// do a push_back for the command, then each of the arguments
command.push_back("echo");
command.push_back("testing");
command.push_back("1");
command.push_back("2");
command.push_back("3");
// push NULL to the end of the vector (execvp expects NULL as last element)
command.push_back(NULL);
// pass the vector's internal array to execvp
execvp(command[0], const_cast<char* const*>(command.data()));
return 1;
}
Code adapted from: How to pass a vector to execvp
Do a const_cast to avoid the "deprecated conversion from string constant to 'char*'". String literals are implemented as const char* in C++. const_cast is the safest form of cast here, as it only removes the const and does not do any other funny business. execvp() will not edit the values anyway.
If you want to avoid all casts, you have to complicate this code by copying all the values to char* types not really worth it.
Although if the number of arguments you want to pass to execv/execl is known, it's easier to write this in C.
You can't change the how execv works (not easily anyway), but you could overload the function name with one that works the way you want it to:
int execv(const string& path, const vector<string>& argv) {
vector<const char*> av;
for (const string& a : argv) {
av.push_back(a.c_str());
av.push_back(0);
return execv(path.c_str(), &av[0]);
}
Of course, this may cause some confusion. You would be better off giving it a name other than execv().
NB: I just typed this in off the top of my head. It may not work. It may not even compile ;-)