This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why do I get a segmentation fault when writing to a string?
I have the following program:
#include <iostream>
using namespace std;
void reverseString(char* first, char* last)
{
while(first < last)
{
cout << *first << " " << *last << endl; //for debugging; prints 'H' and 'o' then crashes
char temp = *last;
*last = *first; //this line crashes the program
*first = temp;
first++;
last--;
}
}
int main()
{
char* s = "Hello";
reverseString(s, s + strlen(s) - 1);
cout << s << endl;
}
However, I'm having trouble swapping the values to which the pointers point. I thought *p = *p1 should just set the pointed-to value of p to the pointed-to value of p1, but something seems bugged up. Thanks in advance for any help!
The code looks fine to me. The most likely problem is that the compiler is allowed to assume that string literals are not modified, so it can put them in read-only memory. Try
char s[] = "Hello";
in main() instead, which creates a writable copy of the string literal.
An alternative solution to #j_random_hacker:
char* buffer = new char[32];
strcpy(buffer, "Hello");
reverseString(buffer, buffer + strlen(buffer) - 1);
... rest of your program ...
delete[] buffer;
This properly allocates memory for a C-style string which can then be modified by any function. Of course, you need to include <string.h> header to access strcpy and strlen.
Header file for strlen() is missing.
Second, it throws a warning - Deprecated conversion from string constant to char*, #j_random_hacker's solution seems to take care of this issue.
Related
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 3 years ago.
Improve this question
The goal is to create substrings from an inputted string by extracting words which are separated by spaces.
The substrings have to be variables themselves.
Sounds easy, but what makes it hard is that you can only use strcpy, strcmp, strlen, strcat, strncpy, strncmp, strnlen, and strncat.
Example:
input:
"John 40 Hitman"
driver:
...
cout << word1 << endl
<< word2 << endl
<< word3 << endl;
output:
John
40
Hitman
Here is my code
#include <iostream>
#include <cstring>
#include <stdio.h>
int main(){
const char *string = "Name Age Job";
char name[10];
char age[10];
char job[10];
int length = strlen(string);
int temp = 0;
bool flag = false;
for(int i = 0; i < length + 1; i++){
if(isspace(string[i]) && !flag){
strncpy(name, string, i);
name[i] = '\0';
temp = i;
flag = !flag;
cout << name << endl;
continue;
}
if(isspace(string[i])){
strncpy(age, string + temp + 1, length - i - 1);
age[temp - i] = '\0';
temp = i;
cout << age << endl;
continue;
}
if(string[i] == '\0'){
strncpy(job, string + temp + 1, length);
job[temp - i] = '\0';
cout << job << endl;
}
}
It works but it has to use a flag boolean, strings are not dynamic, only works for a string with 2 spaces, and there is a lot of repeated code. Overall really janky, but I spent roughly two hours on this and I do not know how to improve it.
If you are wondering, this is indeed a homework problem, but it's an intro class and my professor only wants the correct output for a hard coded string with only 3 words. I, however, want to learn how to improve upon it and would be really grateful for any help. Thanks.
All you need to do is replace space ' ' with '\0'(the string end) thus creating 3 substrings from the original. The following program does that and just dumps the string to cout but you could hold the pointers in an array as well (eg. char* substring[3]).
int main(){
char string[] = "Name Age Job";
char* temp = string;
for(char* it = string; *it; ++it ) {
if (*it == ' ') {
*it = '\0';
std::cout << temp << std::endl;
temp= it + 1;
}
}
std::cout << temp << std::endl;
}
The proper way to do this with C functions only would be to use strtok, although that one chops up a string in-place.
Regarding your code, there's lot of needless branching and checks. You should avoid continue, it is an almost 100% certain sign of a loop in need of improvements. Whenever you find yourself needing it, there's usually a better way.
You should also avoid strncpy because as you've noticed, it is a pain to keep track of when it null terminates and when it doesn't. It's a dangerous function and much slower than memcpy, which could be used instead here.
Here is a simplified version based on using 2 pointers, one all the time set to point at the next space and one all the time set to point at the beginning of the next valid word.
#include <string.h>
#include <ctype.h>
#include <stdio.h>
const char* find_next_space (const char* str)
{
while(*str != '\0' && !isspace(*str))
str++;
return str;
}
int main (void)
{
const char str[] = "hello world how are you";
char substr[5][10];
const char* word_start = str;
for(size_t i = 0; i<5; i++)
{
if(*word_start == '\0')
break;
const char* next_space = find_next_space(word_start);
size_t length = (size_t)(next_space-word_start);
memcpy(substr[i], word_start, length);
substr[i][length] = '\0';
puts(substr[i]);
word_start = next_space+1;
}
}
This code simplifies things by not checking if a string would fit and it doesn't allocate memory. Real production-quality C code wouldn't use char [5][10] but rather a pointer array char* [5] where each pointer is set to point at dynamically allocated memory.
I agree with #acraig5075 as your code is more C than C++.
If you are thinking of writing this in C++ using the STL string, one way of doing this is the following.
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
std::vector<std::string> split_string( const std::string &srcString, const char &delimiterKeywrd )
{
/**
* Splits a string according to a keyword delimiter and returns a vector with the
* segments of the split string.
*
* Args:
* #param srcString: The required string to split.
* #param delimiterKeywrd: The delimiter keyword that the string will be splitted to
* #return segmentsVectr: A vector holding the segments of the srcString that was splitted.
*
*/
std::stringstream inputStr;
inputStr.str( srcString );
std::string segment;
std::vector<std::string> segmentsVectr;
while( std::getline( inputStr, segment, delimiterKeywrd ) )
{
segmentsVectr.push_back( segment );
}
return segmentsVectr;
}
int main() {
std::string inputStr{"John 40 Hitman"};
std::vector<std::string> result;
char delimiterKeywrd = ' ';
result = split_string( inputStr, delimiterKeywrd );
// Iterate through the vector and print items on a different line.
for ( const std::string &item : result )
{
std::cout << item << std::endl;
}
return 0;
}
Check std::string at cppreference and examples if you are not familiar.
Also here I am using std::stringstream which makes it easy to use std::getline.
If std::stringstream is not your preferred way you can always check this implementation from another question about splitting a string by character.
Hope this helps.
No matter what value I set for the size of my array, the function I wrote returns a value some degree larger.
I've tried, while(*str++) and removing the str++ from inside the while loop, instead of what is there now.
I am using Visual Studio 2019.
int strlen(char* str)
{
int i = 0;
while (*str != '\0')
{
i++;
str++;
}
return i;
}
int main()
{
char line[1];
char* v = line;
char* s = new char[1];
cout << "for s " << strlen(s) << endl;
cout << "for v " << strlen(v) << endl;
}
You are neglecting to null-terminate your strings. Your function is iterating past the end of the array and causing Undefined Behavior. Some string-manipulation functions will put the null at the end, but if you want your string to have a terminal null, you must put it there yourself.
char line[2];
char* v = line;
line[0]='x';
line[1]= '\0';
The contents of your arrays are undefined. You are not filling any of the arrays with any characters, let alone with any null terminators. It is undefined behavior to call strlen() on a character array that is not properly null terminated.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why do I get a segmentation fault when writing to a string?
I want to write a simple C++ function that reverses a
string/char[] by only pointer arithmetic. I understand the concept
and have code already typed up.
I have the following .cpp file:
#include <iostream>
using std::cout;
using std::endl;
void reverse(char* target) //Requirements specify to have this argument
{
cout << "Before :" << target << endl; // Print out the word to be reversed
if(strlen(target) > 1) // Check incase no word or 1 letter word is placed
{
char* firstChar = &target[0]; // First Char of char array
char* lastChar = &target[strlen(target) - 1]; //Last Char of char array
char temp; // Temp char to swap
while(firstChar < lastChar) // File the first char position is below the last char position
{
temp = *firstChar; // Temp gets the firstChar
*firstChar = *lastChar; // firstChar now gets lastChar
*lastChar = temp; // lastChar now gets temp (firstChar)
firstChar++; // Move position of firstChar up one
lastChar--; // Move position of lastChar down one and repeat loop
}
}
cout << "After :" << target << endl; // Print out end result.
}
void main()
{
reverse("Test"); //Expect output to be 'tseT'
}
I've stepped through in the debugger several times but each time it
crashes around the temp = *firstChar line in the while loop. It
freezes up here and causes the program to stop running and unable to
finish. Is there something I am simply overlooking or is there
something deeper as to why I can't do it this way.
EDIT: There is an else condition, but I removed it for the sake of
brevity. It was after the if statement and it just prompted that the
word was 1 char or no word was put.
The problem is not in the reverse function, but in the calling code.
reverse("Test");
String literals are read-only, attempting to modify one leads to undefined behavior. Pay attention to compiler warnings (or turn the warning level up if you aren't getting any). The line above should be generating warnings about a deprecated conversion from const char * to char * being performed.
To fix the code:
int main() // <-- note the return type, int NOT void!
{
char str[] = "Test";
reverse( str );
}
This code will reverse it twice. Divide the loop by two.
my main concern is if i am doing this safely, efficiently, and for the most part doing it right.
i need a bit of help writing my implementation of a string class. perhaps someone could help me with what i would like to know?
i am attempting to write my own string class for extended functionality and for learning purposes. i will not use this as a substitute for std::string because that could be potentially dangerous. :-P
when i use std::cout to print out the contents of my string, i get some unexpected output, and i think i know why, but i am not really sure. i narrowed it down to my assign function because any other way i store characters in the string works quite fine. here is my assign function:
void String::assign(const String &s)
{
unsigned bytes = s.length() + 1;
// if there is enough unused space for this assignment
if (res_ >= bytes)
{
strncpy(data_, s.c_str(), s.length()); // use that space
res_ -= bytes;
}
else
{
// allocate enough space for this assignment
data_ = new char[bytes];
strcpy(data_, s.c_str()); // copy over
}
len_ = s.length(); // optimize the length
}
i have a constructor that reserves a fixed amount of bytes for the char ptr to allocate and hold. it is declared like so:
explicit String(unsigned /*rbytes*/);
the res_ variable simply records the passed in amount of bytes and stores it. this is the constructor's code within string.cpp:
String::String(unsigned rbytes)
{
data_ = new char[rbytes];
len_ = 0;
res_ = rbytes;
}
i thought using this method would be a bit more efficient rather than allocating new space for the string. so i can just use whatever spaced i reserved initially when i declared a new string. here is how i am testing to see if it works:
#include <iostream>
#include "./string.hpp"
int main(int argc, char **argv)
{
winks::String s2(winks::String::to_string("hello"));
winks::String s(10);
std::cout << s2.c_str() << "\n" << std::endl;
std::cout << s.unused() << std::endl;
std::cout << s.c_str() << std::endl;
std::cout << s.length() << std::endl;
s.assign(winks::String::to_string("hello")); // Assign s to "hello".
std::cout << s.unused() << std::endl;
std::cout << s.c_str() << std::endl;
std::cout << s.length() << std::endl;
std::cout.flush();
std::cin.ignore();
return 0;
}
if you are concerned about winks::String::to_string, i am simply converting a char ptr to my string object like so:
String String::to_string(const char *c_s)
{
String temp = c_s;
return temp;
}
however, the constructor i use in this method is private, so i am forcing to_string upon myself. i have had no problems with this so far. the reason why i made this is to avoid rewriting methods for different parameters ie: char * and String
the code for the private constructor:
String::String(const char *c_s)
{
unsigned t_len = strlen(c_s);
data_ = new char[t_len + 1];
len_ = t_len;
res_ = 0;
strcpy(data_, c_s);
}
all help is greatly appreciated. if i have no supplied an efficient amount of information please notify me with what you want to know and i will gladly edit my post.
edit: the reason why i am not posting the full string.hpp and string.cpp is because it is rather large and i am not sure if you guys would like that.
You have to make a decision whether you will always store your strings internally terminated with a 0. If you don't store your strings with a terminating zero byte, your c_str function has to add one. Otherwise, it's not returning a C-string.
Your assign function doesn't 0 terminate. So either it's broken, or you didn't intend to 0 terminate. If the former, fix it. If the latter, check your c_str function to make sure it puts a 0 on the end.
how can i print a char array such i initialize and then concatenate to another char array? Please see code below
int main () {
char dest[1020];
char source[7]="baby";
cout <<"source: " <<source <<endl;
cout <<"return value: "<<strcat(dest, source) <<endl;
cout << "pointer pass: "<<dest <<endl;
return 0;
}
this is the output
source: baby
return value: v����baby
pointer pass: v����baby
basically i would like to see the output print
source: baby
return value: baby
pointer pass: baby
You haven't initialized dest
char dest[1020] = ""; //should fix it
You were just lucky that it so happened that the 6th (random) value in dest was 0. If it was the 1000th character, your return value would be much longer. If it were greater than 1024 then you'd get undefined behavior.
Strings as char arrays must be delimited with 0. Otherwise there's no telling where they end. You could alternatively say that the string ends at its zeroth character by explicitly setting it to 0;
char dest[1020];
dest[0] = 0;
Or you could initialize your whole array with 0's
char dest[1024] = {};
And since your question is tagged C++ I cannot but note that in C++ we use std::strings which save you from a lot of headache. Operator + can be used to concatenate two std::strings
Don't use char[]. If you write:
std::string dest;
std::string source( "baby" )
// ...
dest += source;
, you'll have no problems. (In fact, your problem is due to the fact
that strcat requires a '\0' terminated string as its first argument,
and you're giving it random data. Which is undefined behavior.)
your dest array isn't initialized. so strcat tries to append source to the end of dest wich is determined by a trailing '\0' character, but it's undefined where an uninitialized array might end... (if it does at all...)
so you end up printing more or less random characters until accidentially a '\0' character occurs...
Try this
#include <iostream>
using namespace std;
int main()
{
char dest[1020];
memset (dest, 0, sizeof(dest));
char source[7] = "baby";
cout << "Source: " << source << endl;
cout << "return value: " << strcat_s(dest, source) << endl;
cout << "pointer pass: " << dest << endl;
getchar();
return 0;
}
Did using VS 2010 Express.
clear memory using memset as soon as you declare dest, it's more secure. Also if you are using VC++, use strcat_s() instead of strcat().