Error with manually copying string using pointers - c

I am creating this program as part of an assignment for college. The objective is to copy char* slogan = "Comp10120 is my favourite module"; to a new string while removing consonants and capitalising all letters. This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
void printStrings();
char *slogan = "Comp10120 is my favourite module";
char *p = slogan;
char *slogan_copy = NULL;
int main ()
{
//Get size of original string
int slogan_size = 0;
while (*p++ != '\0')
slogan_size++;
// Dynamically allocate memory to copy of string
slogan_copy = (char*) malloc ((slogan_size+1) * sizeof(char));
//Place string terminator at end of copy string
slogan_copy[slogan_size] = '\0';
//Reset p pointer to start of string
p = slogan;
int offset = 0;
while (*p != '\0')
{
//If the current element in the string is a consonant,
//or as defined in the if statement,
//if p is not a vowel and is between a and z or A and Z:
if ((!(*p == 'a' || *p == 'e' || *p == 'i' || *p == 'o' || *p == 'u'))
&& (((*p > 'a') && (*p < 'z')) || ((*p > 'A') && (*p < 'Z'))))
p++;
else
//Copy element to slogan_copy and capitalise
slogan_copy[offset++] = *p++;
slogan_copy[offset] = toupper(slogan_copy[offset]);
}
//Place string terminator after last element copied.
slogan_copy[offset] = '\0';
printStrings();
return 0;
}
void printStrings ()
{
printf("Origianl String: %s\n",*slogan);
printf("Modified String: %s",*slogan_copy);
}
When I try to execute, I get the error
initializer element is not constant
char *p = slogan;
^~~~~~
I am assuming that it is because I am trying to perform operations on slogan as if it was just a regular array of characters, and not a pointer to a string. However, I don't know how to fix this error.
In addition to this, I tried changing char*slogan = "Comp10120 is my favourite module"; to char slogan[] = "Comp10120 is my favourite module"; to see if it would work, out of curiosity. It complies, but crashes upon execution. Any ideas as to how I could modify my code for it to work?

you have quite a lot of mistakes in your program. do consider your use of global variables and consider using const where it is needed, However it is a preety good starting point so I have tested your program and it seems to work with 4 simple corrections:
1.remove p initialazation in the global env
8: //char *p = slogan;
9: char *p;
set p within main block
int main ()
{
p = slogan;
...
}
remove the astrix from the slogan in your printf statments it is already a pointer to a char array
printf("Origianl String: %s\n",slogan);
printf("Modified String: %s",slogan_copy);
hope this helps

A few improvements are needed.
1) In the function printf format %s expects pointer to the buffer. There is no need to dereference slogan or slogan_copy.
2)
slogan_copy[offset++] = *p++;
slogan_copy[offset] = toupper(slogan_copy[offset]);
The above will not work. It will make the next character upper not the current.
3) In C language there is no need to cast malloc.
4) Global variables should be avoided at all cost. The break encapsulation.
Pass variables as a parameters, you will get greater flexibility.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
void printStrings (const char *format, const char *s);
int main (void)
{
char *slogan = "Comp10120 is my favourite module";
char *p = slogan;
char *slogan_copy;
int offset = 0;
int slogan_size = 0;
//Get size of original string
while (*p++ != '\0')
slogan_size++;
// Dynamically allocate memory to copy of string
slogan_copy = malloc ((slogan_size+1) * sizeof(char));
//Place string terminator at end of copy string
slogan_copy[slogan_size] = '\0';
//Reset p pointer to start of string
p = slogan;
while (*p != '\0')
{
//If the current element in the string is a consonant,
//or as defined in the if statement,
//if p is not a vowel and is between a and z or A and Z:
if ((!(*p == 'a' || *p == 'e' || *p == 'i' || *p == 'o' || *p == 'u'))
&& (((*p > 'a') && (*p < 'z')) || ((*p > 'A') && (*p < 'Z'))))
p++;
else{
//Copy element to slogan_copy and capitalise
slogan_copy[offset] = *p;
slogan_copy[offset] = toupper(slogan_copy[offset]);
*p++;
offset++;
}
}
//Place string terminator after last element copied.
slogan_copy[offset] = '\0';
printStrings("Origianl String: %s\n", slogan);
printStrings("Modified String: %s\n", slogan_copy);
return 0;
}
void printStrings (const char *format, const char *s)
{
printf(format,s);
}
Output:
Origianl String: Comp10120 is my favourite module
Modified String: O10120 I AOUIE OUE

As per your request in a comment here is a simplified and correct version:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int main()
{
const char *slogan = "Comp10120 is my favourite module";
// Dynamically allocate memory to copy of string
char *slogan_copy = malloc((strlen(slogan) + 1) * sizeof(char));
//Reset p pointer to start of string
const char *p = slogan;
int offset = 0;
while (*p != '\0')
{
//If the current element in the string is a consonant,
//or as defined in the if statement,
//if p is not a vowel and is between a and z or A and Z:
char c = toupper(*p++);
if (!isalpha(c) || c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U' || )
slogan_copy[offset++] = c;
}
//Place string terminator after last element copied.
slogan_copy[offset] = '\0';
printf("Origianl String: %s\n", slogan);
printf("Modified String: %s\n", slogan_copy);
return 0;
}
Output:
Origianl String: Comp10120 is my favourite module
Modified String: O10120 I AOUIE OUE

Your code is not indented correctly: the else branch has 2 statements but they are not wrapped inside a block with { and }, so only the first statement in executed conditionally and the second is always executed, causing unexpected behavior regarding the uppercasing feature.
Furthermore, the uppercasing is not applied to the correct offset as offset is incremented too early, and uppercase vowels would be removed too.
A sane rule for coding style is to always use braces for all compound statements but the simplest ones. Rewrite the test this way:
int c = toupper((unsigned char)*p++);
if (!isalpha(c) || c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U') {
//Copy and capitalise element to slogan_copy
slogan_copy[offset++] = c;
}
There are other problems in the code, eg: passing incorrect data for the printf arguments, and using global variables for no good reason.
Here is an improved version:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main() {
const char *slogan = "Comp10120 is my favourite module";
char *slogan_copy = NULL;
//Get size of original string
int slogan_size = 0;
while (slogan[slogan_size] != '\0')
slogan_size++;
// Dynamically allocate memory to copy of string
slogan_copy = malloc(slogan_size + 1);
//Reset p pointer to start of string
const char *p = slogan;
int offset = 0;
while (*p != '\0') {
int c = toupper((unsigned char)*p++);
if (!isalpha(c) || c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U') {
//Copy and capitalise element to slogan_copy
slogan_copy[offset++] = c;
}
}
//Place string terminator after last element copied.
slogan_copy[offset] = '\0';
printf("Original string: %s\n", slogan);
printf("Modified string: %s\n", slogan_copy);
return 0;
}

Related

Reading Multiple lines until EOF

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//the function
char* scan(char *string)
{
int c; //as getchar() returns `int`
string = malloc(sizeof(char)); //allocating memory
string[0]='\0';
for(int i=0; i<100 && (c=getchar())!='\n' && c != EOF ; i++)
{
string = realloc(string, (i+2)*sizeof(char)); //reallocating memory
string[i] = (char) c; //type casting `int` to `char`
string[i+1] = '\0'; //inserting null character at the end
}
return string;
}
char** bigScan(char **string)
{
int c;
string=malloc(sizeof(char *));
string[0]='\0';
for(int i=0;(c=getchar()!=EOF);i++)
{
*string = realloc(string, (i+2)*sizeof(char *)); //reallocating memory
string[i] = scan(string[i]); //type casting `int` to `char`
string[i+1] = '\0'; //inserting null character at the end
}
return string;
}
int main(void)
{
char **buf; //pointer to hold base address of string
buf=bigScan(buf);
printf("%s\n",buf[0] );
}
So basically the scan function reads each line until either EOF or new line.The job of bigScan is to read multiple lines (pointer to strings) by invoking the scan function until we hit EOF. So essentially the big scan returns pointer to pointers and we can read the entire text using this.
What am I doing wrong in my approach ?
Basically invoking the scan function in my bigScan until I Hit EOF.
Ideal Input:
"Hi guys and girls
This is a message in multiple lines."
Ideal Output:
"Hi guys and girls
This is a message in multiple lines."
The (c=getchar()!=EOF) inside bigScan is invalid. It assigns the value of 1 or 0 to c, as the bool value is the result of != comparison.
The getchar() inside bigScan will make you loose one character per line, as that character is nowhere saved.
The allocation in bigScan is invalid. You shouldn't allocate the memory for string *string = realloc(string, but you should allocate the memory for pointers themselves, ie. string = realloc(string, ... sizeof(char*)).
NULL is the terminating value used for pointers. Don't use '\0' for pointers.
Use size_t to store sizes.
There is little point in passing parameters value if you are overwriting them. In this function the variable a is unused void f(int a) { a = 1; } as the variable string in your both functions are assigned immediately after entering the function.
The function scan has a hard limit of i<100 characters.
Below is somewhat fixed version of your functions. With also renamed variables. And removed parameters. And different indentation. And with assertions from the standard #include <assert.h> to use as a primitive error checking. And with ungetc so the character read in bigScan doesn't disappear. And I haven't run this code, so it has ton of errors.
char* scan(void)
{
char *string = malloc(sizeof(*string));
assert(string != NULL);
string[0] = '\0';
size_t stringlen = 1;
for(int c; (c=getchar()) != '\n' && c != EOF;) {
void * const ptr = realloc(string, (stringlen + 1) * sizeof(*string));
assert(ptr != NULL);
stringlen++;
string[stringlen - 2] = c;
string[stringlen - 1] = '\0'; //inserting null character at the end
}
return string;
}
char** bigScan(void)
{
char **strings = malloc(sizeof(*strings));
assert(strings != NULL);
strings[0] = NULL;
size_t stringslen = 1;
for(int c; (c = getchar()) != EOF;) {
ungetc(c);
void * const ptr = realloc(strings, (stringslen + 1) * sizeof(*strings));
assert(ptr != NULL);
strings = ptr;
stringslen++;
strings[stringslen - 2] = scan();
strings[stringslen - 1] = NULL;
}
return strings;
}

How to declare a dynamic array of char in c

I'm trying to declare an array of char dynamically, what I need to do is adding the newest character to the string which works fine, the problem is that when I try to print it out, at the beginning of the string there are some unknown characters.
char add[2];
char str2[200];
char c;
int temp = -1;
int num = 0;
char *str3;
str3 = malloc( (size_t)count ); //str3 = malloc(sizeof(char)) not working
while((c= getch()) !='\r')
{
for (int i = 0;i<200;i++)
{
if (str2[i] =='\0')
{
num = i;
break;
}
}
//printf("Num: %d\n",num);
if ((temp == -32) || (temp == 0))
{
}
else
{
if(isalnum((char)c) == 0)
{
if((c == '\'') || (c == -118) || (c == -115) || (c == -107) || (c == -123) || (c == -105)|| (c == 32))
{
realloc(str3,sizeof(char)+2);
printf("true: %c\n",c);
//realloc(str2,sizeof(char)+1);
add[1] = '\0';
add[0] = c;
strcat(str3,add);
strcat(str2,add);
printf("%s\n",str2);
printf("%s\n",str3);
}
else if (c == 8)
{
printf("Deleting something...\n");
}
}
else
{
realloc(str3,sizeof(char)+2);
printf("true: %c\n",c);
//realloc(str2,sizeof(char)+1);
add[1] = '\0';
add[0] = c;
strcat(str3,add);
strcat(str2,add);
printf("%s\n",str2);
printf("%s\n",str3);
}
}
printf("ASCII Code: %d\n",c);
temp = c;
}
To get some memory to your string, you have to tell malloc how many bytes of memory you want. sizeof(char) returns 1, therefore, you'll only have 1 byte. In C, strings are terminated by the NULL byte (\0), and printf and others will print until they find that NULL terminator.
If you do something like this:
char *str = malloc(1);
*str = 'a';
printf("%s", str);
You will probably get a very strange output, since you have no NULL terminator.
When you use the unsigned x; str = malloc(x);, it's actually undefined how many bytes you have, since that x variable is not initialized.
Since your question is very unclear, what I can tell you (from what I think you're asking) is how to actually get space for a string of 63 characters plus the NULL terminating byte.
char *str = malloc(64);
strcpy(str, "Stack Overflow");
printf("%s", str);
That will do it.
Also note that the memory block returned by malloc will not be zeroed, therefore you can't possibly know what's in it (that could be the reason you're getting garbage when you're printing).
I recommend you read about memory allocation in a good C book or in Wikipedia...
After your edit and "MCVE"
I made some edits to what I think it is you want. The modifications are explained in the comments of the source. Let me know if you have any doubts.
#include <stdio.h> /* printf */
#include <stdlib.h> /* malloc, free, realloc */
#include <string.h> /* strcat */
#include <ctype.h> /* isalnum */
#include <conio.h> /* getch */
int main(void)
{
char add[2];
char str2[200];
char c;
int temp = -1;
int num = 0;
char *str3;
/* I just think 'count' is an int, since you didn't put it in the code,
* I also deduced that #count will be used as the length of #str3
*/
int count;
/* Here, count is not initialized, so you MUST initialize it in order
* to call malloc with it! Since it seems you want to add character by
* character using realloc, then we just malloc() 2 bytes - 1 for a
* character and one for the NULL terminator.
*/
count = 2;
str3 = malloc(count);
/* You will be using #strcat to append strings to #str3, so you need
* to put a NULL terminator in it, because strcat will look for that
* NULL byte to find where it should append
*/
*str3 = 0x0;
while((c = getch()) != '\r') {
for (int i = 0;i < 200; i++) {
if (str2[i] =='\0') {
num = i;
break;
}
}
if ((temp == -32) || (temp == 0)) {
/* empty */
} else {
if(isalnum((char)c) == 0)
{
if((c == '\'') || (c == -118) || (c == -115) || (c == -107) || (c == -123) || (c == -105)|| (c == 32))
{
/* this is not the optimal way of using realloc, because
* you should first check for errors, but will do for
* this example.
* You must assign the returned value of realloc to str3.
*
* Also, since #count contains the length
* of #str3, you need to increment it.
*/
str3 = realloc(str3, ++count);
printf("true: %c\n",c);
add[1] = '\0';
add[0] = c;
strcat(str3,add);
strcat(str2,add);
printf("str2: %s\n",str2);
printf("str3: %s\n",str3);
} else if (c == 8) {
printf("Deleting something...\n");
}
} else {
/* see notes above on realloc */
str3 = realloc(str3, ++count);
printf("true: %c\n",c);
add[1] = '\0';
add[0] = c;
strcat(str3,add);
strcat(str2,add);
printf("str2: %s\n",str2);
printf("str3: %s\n",str3);
}
}
printf("ASCII Code: %d\n",c);
temp = c;
}
return 0;
}
In the first two cases, you are only allocating enough space for a single char. If you attempt to write more than one to that block of memory, you'll write past the end of the memory that was allocated for you. Doing so invokes undefined behavior, which in this case manifests as printing strange characters.
In the third case, you allocate x bytes of memory, however x is uninitialized and has an indeterminate value. Reading an indeterminate value is also undefined behavior. In this case it happens to work because the indeterminate value happens to be a valid value and is large enough to hold the string you want, however you can't depend on that behavior.
You need to allocate a byte for every character that you'll need, plus 1 for the terminating null byte that ends a string in C.
Note that the first allocation, this one
str = malloc(sizeof(char));
is exactly equivalent to1
str = malloc(1);
so you don't have room except for one character which is a problem, because it only represents an empty string.
If you allocate this much space you will very likely access memory out of the allocated space, causing undefined and unpredictable behavior. You need to understand what a string in c is,
A string in c is a sequence of non-null characters followed by a null character, so for a string with N characters you need N + 1 array elements (for ascii this equals bytes)
According to that definition of string if you wanted to store the string "Hello" you would need at least the following code
char *str = malloc(6);
if (str != NULL) {
str[0] = 'H';
str[1] = 'e';
str[2] = 'l';
str[3] = 'l';
str[4] = 'o';
str[5] = '\0'; // Or equivalently str[5] = 0;
}
as you can see, the last character being '\0' or 0 — which is the same — is very important.
All the functions in the standard library of c which expect a string parameter expect that there is the null terminator. For instance strlen() will count characters until it reaches the '\0', if it's not there then you can't predict where it is going to stop counting, this causing undefined behavior.
1sizeof(char) is as defined by the c standard always equal to one.

C program strings

For part of my program I would like to concatenate two strings together with an asterisk between each character. For example, if I had a first string of "abcde" and a second string of "1234567", I would like to have a result of "a*b*c*d*e*1*2*3*4*5*6*7*".
For this part of the program I have:
char *widen_stars(char *one, char *two)
{
int length_one = strlength(one); // length of first parameter
int length_two = strlength(two); // Length of second parameter
char *p = malloc((sizeof(char) * (length_one + length_two) * 2)+ 1), *p_start; //Allocate enough memory for both strings concatenated together with a * between each letter
p_start = p;
while(*one != '0')
{
if( (p - p_start) % 2 == 0) // Keeps track of where we are in the p string
{
*p = *one;
p++;
one++;
}
else
{
*p = '*';
p++;
}
}
while(*two != '0')
{
if( (p - p_start) % 2 == 0)
{
*p = *two;
p++;
two++;
}
else
{
*p = '*';
p++;
}
}
return p_start;
}
int main(int argc, char *argv[])
{
char first[31]= {0};
char second[31]= {0};
char *f = first, *s = second;
printf("Please enter a string of maximum 30 characters: ");
scanf("%s", f);
printf("Please enter a string of maximum 30 characters: ");
scanf("%s", s);
printf("The combined string is: %s\n", widen_stars(f, s));
}
return 0;
}
However, when I run the program with the above inputs, I get something like "a*b*c*d*e*", without any of the second string. If I block out the first while loop into comments to test the second loop, I get something like "1*2*3*4*5*5*7*", which leaves me scratching my head.
Your problem lies here:
while(*oneOrTwo != '0')
If you're looking for the end of the strings, it's '\0' that you should be looking for, not '0'. The former is the end-of-string marker, the latter is simply the character 0.
And, as an aside, there are much less ..., err, verbose ways to do this (assuming it's not class work - if it is, you should go with your current method). For example:
#include <stdio.h>
#include <string.h>
char *widen_stars(char *one, char *two) {
// Need to cater for memory exhaustion.
char *p = malloc((strlen(one) + strlen(two)) * 2) + 1);
if (p == NULL) return NULL;
// Init to empty string in case both inputs empty.
*p = '\0';
// Save string start for return.
char *p_start = p;
// Add character and asterisk for every character in both strings.
while (*one != '\0') {
sprintf(p, "%c*", *one++);
p += 2;
}
while (*two != '\0') {
sprintf(p, "%c*", *two++);
p += 2;
}
// Remove last asterisk (if needed).
// *(--p) = '\0';
// Return result.
return p_start;
}
That's based on your actual expected results, which place an asterisk after each character. However, your specifications call for an asterisk between each character. If you decide to got for the latter, it's a simple matter of un-commenting the penultimate statement in the function, to basically back up and replace the final asterisk with an end-of-string marker.
The problem in your code is in while conditions, you should increment pointers until '\0' not '0'. So instead of doing:
while(*one != '0')
...
while(*two != '0')
You do it like this:
while(*one != '\0')
...
while(*two != '\0')
And as you are returning a dynamically allocated memory, consider using a pointer to this memory, which you free after usage:
char *str = widen_stars(f, s);
printf("The combined string is: %s\n", str);
free(str);

Removing spaces from strings

I tried to write a function that gets a string and creates a new string but without multiple spaces (leaving only 1 space between words).
So far I wrote this, but for some reason it crashs and the debugger shows nothing.
I also don't know where do I need to put the free function...
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* upgradestring(char* oldtext);
int main()
{
char str1[] = "Chocolate Can Boost Your Workout" ;
printf("%s\n", str1);
printf("\n%s\n", upgradestring(str1));
return 0;
}
char* upgradestring(char* oldtext)
{
int i,j, count = 1;
char *newstr;
for (i = 0; oldtext[i] != '\0'; i++)
{
if (oldtext[i] != ' ')
count++;
else if (oldtext[i - 1] != ' ')
count++;
}
newstr = (char*)malloc(count * sizeof(char));
if (newstr == NULL)
exit(1);
for (i = 0, j = 0; (oldtext[i] != '\0')|| j<(count+1); i++)
{
if (oldtext[i] != ' ')
{
newstr[j] = oldtext[i];
j++;
}
else if (oldtext[i - 1] != ' ')
{
newstr[j] = oldtext[i];
j++;
}
}
return newstr;
}
You're addressing [i-1] and it's not within the range of the original array if i==0.
Here's how you could do it:
Simply copy one by one and if the char is ' ', keep skipping while it is ' ', otherwise advance by one.
static size_t newlen(char const *o)
{
size_t r=0;
while(*o){
r++;
if(*o==' ')
while(*o==' ')
o++;
else
o++;
}
return r;
}
char *upgradestring(char const *o)
{
char *r, *p;
size_t len = newlen(o);
if( 0==(r = malloc(len+1)))
return 0;
r[len]=0;
for(p=r;*o;){
*p++=*o;
if(*o==' ')
while(*o==' ')
o++;
else
o++;
}
return r;
}
int main()
{
char str1[] = "Chocolate Can Boost Your Workout" ;
char *new;
printf("%s\n", str1);
if(0 == (new = upgradestring(str1)))
return 1;
printf("%s\n", new);
free(new);
}
Failures to allocate are best signalled by return codes (you wouldn't want a library function to abort your program if it fails).
In order to be able to free the returned string, you first must capture it in a variable.
Good attempt, but let's focus on when you need to free your memory. You allocate dynamically the memory inside the function, then call the function inside a printf, which will allow the string to print, but how will you deallocate it? Use a pointer to assign the return value of your function, print it, and then free it!
Moreover, you need to allocate space for as many characters the new string has, plus one for the null terminator, since C strings require that to work smoothly with functions coming from headers, such as printf().
Furthermore, we do not cast what malloc() returns in C, read more here.
Also this:
else if (oldtext[i - 1] != ' ')
should be written as:
else if (i != 0 && oldtext[i - 1] != ' ')
to avoid accessing oldtext[-1] which is out of bounds, when i is 0.
Lastly, the condition you used when populating the new string, would be better with a logical AND, instead of an OR, since we have to stop as soon as either condition is false (we do not want to read past the null terminator of the original string, or past the size of the new string).
Putting everything together, we:
#include <stdio.h>
#include <stdlib.h>
char* upgradestring(char* oldtext)
{
int i, j, count = 0;
// compute 'count'
for(i = 0; oldtext[i]; i++)
{
if (oldtext[i] != ' ')
count++;
else if (i != 0 && oldtext[i - 1] != ' ')
count++;
}
char* newstr = malloc(count + 1); // PLUS ONE for the null terminator
if(!newstr) // check if malloc failed
{
printf("Malloc failed\n");
return 0;
}
// populate 'newstr'. We need to stop when either condition is false
for (i = 0, j = 0; (oldtext[i]) && j<(count+1); i++)
{
// Same as your code
}
// Assign the null terminator!
newstr[j] = '\0';
return newstr;
}
int main(void) {
char str1[] = "Chocolate Can Boost Your Workout" ;
// store the result of your function into 'newstr'
char* newstr = upgradestring(str1);
// print it
printf("%s\n", newstr);
// free it, since you no longer need it!
free(newstr);
return 0;
}
Output:
Chocolate Can Boost Your Workout
#include <stdio.h>
#include <stdlib.h>
char *upgradestring(char *oldtext)
{
size_t len,src,dst,spc;
char *result;
// First pass: count needed size
for (len=src=spc=0;oldtext[src]; src++){
if (oldtext[src] != ' ') spc=0; // non-space needs space
else if(spc++) continue; // skip non first space
len++;
}
result= malloc (1+len);
// Second pass: copy(K&R style)
for (dst=src=spc=0; (result[dst] = oldtext[src]) ; src++){
if (oldtext[src] != ' ') spc=0; // non-space: rest counter
else if(spc++) continue; // skip non-first space
dst++;
}
return result;
}
Simplified version: dont calculate the size in a first pass, but start with the same size as the original, and resize after the second pass. (strdup() can be replaced by strlen+malloc+memcpy)
char * strdup(char *);
char *upgradestring2(char *oldtext)
{
size_t src,dst,spc;
char *result;
result= strdup (oldtext);
// edit the copy, skipping all spaces except the first
for (dst=src=spc=0; result[src] ; src++){
if (result[src] != ' ') spc=0; // non-space:reset counter
else if(spc++) continue; // skip space,except the first
result[dst++] = result[src]; // Copy
}
result[dst] = 0;// terminate string;
// result=realloc(result, dst+1);
return result;
}
For starters neither declaration from the header <string.h> is used in your program. Thus this directive
#include <string.h>
may be removed from the program.
According to the C Standard the function main without parameters shall be declared like
int main( void )
The function with the strange name upgradestring:) does not change the argument. Hence it should be declared like
char* upgradestring( const char* oldtext);
^^^^^
Take into account that the source string can start with blanks. In this case statements like this
else if (oldtext[i - 1] != ' ')
count++;
result in undefined behavior because there is an attempt to access memory beyond the string when i is equal to 0.
The condition
(oldtext[i] != '\0')|| j<(count+1);
should be written at least like
(oldtext[i] != '\0') && j<(count+1);
^^^
though it is enough to check the index j because it can not be greater than the length of the source string.
You forgot to append the result string with the terminating zero '\0'.
Also it is not a good idea to exit the function with this statement
exit(1);
In this case you could just return a null pointer.
And the allocated memory should be freed before exiting the program.
As it has been mentioned before a source string can start with spaces and also have a redundant trailing space. I think it will be logically consistent to exclude them from the result string.
Usually the space character is considered in pair with the tab character. Moreover C has a special function isblank declared in the header <ctype.h> that checks whether a character is a space or a blank. (As far as I know the MS VS does not support this function)
Taking all this into account the function can be defined the following way as it is shown in the demonstrative program.
#include <stdio.h>
#include <stdlib.h>
char * trim_blanks( const char *s )
{
size_t n = 0;
const char *p = s;
// skip leading blanks
while ( *p == ' ' || *p == '\t' ) ++p;
_Bool last_blank = 0;
for ( ; *p; ++p )
{
++n;
if ( ( last_blank = ( *p == ' ' || *p == '\t' ) ) )
{
while ( p[1] == ' ' || p[1] == '\t' ) ++p;
}
}
if ( last_blank ) --n;
char *q = malloc( n + 1 );
if ( q )
{
p = s;
// skip leading blanks
while ( *p == ' ' || *p == '\t' ) ++p;
size_t i = 0;
for ( ; i < n; i++, ++p )
{
q[i] = *p == '\t' ? ' ' : *p;
if ( q[i] == ' ' )
{
while ( p[1] == ' ' || p[1] == '\t' ) ++p;
}
}
q[i] = '\0';
}
return q;
}
int main(void)
{
char s[] = "\t\tChocolate \t Can \t Boost Your Workout ";
printf( "\"%s\"\n", s );
char *t = trim_blanks( s );
printf( "\"%s\"\n", t );
free( t );
return 0;
}
The program output is
" Chocolate Can Boost Your Workout "
"Chocolate Can Boost Your Workout"

Reversing words in a sentence using pointers using c

I'm writing a program in which a function that reverses each word in a string. When I call the function, it will pass the pointer to source string and then return the pointer to modified string.
input: Why always me?
output: yhW syawla ?em
But for some reason, it is not working. No errors. And logic seemed fine to me (i'm not that good with c, btw)
Here's my code:
char *reverse_words(char *source_string)
{
char *startW, *endW;
startW = source_string;
while(*startW != '\0')
{
while(*startW == ' ')
startW++; //Skip multiple spaces in the beginning
endW = startW;
while(*endW != ' ' || *endW != '\0')
endW++;
char *_start = startW;
char *_end = endW - 1;
char temp;
while(_end > _start)
{
temp = *_start;
*_start++ = *_end;
*_end++ = temp;
}
startW = endW;
}
return source_string;
}
void main() {
char *s;
s = malloc(256);
gets(s);
printf("%s\n", s);
char *r = reverse_words(s);
printf("\nReversed String : %s",r);
free(s);
free(r);
}
Also, i'm using codeblocks IDE. After I input my string, it prints it back (scanf and printf in main) and after that, the program stops working.
Any help would be appreciated.
First,
while(*endW != ' ' || *endW != '\0')
is an infinite loop, try this instead:
while(*endW != ' ' && *endW != '\0')
Second,
*_end++ = temp;
should be this:
*_end-- = temp;
In the innermost while(_end > _start) loop you increment both _start and _end. So the condition will never become false. (Well, not until _end overflows.)
I'd recommend figuring out how to do step-by-step debugging in your IDE. Then you can easily understand what exactly goes wrong in a case like this, without simulating the execution in your head.
#include <stdio.h>
#include <stdlib.h>
char *reverse_words(const char *source_string){
const char *startW, *endW;
char *p, *rev = malloc(256);
startW = source_string;
p = rev;
while(*startW != '\0'){
while(*startW == ' ')
*p++ = *startW++;
if(!*startW)
break;
endW = startW;
while(*endW != ' ' && *endW != '\0')
endW++;
const char *endW2 = endW;
do{
*p++ = *--endW;
}while(startW!=endW);
startW = endW2;
}
*p = '\0';
return rev;
}
int main() {
char s[256];
scanf("%255[^\n]", s);
printf("%s,\n", s);
char *r = reverse_words(s);
printf("\nReversed String : %s.", r);
free(r);
return 0;
}

Resources