I have written a program that i want to input for example "11:12" to and then get "11" and "12" back. This is my code:
#include<stdio.h>
#include <string.h>
#include <conio.h>
int main(void)
{
char s[] = "";
char seperator[] = ":";
char *ch;
while (1)
{
scanf("%s", &s); //scanf("%s", &s);
if (s == -1) break;
char *result = NULL;
result = strtok(s, seperator);
while( result != NULL ) {
printf( "result is \"%s\"\n", result );
result = strtok( NULL, seperator );
}
fflush(stdout);
}
return 0;
}
but when i input "11:12", this is my output:
result is "11"
result is "12→ ("
How can i get rid of the last "→ ("?
You are not allocating any memory for your "s" array. Try it out like this:
char s[128] = "";
Plus, you are doing a double indirection on scanf with parameter &s, it is not necessary since "s" will be decayed to a pointer as a parameter to the function.
Try it like this:
scanf("%s", s);
You are defining s to be a string of length 0
char s[] = "";
So when you're reading into a string of length 0 with your scanf(), bad things will happen. Assign it some memory:
char s[100];
Also, you don't need the & in your scanf(). Just scanf("%s", s) will do.
And while we're at it, what is this trying to do?
if (s == -1) break;
Because that doesn't work... if you want to check if a "-1" was entered you need to use something like strcmp:
if (strcmp(s,"-1") == 0) break;
Your code has undefined behaviour.
char s[] = "";
With this line, you are declaring an array of chars with length 1 which means effectively you can store only null. So when you read input, you are overflowing s thus invoking UB.
You can also get rid of &s in the scanf as array name is same as its address.
Another issue is:
if (s == -1) break;
s is an array and hence it's address can't be -1. This comparison doesn't make sense.
Related
I am having issues when it comes to concatenating these two pointer strings together, below is my concatenating function, I am supposed to take string 1 and add it to string 2. Also I cannot use any functions in the string library, that's the point of this is to help us understand what code is actually in the functions by writing it ourself.
char strconcat(char *user2p, char *user1p) {
while (*user2p) {
user2p++;
}
while (*user1p) {
*user2p = *user1p;
*user2p++;
*user1p++;
}
*user2p = '\0';
printf("test: %c", *user2p);
return *user2p;
}
And here is the part of my main that is relevant to the function.
int main() {
char userString1[21], userString2[21];
char *user1p, *user2p;
user1p = userString1;
user2p = userString2;
printf("Please enter the first string: ");
gets(userString1);
printf("Please enter the second string: ");
gets(userString2);
printf("String 1 after concatenation: ");
puts(userString1);
printf("String 2 after concatenation: %c\n", strconcat(user2p, user1p));
The terminal keeps giving me this, I didn't include the code for the length and alphabetical order. It gives me a null when I try to run the test printf in the function and it gives me nothing when I return the function. I'm at a loss and any help is much appreciated!
Please enter the first string: jackhammer
Please enter the second string: jacky
The length of string 1 is: 10
The length of string 2 is: 5
String 1 comes before string 2 alphabetically.
String 1 after concatenation: jackhammer
(null)
String 2 after concatenation:
Your concat algorithm is fine, but you have to return a pointer to the original [leftmost] value, so your function needs to save it before looping:
char *
strconcat(char *user2p, char *user1p)
{
char *orig2p = user2p;
while (*user2p) {
user2p++;
}
while (*user1p) {
*user2p = *user1p;
user2p++;
user1p++;
}
*user2p = '\0';
printf("test: %s\n", orig2p);
return orig2p;
}
UPDATE:
To come up with a completely bulletproof test program for the concat function, we can use [overly] large input buffers and clip the input length to a maximum of 1/2 of the target buffer.
gets strips the newline but fgets does not. So, I've created an xgets function that is similar to gets but uses fgets and strchr to get [nearly] the same effect.
Although I believe it's okay to use standard string functions as part of the test code, I've created a hand coded version of strchr [hope that's not your next assignment :-)].
Anyway, here's the full program:
#include <stdio.h>
char *
strconcat(char *user2p, char *user1p)
{
char *orig2p = user2p;
while (*user2p) {
user2p++;
}
while (*user1p) {
*user2p = *user1p;
*user2p++;
*user1p++;
}
*user2p = '\0';
printf("test: %s\n", orig2p);
return orig2p;
}
char *
xstrchr(char *buf,int chrwant)
{
int chrcur;
char *res = NULL;
for (chrcur = *buf++; chrcur != 0; chrcur = *buf++) {
if (chrcur == chrwant) {
res = buf - 1;
break;
}
}
return res;
}
char *
xgets(char *buf,int maxlen)
{
char *cp;
char *res;
res = fgets(buf,maxlen,stdin);
if (res != NULL) {
cp = xstrchr(buf,'\n');
if (cp != NULL)
*cp = 0;
}
return res;
}
#define MAXLEN 800
int
main(void)
{
char userString1[MAXLEN], userString2[MAXLEN + 1];
char *user1p, *user2p;
printf("Please enter the first string: ");
user1p = xgets(userString1,MAXLEN / 2);
printf("Please enter the second string: ");
user2p = xgets(userString2,MAXLEN / 2);
if ((user2p != NULL) && (user1p != NULL))
printf("String 2 after concatenation: %s\n",strconcat(user2p, user1p));
return 0;
}
There's a number of issues. First is this.
while (*user1p) {
*user2p = *user1p;
*user2p++;
*user1p++;
}
This is working by accident. If you have compiler warnings on you should get a warning...
test.c:13:9: warning: expression result unused [-Wunused-value]
*user2p++;
^~~~~
test.c:14:9: warning: expression result unused [-Wunused-value]
*user1p++;
^~~~~~~
The reason it's unused is because C is interpreting it like so:
*(user1p++)
Increment the pointer, then dereference it. You just want to increment the pointers, no dereferencing required.
while (*user1p) {
*user2p = *user1p;
user2p++;
user1p++;
}
Then down here.
printf("String 2 after concatenation: %c\n", strconcat(user2p, user1p));
%c prints an individual char. You want %s which prints a char *. This reveals you have the wrong signature. strconcat should return a char * (ie. what C uses for strings) and return user2p (a char *).
char *strconcat(char *orig_to, const char *from) {
...
return user2p;
}
And since you're not changing from it should be const char * to let the compiler know and warn you if its accidentally changed.
Finally, when you return *user2p it's already been moved to the end of the string.
while (*user1p) {
*user2p = *user1p;
user2p++;
user1p++;
}
*user2p = '\0';
printf("test: %c", *user2p);
// This points to the null byte just set above
return user2p;
So printing the result of strconcat will print nothing. To get around this, store the original pointer for user2p and return that.
char *strconcat(char *orig_to, const char *from) {
char *orig_user2p = user2p;
...
return orig_user2p;
}
And some tips. It's easier to follow the code with good variable names that describe what they're doing.
char *strconcat(char *orig_to, const char *from) {
char *to = orig_to;
...
}
char foo[NN] already makes foo a pointer. There's no need to declare separate char * variables and copy the pointer.
char from[21], to[21];
Never use gets. There's no limit to how much memory it can use and it can easily overflow your buffer. Use fgets which can limit how much can be read to available memory.
printf("Please enter the string to concat from: ");
fgets(from, sizeof(from), stdin);
Though it's annoying that it keeps the newline and there's no simple function to strip it. You can use scanf which will strip whitespace, but beware its many pitfalls.
printf("Please enter the string to concat from: ");
scanf("%20s", from);
printf("Please enter the string to concat to: ");
scanf("%20s", to);
Finally, be sure the string you're concatenating to can hold its own contents and the new contents.
char from[21], to[41];
printf("Please enter the string to concat to: ");
// Be sure to leave enough room in `to` to fit `from`.
fgets(to, sizeof(to) - sizeof(from), stdin);
I would have created a more dynamic memory model. This code is more generic and concatenates strings creating a new string containing both strings.. Free when done :-)...
char *strconcat(char *string1, char *string2) {
int lenStr1=0,lenStr2=0;
char *tmpStr1=string1,*tmpStr2=string2,*returnStr;
while (*tmpStr1++)lenStr1++;
while (*tmpStr2++)lenStr2++;
if((returnStr=(char *)malloc(lenStr1+lenStr2+1))){
memcpy(returnStr,string1,lenStr1);
memcpy(&returnStr[lenStr1],string2,lenStr2);
returnStr[lenStr1+lenStr2]=0;
return returnStr;
} else {
return 0;
}
}
int main() {
char *string1="String 1 ",*string2="String 2 ",*result;
if((result=strconcat(string1, string2))) {
printf("-> %s \n",result);
free(result);
} else {
printf("Out of memory");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
I'd like to iterate through a string (entered by the user), returning the inputted string with an added space after each character (i.e. "Hello" --> "H e l l o ".
If I preset the value for str (i.e. char str[] = "Hello";) then the desired result is printed ("H e l l o "), but not so with user input (i.e. If the user inputs "Hello" the output is "H"). How does one successfully extract and manipulate a C string based on user input?
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "";
printf("\nEnter a string: ");
scanf("%s", &str);
printf("\nYou typed: %s \n", str);
int i = 0;
char newstr[150] = "";
for (i = 0; i < strlen(str); i++)
{
newstr[2*i] = str[i];
newstr[2*i+1] = ' ';
}
newstr[2 * strlen(str)] = '\0';
printf("\nExpanded String: ");
printf("%s", newstr);
return 0;
}
Here:
char str[] = "";
the size of str is inferred from the initializer, which is in this case one byte large. Thus str cannot hold a string larger than one byte, and since the zero-terminator is one byte large, there is no more space for a payload. A fix is to specify a size:
char str[1024] = "";
Now str has enough space for a kilobyte of data, or 1023 characters in addition to the terminator. The size is deliberately chosen to be much larger than the input you expect.
In addition to this, it would be a good idea to prevent scanf from writing past the end of the buffer by including the size in the format string. That is
scanf("%1023s", str); // now scanf will not read more than 1023 bytes plus sentinel.
...and in turn, it would be a good idea to increase the size of newstr accordingly (to twice that of str), i.e.
char newstr[2047]; // 2 * 1023 + terminator
...or, you know, make str smaller, depending on how long a string you want to support.
Thanks to Cool Guy for catching the superfluous & and newstr size implications.
"How does one successfully extract and manipulate a C string based on user input?"
You can use getchar() instead.
For example, you can store the user input in an array first. Then the problem becomes the same as if you did your 'char str[] = "Hello":
int index = 0
while((temp1 = getchar())!= '\n'){
str[index++] = temp1;
}
the following code
-complies cleanly
-checks and handles errors
-does the job
-doesn't use unneeded memory
(well actually) the logic could be a loop
that reads one char, outputs char, outputs space
or something similar if a trailing space is a problem
then the input buffer could be a single character
#include <stdio.h>
#include <stdlib.h> // exit, EXIT_FAILURE
#include <string.h>
int main()
{
// char str[] = "";
// there actually has to be room for the string
char str[100] = {'\0'};
printf("\nEnter a string: ");
if( 1 != scanf("%s", str) ) // arrays degenerate to pointer so no extra '&' needed
{ // then scanf failed
perror( "scanf failed" );
exit( EXIT_FAILURE );
}
// implied else, scanf successful
printf("\nYou typed: %s \n", str);
// there is no need to keep the modified string in memory
// when all that will be done is print it
int i = 0; // loop counter
printf("\nExpanded String: ");
for (i = 0; i < strlen(str); i++)
{
printf("%c", str[i]);
if( i < (strlen(str)-1) )
{ // then another char will follow
printf( " " );
}
else
{
printf( "\n" );
} // end if
} // end for
return 0;
} // end function: main
If i have a string that contains 10X15. And i want to separate the 10 and 15. Would the following code be correct. I am concerned about the second part of the code, is putting "NULL" there the right thing to do.
char * stringSixrows = strtok(stringSix[0], "X");
char * stringSixcollumns = strtok(NULL, "NULL");
//I put the second null there cause its the end of string, im not sure if its right though.
I'd say the "canonical" way to obtain the "pointer to the remaining string" is:
strtok(NULL, "")
strtok searches for any of the delimiters in the provided string, so if you don't provide any delimiters, it cannot find anything and thus only stops at the end of the input string.
example
#include <stdio.h>
#include <string.h>
int main(void){
char stringSix[] = "10X15";
char *stringSixrows = strtok(stringSix, "X");
char *stringSixcolumns = strtok(NULL, "X");
printf("%s, %s\n", stringSixrows, stringSixcolumns);
return 0;
}
another way
char stringSix[] = "10X15";
char stringSixrows[3];
char stringSixcolumns[3];
char *p;
if(NULL!=(p = strchr(stringSix, 'X')))
*p = '\0';
else {
printf("invalid format\n");
return -1;
}
strcpy(stringSixrows, stringSix);
strcpy(stringSixcolumns, p+1);
printf("%s, %s\n", stringSixrows, stringSixcolumns);
const char *stringSix = "10X15";
int stringSixrows;
int stringSixcolumns;
if(2==sscanf(stringSix, "%dX%d", &stringSixrows, &stringSixcolumns))
printf("%d, %d\n", stringSixrows, stringSixcolumns);
You can use strtol to convert the string to numbers as well as seek to the next string. Below code safely does the intended operation:
char stringSix[] = "10X15";
char * pEnd;
long firstNumber = strtol (stringSix,&pEnd, 10);
pEnd = strtok(pEnd, "");
long secondNumber = strtol (pEnd,&pEnd, 10);
Here's the code, which is supposed to execute the first command in history when "history 1" is entered:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char *argv[])
{
int i=0; int j=0; int k=0;
char inputString[100];
char *result=NULL;
char delims[] = " ";
char historyArray[100][100] = {0};
char *tokenArray[100][100] ;
do
{
j = 0;
printf("hshell>");
gets(inputString);
strcpy (historyArray[k], inputString);
k++;
// Break the string into parts
result = strtok(inputString, delims);
while (result!=NULL)
{
//result2 = result;
strcpy(tokenArray[j], result);
//puts(result);
j++;
result= strtok(NULL, delims);
//puts(tokenArray[j]);
}
//j = 0;
puts(tokenArray[0]);
puts(tokenArray[1]);
if (strcmp(tokenArray[0], "exit") == 0)
{
return 0;
}
else if (strcmp(tokenArray[0], "history") == 0)
{
if (j>1)
{
strcpy (result,historyArray[atoi(tokenArray[j-1])]);
}
else
{
//print history array
for (i=0; i<k;i++)
printf("%i. %s\n", i+1, historyArray[i]);
}
}
else
{
printf("Command not found\n");
}
}while (1);
}
However, it crashes. When in debugging, I noticed two things: - the array (tokenArray) address is out of bounds and - Access Violation (Segmentation Fault). You can see the errors in the images below.
What am I missing? What am I doing wrong?
The reason why you are dealing with segmentation fault is because you are trying to copy a string into the memory that has not yet been allocated. You have defined result as a char* and just assigned NULL to it, so trying to copy string into it is wrong:
char *result = NULL;
// ...
strcpy(result, historyArray[atoi(tokenArray[j-1])]);
You need to allocate some memory, that result will point to. Then strcpy can be used to copy string into this memory. You can either use malloc to allocate it dynamically or you can define result as an temporary variable with automatic storage duration (i.e. char result[100];).
Also note that
char *tokenArray[100][100];
defines a two-dimensional array of pointers to char. But what you actually need in this case is an array of strings, so you need to get rid of * just like #cnicutar has pointed out.
And one more note:
strcpy(result,historyArray[atoi(tokenArray[j-1])]);
is quite dangerous thing to do, because when atoi fails, you are trying to access the element out of array bounds, which produces undefined behavior, thus I recommend you doing something like this:
char tokenArray[100][100] = {0};
int index;
char indexString[100] = "8";
if (sscanf(indexString, "%d", &index) == 1) // integer successfully retrieved
{
strcpy(tokenArray[index], "some string");
printf("%s", tokenArray[8]);
}
You probably meant char tokenArray[100][100]; which creates 100 tokens with 100 characters each in 1 token.
writing char *tokenArray[100][100] literally means tokenArray is an array of 100 arrays, which contain 100 char *. But each of those char * points to a random addresses if it is not assigned a proper address.
You are getting a segmentation violation error because one of the char * contains an address which you cannot access.
Well, I declared a global array of chars like this char * strarr[];
in a method I am tokenising a line and try to put everything into that array like this
*line = strtok(s, " ");
while (line != NULL) {
*line = strtok(NULL, " ");
}
seems like this is not working.. How can I fix it?
Thanks
Any number of things could be going wrong with the code you haven't shown us, such as undefined behaviour by strtoking a string constatnt, or getting your parameters wrong when calling the function.
But the most likely problem from the code we can see is the use of *line instead of line, assuming that line is of type char *.
Use the following code as a baseline:
#include <stdio.h>
#include <string.h>
int main (void) {
char str[] = "My name is paxdiablo";
// Start tokenising words.
char *line = strtok (str, " ");
while (line != NULL) {
// Print current token and get next word.
printf ("[%s]\n", line);
line = strtok(NULL, " ");
}
return 0;
}
This outputs:
[My]
[name]
[is]
[paxdiablo]
and should be easily modifiable into something you can use.
Be aware that, if you're trying to save the character pointers returned from strtok (which would make sense for using *line), they are transitory and will not be what you expect after you're done. That's because modifications are made in-place within the source string. You can do it with something like:
#include <stdio.h>
#include <string.h>
int main (void) {
char *word[4]; // The array of words.
size_t i; // General counter.
size_t nextword = 0; // For preventing array overflow.
char str[] = "My name is paxdiablo";
// Start tokenising.
char *line = strtok (str, " ");
while (line != NULL) {
// If array not full, duplicate string to array and advance index.
if (nextword < sizeof(word) / sizeof(*word))
word[nextword++] = strdup (line);
// Get next word.
line = strtok(NULL, " ");
}
// Print out all stored words.
for (i = 0; i < nextword; i++)
printf ("[%s]\n", word[i]);
return 0;
}
Note the specific size of the word array in that code above. The use of char * strarr[] in your code, along with the message tentative array definition assumed to have one element is almost certainly where the problem lies.
If your implementation doesn't come with a strdup, you can get a reasonably-priced one here :-)