int main()
{
char name[]="avinash";
const char* nameano="a";
strtok(name,"n");
cout<<"the size of name is"<< sizeof(name);
cout<< name;
}
strtok takes in arguments (char*, const char*); name is an array, and hence a pointer to its first element. But if we make a declaration like
string name="avinash";
and pass name as first argument to strtok, then the program doesn't work, but it should, because name, a string, is a pointer to its first character.
Also, if we write
const string n = "n";
and pass it as second argument it doesn't work; this was my first problem.
Now also the sizeof(name) output is 8, but it should be 4, as avinash has been tokenized. Why does this happen?
You are confusing several things.
strtok takes in arguments (char*, const char*)....name is an array and hence a pointer to its first element...
name is an array, and it's not a pointer to its first element. An array decays in a pointer to its first argument in several contexts, but in principle it's a completely different thing. You notice this e.g. when you apply the sizeof operator on a pointer and on an array: on an array you get the array size (i.e. the cumulative size of its elements), on a pointer you get the size of a pointer (which is fixed).
but if we made a declaration like string name="avinash" and passed name as argument
then the prog doesnt work but it should because name of string is a pointer to its first character...
If you make a declaration like
string name="avinash";
you're are saying a completely different thing; string here is not a C-string (i.e. a char[]), but the C++ std::string type, which is a class that manages a dynamic string; those two things are completely different.
If you want to obtain a constant C-string (const char *) from a std::string you have to use it's c_str() method. Still, you can't use the pointer obtained in this way with strtok, since c_str() returns a pointer to a const C-string, i.e. it cannot be modified. Notice that strtok is not intended to work with C++ strings, since it's part of the legacy C library.
also if we write const string n = "n"; and pass it as second argument it doesnt work...this was my first problem...
This doesn't work for the exact same motivation, but in this case you can simply use the c_str() method, since the second argument of strtok is a const char *.
now also the sizeof(name) output is 8 but it should be 4 as avinash has been tokenised..
sizeof returns the "static" size of its operand (i.e. how much memory is allocated for it), it knows nothing about the content of name. To get the length of a C-string you have to use the strlen function; for C++ std::string just use its size() method.
I think you have to pass your name[] array to strtok like this:
strtok(&name[0],"n");
First of all, strtok is C and not C++ and is not made to work with C++ strings. Plus, it cant work if you dont use the return result of the function.
char name[]="avinash";
char * tok_name = strtok(name,"n");
std::cout<<"the size of name is"<< sizeof(tok_name);
std::cout<< tok_name;
You should consider to use std::string::find, std::string::substr and other stuff from the STL library instead of C functions.
As you said, strtok takes arguments of type char * and const char *. A string is not a char * so passing it as the first argument will not compile. For the second argument, you can convert a string to a const char * using the c_str() member function.
What problem are you trying to solve? If you're just trying to learn how strtok works, it would be much better to stick to raw character arrays. That function is inherited from C and thus wasn't designed with C++ strings in mind.
I don't believe the actual size of the name array is ever changed when you call strtok. The call remembers the last location it was called if you pass it a null pointer, and it continues to tokenize the string. The return value of the strtok call is the token that has been found in the string provided. Your code is calling sizeof(name) which is never actually adjusted by the strtok function.
int main()
{
char name[] = "avinash";
const char* nameano = "a";
char* token;
token = strtok(name, "n");
cout << "the length of the TOKEN is" << strlen(token) << endl;
cout << token << endl;
cout << "the length of the string is" << strlen(name) << endl;
cout << name << endl;
}
Try this maybe? I am not in front of a compiler so its likely I made a mistake, but it should get you on the right track to solve this problem.
This might also help:
http://msdn.microsoft.com/en-us/library/2c8d19sb(v=vs.71).aspx
Related
I'm working on an exercise to calculate the length of a string using pointers.
Here's the code I've written below:
int main() {
std::string text = "Hello World";
std::string *string_ptr = &text;
int size = 0;
//Error below: ISO C++ forbids comparison between pointer and integer [-fpermissive]
while (string_ptr != '\0') {
size++;
string_ptr++;
}
std::cout << size;
}
In a lot of examples that I've seen, the string is often a char array which I also understand is a string. However, I want to try calculate it as a string object but I'm getting the error below.
Is it possible to calculate it where the string is an object, or does it need to be a char array?
If you just want the size of the string, well, use std::string::size():
auto size = text.size();
Alternatively, you can use length(), which does the same thing.
But I'm guessing you're trying to reimplement strlen for learning purposes. In that case, there are three problems with your code.
First, you're trying to count the number of characters in the string, and that means you need a pointer to char, not a pointer to std::string. That pointer should also point to constant characters, because you're not trying to modify those characters.
Second, to get a pointer to the string's characters, use its method c_str(). Getting the address of the string just gets you a pointer to the string itself, not its contents. Most importantly, the characters pointed to by c_str() are null terminated, so it is safe to use for your purposes here. Alternatively, use data(), which has been behaving identically to c_str() since C++11.
Finally, counting those characters involves checking if the value pointed to by the pointer is '\0', so you'll need to dereference it in your loop.
Putting all of this together:
const char* string_ptr = text.c_str(); // get the characters
int size = 0;
while (*string_ptr != '\0') { // make sure you dereference the pointer
size++;
string_ptr++;
}
Of course, this assumes the string does not contain what are known as "embedded nulls", which is when there are '\0' characters before the end. std::string can contain such characters and will work correctly. In that case, your function will return a different value from what the string's size() method would, but there's no way around it.
For that reason, you should really just call size().
First things first, the problem is irrelevant. std::string::size() is a O(1) (constant time) operation, as std::string's typically store their size. Even if you need to know the length of a C-style string (aka char*), you can use strlen. (I get that this is an exercise, but I still wanted to warn you.)
Anyway, here you go:
size_t cstrSize(const char* cstr)
{
size_t size(0);
while (*cstr != '\0')
{
++size;
++cstr;
}
return size;
}
You can get the underlying C-style string (which is a pointer to the first character) of a std::string by calling std::string::c_str(). What you did was getting a pointer to the std::string object itself, and dereferencing it would just give you that object back. And yes, you need to dereference it (using the * unary operator). That is why you got an error (which was on the (string_ptr != '\0') btw).
You are totally confused here.
“text” is a std::string, that is an object with a size() method retuning the length of the string.
“string_ptr” is a pointer to a std::string, that is a pointer to an object. Since it is a pointer to an object, you don’t use text.size() to get the length, but string_ptr->size().
So first, no, you can’t compare a pointer with an integer constant, only with NULL or another pointer.
The first time you increase string_ptr it points to the memory after the variable text. At that point using *string_ptr for anything will crash.
Remember: std::string is an object.
When I tried to compile the below code it compiles fine.
char str[] = "I am Lokesh Kumar. But I liked to be called The Loki";
char *token;
char *p = str;
while (token = strtok_r(p, " ", &p)) {
cout << token << endl;
}
But a error
"[Error] cannot convert 'char ()[53]' to 'char**' for argument '3' to 'char* strtok_r(char*, const char*, char**)*"
popped out for below code
char str[] = "I am Lokesh Kumar. But I liked to be called The Loki";
char *token;
char *p = str;
while (token = strtok_r(str, " ", &str)) {
cout << token << endl;
}
str and p both holds the address of first character element, then why this error
strtok_r takes the address of a char pointer as its third argument. It will update this pointer to point past the matched token (or some other internal use). str is not a pointer, it is an array of char, you cannot pass its address to strtok_r because the address of an array is not the same thing as the address of a pointer.
The confusion come from the automatic conversion of array objects to pointers to their first elements that occurs when an array is used in most expression contexts, such as p = str.
Arrays and pointers are very different things, just like families and individual names. A family (array) is a collection of people (characters), a full name (pointer) points to an individual. A pointer to pointer to character is similar to a piece of paper on which you can write the name of a individual person (character in the sense of a person ;).
Not also these points:
p does not need to be initialized before passing its address to strtok_r with a non NULL first argument;
p should not be passed to strtok_r as the first argument in subsequent calls,
It is considered poor coding style to use an assignment expression as a test expression in a conditional statement, you should parenthesize the assignment and compare the value to NULL explicity.
Here is a corrected version:
char str[] = "I am Lokesh Kumar. But I liked to be called The Loki";
char *token;
char *arg = str;
char *p;
while ((token = strtok_r(arg, " ", &p)) != NULL) {
printf("%s\n", token); // using printf since you tagged the question as C
arg = NULL;
}
str is an array, p is a pointer. It's not true that
str and p both holds the address of first character element
What is true is that str is convertable to the address of the first character. But you are doing this &str so you get the address of the array, not the address of the first character.
Since strtok_r requires a modifiable pointer to a character there's no way to get that other than to declare a pointer variable. Unless you decide to pass nullptr to the third parameter of course.
p has type char* so &p has type char**, as required.
str has type char ()[53], so &str has type char (*)[53].
The array decays to a pointer to the first element in many contexts, but it's not the same thing.
Anyway, you know the 3rd parameter to strtok_r can be NULL to start? It's only output for the first call.
This is a generic example usage of strtok_r. Though you already got detailed information about char array and pointer difference from #john answer.
Just for the simplicity sake, I am adding this example. I don't know why you want the pointer to point str.
You can delimit the string into token in below simple way.
There are two ways to solve the issue.
char str[] = "I am Lokesh Kumar. But I liked to be called The Loki";
cahr *p;
char *token = strtok_r(str, " ", &p);
I am trying to make a function like strcpy in C++. I cannot use built-in string.h functions because of restriction by our instructor. I have made the following function:
int strlen (char* string)
{
int len = 0;
while (string [len] != (char)0) len ++;
return len;
}
char* strcpy (char* *string1, char* string2)
{
for (int i = 0; i<strlen (string2); i++) *string1[i] = string2[i];
return *string1;
}
main()
{
char* i = "Farid";
strcpy (&i, "ABC ");
cout<<i;
}
But I am unable to set *string1 [i] value. When I try to do so an error appears on screen 'Program has encountered a problem and need to close'.
What should I do to resolve this problem?
Your strcpy function is wrong. When you write *string1[i] you are actually modifying the first character of the i-th element of an imaginary array of strings. That memory location does not exist and your program segfaults.
Do this instead:
char* strcpy (char* string1, char* string2)
{
for (int i = 0; i<strlen (string2); i++) string1[i] = string2[i];
return string1;
}
If you pass a char* the characters are already modifiable. Note It is responsibility of the caller to allocate the memory to hold the copy. And the declaration:
char* i = "Farid";
is not a valid allocation, because the i pointer will likely point to read-only memory. Do instead:
char i[100] = "Farid";
Now i holds 100 chars of local memory, plenty of room for your copy:
strcpy(i, "ABC ");
If you wanted this function to allocate memory, then you should create another one, say strdup():
char* strdup (char* string)
{
size_t len = strlen(string);
char *n = malloc(len);
if (!n)
return 0;
strcpy(n, string);
return n;
}
Now, with this function the caller has the responsibility to free the memory:
char *i = strdup("ABC ");
//use i
free(i);
Because this error in the declaration of strcpy: "char* *string1"
I don't think you meant string1 to be a pointer to a pointer to char.
Removing one of the * should word
The code has several issues:
You can't assign a string literal to char* because the string literal has type char const[N] (for a suitable value of N) which converts to char const* but not to char*. In C++03 it was possible to convert to char* for backward compatibility but this rule is now gone. That is, your i needs to be declared char const*. As implemented above, your code tries to write read-only memory which will have undesirable effects.
The declaration of std::strcpy() takes a char* and a char const*: for the first pointer you need to provide sufficient space to hold a string of the second argument. Since this is error-prone it is a bad idea to use strcpy() in the first place! Instead, you want to replicate std::strncpy() which takes as third argument the length of the first buffer (actually, I'm never sure if std::strncpy() guarantees zero termination or not; you definitely also want to guarantee zero termination).
It is a bad idea to use strlen() in the loop condition as the function needs to be evaluated for each iteration of the loop, effectively changing the complexity of strlen() from linear (O(N)) to quadratic (O(N2)). Quadratic complexity is very bad. Copying a string of 1000 characters takes 1000000 operations. If you want to try out the effect, copy a string with 1000000 characters using a linear and a quadratic algorithm.
Your strcpy() doesn't add a null-terminator.
In C++ (and in C since ~1990) the implicit int rule doesn't apply. That is, you really need to write int in front of main().
OK, a couple of things:
you are missing the return type for the main function
declaration. Not really allowed under the standard. Some compilers will still allow it, but others will fail on the compile.
the way you have your for loop structured in
strcpy you are calling your strlen function each time through
the loop, and it is having to re-count the characters in the source
string. Not a big deal with a string like "ABC " but as strings get
longer.... Better to save the value of the result into a variable and use that in the for loop
Because of the way that you are declaring i in
`main' you are pointing to read-only storage, and will be causing an
access violation
Look at the other answers here for how to rebuild your code.
Pointer use in C and C++ is a perennial issue. I'd like to suggest the following tutorial from Paul DiLorenzo, "Learning C++ Pointers for REAL dummies.".
(This is not to imply that you are a "dummy," it's just a reference to the ",insert subject here> for Dummies" lines of books. I would not be surprised that the insertion of "REAL" is to forestall lawsuits over trademarked titles)
It is an excellent tutorial.
Hope it helps.
I'm using C++ map to implemented a dictionary in my program. My function gets a structure as an argument and should return the associated value based on structure.name member which is char named[32]. The following code demonstrates my problem:
map <const char *, const char *> myMap;
myMap.insert(pair<const char *, const char *>("test", "myTest"));
char *p = "test";
char buf[5] = {'\0'};
strcpy(buf, "test");
cout << myMap.find(p)->second << endl; // WORKS
cout << myMap.find("test")->second << endl; // WORKS
cout << myMap.find(buf)->second << endl; // DOES NOT WORK
I am not sure why the third case doesn't work and what should I do to make it work.
I debugged the above code to watch the values passed and I still cannot figure the problem.
Thanks!
Pointer comparison, not string comparison, will be performed by map to locate elements. The first two work because "test" is a string literal and will have the same address. The last does not work because buf will not have the same address as "test".
To fix, either use a std::string or define a comparator for char*.
The map key is a pointer, not a value. All your literal "test" strings share storage, because the compiler is clever that way, so their pointers are the same, but buf is a different memory address.
You need to use a map key that has value equality semantics, such as std::string, instead of char*.
Like was mentioned you are comparing on the address not the value. I wanted to link this article:
Is a string literal in c++ created in static memory?
Since all the literals had the same address this explains why your comparison of string literals worked even though the underlying type is still a const char * (but depending on the compiler it may not ALWAYS be so)
Its because by buf[5] you are allocating the memory pointed by buf but when u use 'p' pointer it points to the same memory location as used by map. So always use std::string in key instead of pointer variable.
char el[3] = myvector[1].c_str();
myvector[i] is a string with three letters in. Why does this error?
It returns type char* which is a pointer to a string. You can't assign this directly to an array like that, as that array already has memory assigned to it. Try:
const char* el = myvector[1].c_str();
But very careful if the string itself is destroyed or changed as the pointer will no longer be valid.
Because a const char * is not a valid initializer for an array. What's more, I believe c_str returns a pointer to internal memory so it's not safe to store.
You probably want to copy the value in some way (memcpy or std::copy or something else).
In addition to what others have said, keep in mind that a string with a length of three characters requires four bytes when converted to a c_str. This is because an extra byte has to be reserved for the null at the end of the string.
Arrays in C++ must know their size, and be provided with initialisers, at compile-time. The value returned by c_str() is only known at run-time. If e1 were a std::string, as it probably should be, there would be no problem. If it must be a char[], then use strcpy to populate it.
char el[3];
strcpy( e1, myvector[1].c_str() );
This assumes that the string myvector[1] contains at most two characters.
Just create a copy of the string. Then, if you ever need to access it as a char*, just do so.
string el = myvector[1];
cout << &el[0] << endl;
Make the string const if you don't need to modify it. Use c_str() on 'el' instead if you want.
Or, just access it right from the vector with:
cout << &myvector[1][0] << endl;
if possible for your situation.