I couldn't believe that I couldn't get this right! Someone please help!
This says word is being used without being initialised(Is that required?):
int main(int argc, char* argv[])
{
char* word;
sprintf(word,"%d",12);
std::cout << word;
return 0;
}
And if I were to do this, it gives DEBUG ASSERTION FAILED!:
int main(int argc, char* argv[])
{
char* word = NULL; // char* word = nullptr;
sprintf(word,"%d",12);
std::cout << word;
return 0;
}
I have included stdio.h header. It is not making sense how I am fumbling over this.
You need to initialize it with allocated memory.
char word[20];
or
char* word = new char[20];
Initializing it to NULL will make it crash as the function tries to write at this address. Not initializing it will be undefined behavior as well since the address will be garbage.
Both those code snippets are undefined behavior.
In the first, the pointer word is uninitialized, so its value is indeterminate (and will seem to be random), so when using to write data you don't know where it will be written.
The second will always write to address zero, which is also undefined behavior.
The solution to this is to remember that you are using C++, which have std::string:
std::string word;
word = "12";
Or if you have the number as an integer that you want to use, then look at std::ostringstream:
int value = 12;
// ...
std::ostringstream os;
os << value;
std::string word = os.str();
You are declaring word as a pointer: to what?
If you need a string, and you still want to use sprintf, you have to declare its length:
char word[20]; // twenty bytes including string terminator \0
This fixed-length syntax is prone to errors and nowadays obsolete: please refer to #Joachim Pileborg answer for a better use of strings in C++
Related
I have the following code:
void uppercase(char *sir)
{
for(int i=0;i<strlen(sir);i++)
{
sir[i]=(char)toupper(sir[i]);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
//char lower[]="u forgot the funny"; this works
//char *lower="u forgot the funny"; this gives me a runtime error
uppercase(lower);
cout<<lower<<"\n\n";
system("PAUSE");
return 0;
}
I have noted that if I run with the char vector it works.
When I try to run with the second method it generates a runtime error.
I would like to know the reason for this behaviour please.
You cannot modify string literals; doing so (as in your second case) is undefined behaviour.
char x[] = "foo";
creates a character array containing the characters f,o,o,\0. It's basically a mutable copy of the string.
char *x = "foo";
creates a string pointer pointing to the "foo" string literal. The literal may live in some read-only memory, in the program memory, or in a constant pool. Writing to it is undefined behaviour. Also, not that the type of a string literal is always const char[], so assigning it to a char * is violating const-correctness.
The former creates a character array which can be mutated, the latter is a pointer to fixed memory (which cannot be manipulated)
I've done a bit of basic reading and from what I've gathered .c_str() always has a null terminator.
I have a fairly simple C++ program:
int main(int argc, char** argv)
{
std::string from = "hello";
char to[20];
memcpy(to, from.c_str(), strlen(from.c_str())+1);
std::cout<< to << std::endl;
return 0;
}
Will that memcpy ensure that I copy over a null-terminated string to my variable to (provided that my string from is shorter in length)?
You should use std::string to copy strings. However, if you want to do it like that you should use strcpy instead of memcpy
int main(int argc, char** argv)
{
std::string from = "hello";
char to[20];
strcpy(to, from.c_str());
std::cout<< to << std::endl;
return 0;
}
memcpy doesn't know what is a string. you give it a range to copy. it depends on strlen here to provide the termination point of range
Yes, this will work as long as your string in from is shorter than 20. Oh, no, wait, we are also copying the NULL terminator so it must be shorter than 19. I guess you see how that can lead to some confusion.
Which is by the way exactly the reason why this code is dangerous: I am assuming this is demo code and your actual code will get that string from some sort of input. At that moment you will not be able to control how long it is. You can truncate it but you might forget to do that which means that your program will be copying content to memory that possibly does not belong to it. This might lead to a crash but at least to undefined behaviour as you are overwriting memory that might contain other important data.
Using the string class actually helps to avoid such problems by not having to specify the string length. I would suggest using this class and only do operations involving c_str() when absolutely necessary.
The answer to your question is "yes" (as long as you char array is big enough, you should put a verification step).
If you really want to do this, you can do:
int main(int argc, char** argv)
{
std::string from = "hello";
char* to = new char[from.length() + 1];
strcpy(to, from.c_str()); ///Or memcpy(to, from.c_str(), from.length() + 1 );
std::cout<< to << std::endl;
delete[] to;
return 0;
}
Which is safer as long as you are not writing another, longer string into "to"
This code crashes the program
#include <cstdio>
int main()
{
char *name1;
char *name2 = "Mark";
gets(name1);
puts(name1);
return 0;
}
whereas this doesn't
#include <cstdio>
int main()
{
char *name1 = "Mark";
char *name2;
gets(name2);
puts(name2);
return 0;
}
Why ?
I am using MinGW with Code::Blocks IDE.
You are just lucky that one crashes and other doesn't.
Both of the programs produce undefined behavior.
char *name2;
gets(name2);
You need to point the pointer to a valid and big enough memory to be able to write to it. You are just writing to a uninitialized pointer. This results in Undefined behavior. Undefined behavior does not mandate a crash, it literally means any behavior is possible, as in your case it might crash sometimes and may not but nevertheless it is a incorrect program.
Ideal Solution is to simply use std::string.
If you insist on using char * you need to point this pointer to a valid memory. For e.g.
char myArr[256];
char *name2 = &myArr;
Both are Undefined Behavior, will it crash or not, is rather matter of luck.
You have to provide the memory for your input, but you don't. If you want to stick at gets and puts you should change char *name to char name[100] or allocate memory:
char *name = new char[100];
...
delete name;
If you need more than 100 chars (including the \0 char at the end of the string) you have to increase the size accordingly.
In C++ using std::string is most likely the better alternative.
I want to copy a char to an address where a given char* points to.
it's in a function which is called by main:
char data = " ";
myfunction(data, somethingelse);
...
inside the function i have something like
void myfunction(char* data, short somethingelse) {
...
char byte = 0;
inputfilestream.read(&byte, 1);
*data = byte; // here i get the segfault
data++;
...
}
the segfault also comes when i to the copy using strncpy:
strncpy(data, byte, 1);
why is there a segfault? data isn't const and the address where i actually write to is exactly the same as the one where i allocated the data-array. i've tested that multiple times.
thanks in advance.
String literals are readonly. If you want a modifyable string, you must use an array, e.g.:
char data[10];
Or:
char *data = new char[10];
To elaborate a bit more: the type of a string literal is actually const char*. Assigning a string literal to a non-const char* is therefore technically invalid, but most compilers allow it anyway for legacy reasons. Many modern compilers will at least issue a warning when you try to do that.
data is assigned a string literal. String literals are ready only, and writing to them will cause segfaults.
Try this:
char data[10]; // or whatever size you want.
instead.
why is there a segfault? data isn't const and the address where i actually write to is exactly the same as the one where i allocated the data-array.
You didn't allocate anything. char *data = " "; shouldn't even compile in C++. You are assigning a constant string to a non-constant.
char byte = 0;
inputfilestream.read(&byte, 1);
*data = byte; // here i get the segfault
data++; // << How many times?
No problem
#include <stdio.h>
int main(int argc, char **argv)
{
char *data = "Yello"; // or char data[] = "Yello";
*data = 'H';
puts(data); // Hello
}
Test program which causes a EXC_BAD_ACCESS signal.
Why does this cause a bus error? I want to change the 'HI' to 'fI'.
//BUS ERROR TEST
#include <iostream>
void test(char *text)
{
text[0] = 'f';
}
int main()
{
char *text = (char *)"HI";
test(text);
std::cout << text << std::endl;
return 0;
}
You are not allowed to change string constants, that's undefined behaviour as per the standard.
If you replace:
char *text = (char *)"HI";
with something like:
char text[3];
strcpy (text, "HI");
or:
char text[] = "HI";
you'll find that it will work, because text in that case is modifiable memory.
You must not cast away const, it's UB. The string constants are read-only, so the compiler is allowed to put them into read-only memory.
Use
char text[] = "Hi!";
to get a modifiable string.
char *text = (char *)"HI";
text[0] = 'f';
This is actually against the C++ standard. Quoted strings are declared const for a reason. In your case, it probably stores the string as part of your "code data" rather than regular "data". This, combined with the common usage of making the "code data" area read only makes it so that you won't be able to write to quoted constant strings.