C - Add space inside brackets - c

So I am doing something wrong with this program and would like someones help. I want to add spaces between the brackets and the words inside. For instance if the original string is if (x=5) then I want to make it if ( x=5 ). Simple task I know and here is the code I have:
char* addSpaces(char* line) {
int i,j;
char *result = line;
for (i = 0, j = 0; i<strlen(line); i++,j++)
{
if (line[i] == '[') {
result[j] = line[i];
j++;
result[j] = ' ';
}
else if (line[i] == ']') {
result[j] = ' ';
j++;
result[j] = line[i];
}
else result[j] = line[i];
}
result[j] = 0;
return result;
}
For some reason it freezes when I run the program. I don't get any errors. Any ideas?

Your algorithm can't work if the input and output strings point to the same buffer (which is how you have it set up). Since you're overwriting the input string with a longer string (at least in your example case), you end up overwriting the null terminator. That causes undefined behaviour the next time your loop condition calls strlen, and then all bets are off.
Instead of overwriting the input buffer, allocate a new larger buffer, do the copy, and then return a pointer to that new buffer.

There is no validation of input buffer, or do you just assume that input buffer is long enough to hold after spaces are added? Like Carl said, it's better to allocate memory inside the function (you may need to run through your string once to calculate how much you need to allocate) and then return the newly allocated buffer that is filled with new value.

Related

Strange loop incrementing behaviour in C

I came across this strange loop behaviour in C. Here is the code:
char arr[SIZE];
fgets(arr, SIZE, stdin);
char brr[SIZE];
int i;
for(i=0; arr[i] != '\0'; i++){
if(arr[i] != ' ') brr[i] = (arr[i] + shift - 'a')%26 + 'a';
else brr[i] = ' ';
}
brr[i-1] = '\0';
The code is for Caesar cipher. It takes some text from the user and stores it in arr, then encodes it by shifting the characters, and this encoded version is put in brr. So, if user enters 'car' and shift is 2, brr is 'ect'.
The problem is in the last line. Normally, it should be brr[i] = '\0', but for some reason the loop will increase I once more after the condition is wrong. I have tried different combinations and it seems like the problem is somehow related to fgets.
Also, the problem is not specific to for loop, while behaves exactly the same.
Edit: Thanks for all the answers. I see now that I should have taken '\n' into account.
somehow related to fgets.
Always test the return values of input functions.
// fgets(arr, SIZE, stdin);
if (fgets(arr, SIZE, stdin)) {
// process arr[]
...
if user enters 'car' and shift is 2,
This is unlikely to only typed 'car' (3 characters). Certainly the user typed c a r Enter (4 characters) which filled arr[SIZE] with "car\n".
Rather than encode the 4 characters with if(arr[i] != ' '), test if arr[i] is a letter. Research isalpha().
Code could exit the for() loop early if '\n' is detected.
// for(i=0; arr[i] != '\0'; i++){
for(i=0; arr[i] != '\0' && arr[i] != '\n'; i++){
...
}
// brr[i-1] = '\0';
brr[i] = '\0';
Advanced
brr[i-1] = '\0'; is certainly a problem if i==0, possible if the first character read is a null character.

Beginner C: Reading Age

I'm studying elementary programming in C and I'm doing a challenge to determine the reading age of various sentences. This is achieved by determining the amount of sentences in a string etc. I have some code that does the my first very basic step but it's not quite working as expected.
I'm thinking this is because my knowledge of the strlen function etc isn't sufficient.
I don't want to cheat for the answer as I like the sense of achievement from the problem solving. But would it be possible to get a gentle push in the right direction if at all possible?
char sentence[] = "One fish. Two fish. Red fish. Blue fish.";
int main(void)
{
int sentence_count = 0;
int word_count = 0;
int i;
int length = strlen(sentence);
for (i = 0; i == strlen(sentence); i++) //need to somehow go through a string one char at a time until the end.
{
if (sentence[i] == '.' || sentence[i] == '!' || sentence[i] == ';' || sentence[i] == '?')
{
return sentence_count ++;
}
if (sentence[i] == '\0')
{
return word_count ++;
}
}
printf("There are %i in %i sentences.\n", word_count, sentence_count);
printf("%i\n", length);
}
First Problem -
for (i = 0; i == strlen(sentence); i++)
This state should be -
for (i = 0; i < strlen(sentence); i++)
In your case, it would be terminating on the first iteration itself. However, You need to loop until you have reached the index - strlen(sentence).
Do recall that for loop has syntax - for(initialisation; condition; increment/decrement) will run only until the condition evaluates to true. So, you need the condition to evaluate to true till you have traversed the whole string which is done by the second line of code mentioned above.
A better alternative approach would be -
for (i = 0; sentence[i] != '\0'; i++)
which means the loop will run until you encounter a null-terminated character.
Second Problem -
return sentence_count ++;
.
.
return word_count ++;
Here, you don't need to add the return keyword before the above two statements. The return will directly exit from your program. Simply writing sentence_count++ and word_count++ would be correct.
Third Problem -
sentence[i] == '\0'
This statement doesn't quite fit with the logic that we are trying to achieve. The statement instead must check if the character is a space and then increment the word count -
if (sentence[i] == ' ')
{
return word_count ++;
}
Your for loop condition is the main problem; for (i = 0; i == strlen(sentence); i++) reads as "on entry, set i to 0, enter the body each time i is equal to the length of sentence, increment i at the end of each loop". But this means the loop never runs unless sentence is the empty string (has strlen of 0). You want to test i < strlen(sentence) (or to avoid potentially recomputing the length over and over, use the length you already calculated, i < length).
You also need to remove your returns; the function is supposed to count, and as written, it will return 0 the instant it finds any of the target characters, without using the incremented values in any way. Put a return 0; at the end of main to indicate exiting successfully (optionally, stdlib.h can be included so you can return EXIT_SUCCESS; to avoid magic numbers, but it's the same behavior).
The information in others answers being already covered, here are a couple of other suggestions for your consideration.
Remove function calls, such as strlen() from within the for(;;) loop. You've already obtained the length with:
int length = strlen(sentence);
Now, just use it in your for loop:
for(i = 0; i < length ; i++)//includes replacement of == with <
Encapsulate the working part of your code, in this case the the counting of words and sentences. The following uses a different approach, but its the same idea:
//includes deliminators for common end-of-sentence punctuation:
int count_sentences(const char *buf)
{
const char *delim = {".?!"};//add additional 'end-of-sentence' punctuation as needed.
char *tok = NULL;
int count = 0;
char *dup = strdup(buf);//preserve original input buffer
if(dup)
{
tok = strtok(dup, delim);
while(tok)
{
count++;
tok = strtok(NULL, delim);
}
free(dup);
}
return count;
}
One additional idea, that is out of scope with your original code, but very useful in practice is to remove any parts of the buffer that may not be part of the sentence, i.e. leading or trailing space. In your example, you have the test case for your sentences tightly defined within a string literal:
char sentence[] = "One fish. Two fish. Red fish. Blue fish.";
This is not incorrect, but what would happen at some point your code were to be expected to work with string buffers not so neatly packaged? i.e. leading or trailing white space characters in the buffer to be processed?
"\n\n\tOne fish. Two fish. Red fish. Blue fish.\f"
Removing unknown and unwanted content from the buffer prior to processioning simplifies the code doing the work, in this case counting sentences. Following is a simple example of how this can be done.
//prototype:
char *new = clear_leading_trailing_whitespace(sentence);
char * clear_end_space(const char *buf)
{
char *new = buf;
//clear leading whitespace
while (isspace(*new))
{
new++;
}
//clar trailing whitespace
int len = strlen(new);
while(isspace(*(new + len-1)))
{
len--;
}
*(new + len) = 0;
return buf;
}
Next, the following code segment is intending to count words:
if (sentence[i] == '\0')
{
return word_count ++;
}
But after being initialized to 0, word_count is only incremented once when seeing the null terminator, \0 once. Word count is generally a count of spaces between non-sentence terminating and non-whitespaces characters in a buffer. Or in otherwords, tracking how many clusters of non-whitespace there are. The following is a way to do this:
void countwords(const char *text, *count)
{
bool reading_word = false; // Flag
int words = 0;
for(int i=0; i<strlen(text); i++)
{
if(isspace(text[i])) {
reading_word = false;
}
else if(isalpha(text[i])) {
if(!reading_word) {
reading_word = true;
words++;
}
}
}
*count = words;
}
Functions like this can be used to greatly simplify contents of the main function:
char sentence[] = "One fish. Two fish. Red fish. Blue fish.";
int main(void)
{
int sentence_count = 0;
int word_count = 0;
char *new = clear_leading_trailing_whitespace(sentence);
countwords(new, &word_count);
sentence_count = count_sentences(new);
...
printf("There are %d words in %d sentences.\n", word_count, sentence_count);
}

which code is better ? and how do we understand that ? why is the final print function in the second code sometimes printing random numbers?

I am very new here .. so please excuse me if my question is really unnecessary .. but I think the answer will help me have some faith in myself ..here are two code snippets ..one i got on the website c4learn.com ..
#include<stdio.h>
int main()
{
char s1[100], s2[100];
int i;
printf("\nEnter the string :");
gets(s1);
i = 0;
while (s1[i] != '\0')
{
s2[i] = s1[i];
i++;
}
s2[i] = '\0';
printf("\nCopied String is %s ", s2);
return (0);
}
and the other i wrote myself ..
#include<stdio.h>
int main()
{
char s1[100], s2[100];
int i;
printf("\n Enter the string 1");
gets(s1);
printf("\n Enter the string2");
gets(s2);
for(i=0;i<100;i++)
{
if (s1[i]!='\0')
{
s2[i]=s1[i];
}
}
s2[i]='\0';
printf("\n Copied string is %s ", s2);
return(0);``
}
the problem is while running the code on dev c++ .. the final printf displayed is showing some random characters at the end of the string .. Can anyone help me understand that and which is code is better ? the initial question was ... HOW WILL YOU COPY ONE STRING TO ANOTHER WITHOUT USING ANY INBUILT LIBRARIES ? thank you ..
Your code is not quite right:
Why do you ask for the user input for s2 if you then overwrite it, copying s1?
The for cycle you wrote doesn't stop when s1 is over (I mean the null terminator character '\0') so you are also copying all the chars remaining in s1 after '\0'. If the chars in the array are not initialized (and that's the case for chars after '\0') they of course might result in random characters.
So answering your question, the first code is the right way to do it.
Any way if you want to use a for cycle you could do:
for (i = 0; i < 100; i++) {
s2[i] = s1[i];
if (s1[i] == '\0')
break;
}
You have to break out of the loop when you reach the null terminator character \0. The first code breaks out of the while loop while you're code continues on until i == 100 skipping over the null character. This is why its printing garbage past the original string.
This is what you should do to break out after the null character.
for (i = 0; i < 100; i++) {
s2[i] = s1[i];
if (s1[i] == '\0') break;
}
In the second block of code, after exiting the for loop, i has a value of 100. So you're putting the 0 byte at index 100.
Since an array of size 100 has indexes 0 to 99, you're writing past the end of the array. That causes undefined behavior.
When you're inside of the for loop, you need to break out after you find the null byte.
Also, both programs use gets which is unsafe because it does not perform any bounds checking and may write past the end of the array.

Error using 'free' on char array

I'm new to C and trying to write a command line program with it. I'm trying to free a char array right before the program terminates. But I'm getting a "debug assertion failed" run-time error when it reaches the free command. Before it reaches that point, the program is removing characters in that array up until the first whitespace. I'm using the incrementing technique on the array since I read that was a way to remove chars one by one from an array. Here's that piece of code:
char command[140];
char * input = getInput(); //prompt function for user input
//get string up to first whitespace to separate the command and its parameters
for (i = 0; i < strlen(input); i++)
{
if (input[i] == ' ' || input[i] == '\0')
break;
command[i] = input[i];
}
for (j = 0; j <= i; j++) //removes command and space and leaves parameters
input++;
command[i] = '\0'; //null terminate char array
numParams = getNumParams(input);
free(input); //should've added this line earlier to avoid confusion.
My getInput() function does this:
char * getInput()
{
int n, size = 260;
char * input = (char*)malloc(size);
if (!input) //make sure memory allocation worked
return NULL;
do
{
printf("cmd> "); //prompt
fgets(input, 256, stdin); //get user input/commands
n = strlen(input);
} while (n <= 1);
if (input[n - 1] == '\n') //remove new line from input array
input[n - 1] = '\0';
return input;
}
So after the rest of the program ends I want to be able to free the memory that was allocated in the getInput() function. I'm thinking the way I have the input returning to a char pointer is messing that up. But I'm not sure how to fix it. Any help is appreciated.
You haven't posted the line that calls free. I am assuming that you are calling:
free(input);
I can see why that would be a problem.
You are changing the value of input in the lines:
for (j = 0; j <= i; j++)
input++;
When you call free, the value of the pointer must be what was returned by malloc, calloc, or realloc. If you use any other pointer value, the program is subject to undefined behavior.
Make sure that you keep the value that was returned, so you can call free using it.
char* input = getInput();
char* saved_ptr = input;
// ...
// Change input
// ...
// Deallocate memory using the original pointer
free(saved_ptr);
The problem is most likely these two lines:
for (j = 0; j <= i; j++) //removes command and space and leaves parameters
input++;
Here you modify the pointer, making you loose the original pointer that you should pass to free. You need to save the original pointer in a temporary variable, and pass that to free.
You get an error because you are modifying the input pointer here:
for (j = 0; j <= i; j++) //removes command and space and leaves parameters
input++;
After this operation, input no longer points to the start of memory, allocated by malloc. Thus giving you the error. Instead, copy input to another pointer variable.
Also, consider doing allocation outside of getInput() since it is considered a good practice to allocate and free the memory in the same function, if possible.

C decryption program - Heap corruption detected

I'm trying to write a very simple code.
Basically, I get an "encrypted" string from the user, and my code decrypts it with the following rule:
"From the first letter of the string subtract one, from the second subtract 2, from the third subtract 3 and so on. Spaces stay unchanged"
I wrote this code:
char* q4(char* str_in)
{
char* str_out;
int str_out_length,i,temp;
str_out_length=strlen(str_in)+1; //+1 for null terminator
str_out=(char*)malloc(sizeof(char)*str_out_length);
str_out[str_out_length]='\0'; //null terminator at last index
for(i=0;i<str_out_length;i++)
{
if(str_in[i]!=' ')
{
temp=str_in[i];
str_out[i]=(char)(temp-i-1);
}
else
str_out[i]=str_in[i];
}
return str_out;
}
My code seems to return incorrect output, and not only that, I also have an error that reads "Heap Corruption detected".
For example, when the string i input is Btwlzx Dqqes, the output is Arthur <hgZg<= and an error. Basically the first word is correct. The second one is rubbish. And if I enter just Btwlzx, then the output is Arthur- and said error.
What am I doing wrong?
str_out_length=strlen(str_in)+1; //+1 for null terminator
str_out=(char*)malloc(sizeof(char)*str_out_length);
It seems that the terminator is placed one byte behind the allocated buffer. Try
str_out[str_out_length - 1]='\0';
And similarly, perhaps
for(i=0;i<str_out_length;i++)
should be
for(i=0;i<str_out_length-1;i++)
Or, maybe better, change
str_out_length=strlen(str_in)+1; //+1 for null terminator
str_out=(char*)malloc(sizeof(char)*str_out_length);
with
str_out_length=strlen(str_in);
str_out=(char*)malloc(sizeof(char)*(str_out_length+1)); //+1 for null terminator
and then use str_out_length without -1-correction.
Update (after reading the comment)
print_decrypted("Btwlzx Dqqes Eq|pj2 Tjhvqujs Iqoqjy bpg Eqfxtx Xcwwtt");
outputs
Arthur Conan Doyle, Sherlock Holmes and Doctor Watson
void print_decrypted(char* str)
{
int k = 0;
for (int i = 0; str[i] != '\0'; i++)
if (str[i] != ' ')
{
printf("%c", str[i] - k - 1);
k++;
}
else
{
printf(" ");
k = 0;
}
printf("\n");
}

Resources