Struggling with strings. What is wrong with my function? - c

I am trying to write a small function to trim left spaces from a string, but I cannot get it right. In this version, I get the following error:
bus error: 10
Could anyone please explain to me what I am doing wrong? I am not looking so much for an alternative piece of code, but would like to understand the errors in my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
void trim_string(char *);
int main(int argc, char *argv[]) {
char *temp = " I struggle with strings in C.\n";
trim_string(temp);
printf("%s", temp);
return 0;
}
void trim_string(char *string) {
char *string_trimmed = "";
int i=0, j=0;
while (isblank(string[i])) {
i++;
}
while (string[i] != '\0') {
string_trimmed[j] = string[i];
i++;
j++;
}
string_trimmed[j] = '\0';
strcpy(string, string_trimmed);
}
I have now found a workaround solution, shown below. But I am still not very clear about what I did wrong in the first place:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_LENGTH 100
void trim_string(char [MAX_LENGTH]);
int main(int argc, char *argv[]) {
char temp[MAX_LENGTH] = " I struggle with strings in C.\n";
trim_string(temp);
printf("%s", temp);
return 0;
}
void trim_string(char string[MAX_LENGTH]) {
char string_trimmed[MAX_LENGTH];
int i=0, j=0;
while (isblank(string[i])) {
i++;
}
while (string[i] != '\0') {
string_trimmed[j] = string[i];
i++;
j++;
}
string_trimmed[j] = '\0';
printf("c\n");
strcpy(string, string_trimmed);
}

Both string and string_trimmed point to string literals, here in main:
char *temp = " I struggle with strings in C.\n";
^
|
This is a string literal
temp points to a string literal and the standard says you are not allowed to modify them.
In the function trim_string you are modifying a them which is undefined behavior of which a bus error is one possible result, although anything can happen.
string_trimmed either needs to be an array like this:
char string_trimmed[n] ;
where n is the size of your input using strlen(string) would probably make sense or dynamically allocated via malloc which you would need to free at the end of your function. The same things goes for your input from main, this would work as a substitute:
char temp[] = " I struggle with strings in C.\n";
For completeness sake, the draft C99 standard section 6.4.5 String literals paragraph 6 says (emphasis mine):
It is unspecified whether these arrays are distinct provided their elements have the
appropriate values. If the program attempts to modify such an array, the behavior is
undefined.

Related

Stack smashing detected in C - why does this happen?

I have the following function, which, given a string, should find the most recurrent couple of letters in it and store the result in a different string.
For example - for the string "ababa", the most recurrent couple would be "ba", and for "excxexd" it would be "ex". This is the code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
void printError(){
printf("Error: please check your input\n");
}
bool isLexicographicallyPreceding(char couple1[], char couple2[])
{
if (strcmp(couple1, couple2)>=0) return true;
return false;
}
void coupleDetector(int length, char word[], char result[])
{
char couples[length-1][2];
for (int i=0; i<length-1; i++)
{
char couple[2] = {word[i], word[i+1]};
strcpy(couples[i], couple);
}
char element[]="";
int count=0;
for (int j=0; j<length-1; j++)
{
char tempElement[2];
strcpy(tempElement,couples[j]);
int tempCount=0;
for (int p=0; p<length-1; p++)
{
if (couples[p]==tempElement) tempCount++;
}
if (tempCount>count)
{
strcpy(element, tempElement);
count=tempCount;
}
if (tempCount==count)
{
if (isLexicographicallyPreceding(tempElement,element) == true) strcpy(element, tempElement);
}
}
strcpy(result,element);
}
int main() {
//Supposed to print "ba" but instead presents "stack smashing detected".
int length=5;
char arr[] = "ababa";
char mostCommonCouple[2];
coupleDetector(length,arr,mostCommonCouple);
printf("%s", mostCommonCouple);
return 0;
}
The code compiles without errors, but for some reason does not work as intended but prints out "stack smashing detected". Why would that be? Advices would be very helpful.
Thanks.
In trying out your program, I found a few of your character arrays undersized. Character arrays (strings) need to be sized large enough to also include the null terminator value in the array. So in many locations, having a two-character array size is not sufficient and was the cause of the stack smashing. With that in mind, following is a refactored version of your program.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
void printError()
{
printf("Error: please check your input\n");
}
bool isLexicographicallyPreceding(char couple1[], char couple2[])
{
if (strcmp(couple1, couple2)>=0) return true;
return false;
}
void coupleDetector(int length, char word[], char result[])
{
char couples[length-1][3];
for (int i=0; i<length-1; i++)
{
char couple[3] = {word[i], word[i+1], '\0'};
strcpy(couples[i], couple);
}
char element[3]; /* Define the character array */
strcpy(element, ""); /* Then initialize it if need be */
int count=0;
for (int j=0; j<length-1; j++)
{
char tempElement[3];
strcpy(tempElement,couples[j]);
int tempCount=0;
for (int p=0; p<length-1; p++)
{
if (couples[p]==tempElement) tempCount++;
}
if (tempCount>count)
{
strcpy(element, tempElement);
count=tempCount;
}
if (tempCount==count)
{
if (isLexicographicallyPreceding(tempElement,element)) strcpy(element, tempElement);
}
}
strcpy(result,element);
}
int main()
{
//Supposed to print "ba" but instead presents "stack smashing detected".
int length=5;
char arr[] = "ababa";
char mostCommonCouple[3]; /* Notice size requirement to also contain the '\0' terminator */
coupleDetector(length,arr,mostCommonCouple);
printf("%s\n", mostCommonCouple);
return 0;
}
Here are some key points.
Viewing the code, most sizes for arrays was enlarged by one to accommodate storage of the null terminator.
Work fields such as "element" need to be defined to their proper size so that subsequent usage won't also result in stack smashing.
Testing out the refactored code resulted in the following terminal output.
#Vera:~/C_Programs/Console/Recurrent/bin/Release$ ./Recurrent
ba
So to reiterate, be cognizant that character arrays normally need to be defined to be large enough to contain the largest expected string plus one for the null terminator.
Give that a try and see if it meets the spirit of your project.

Segmentation fault (core dumped) error for C program

I am trying to run below program in an online C compiler. But I get segmentation error. Can you help me fix this
#include <stdio.h>
#include <string.h>
int main()
{
char string[15] = "Strlwr in C";
printf("%s",tolower(string));
return 0;
}
Following is the prototype of tolower
int tolower(int c);
You should pass an int or something like char which can safely convert to int. Passing char * (Type of string) like you do leads to UB.
To convert a string to lowercase, you need to convert each character separately. One way to do this is:
char string[15] = "Strlwr in C";
char lstr[15];
int i = 0;
do {
lstr[i] = tolower(string[i]);
} while(lstr[i] != '\0');
printf("%s", lstr);
You are using tolower incorrectly. This function returns int and gets int as a parameter (here is it's declaration: int tolower(int c);). What you want to do is call it on each char of your char array, and print each one:
char string[15] = "Strlwr in C";
for(int i = 0; i < strlen(string); i++)
printf("%c",tolower(string[i]));
Read cplusplus.com/reference/cctype/tolower It takes a single int as parameter, not char and not array.
You probably want to use a loop on "string", which processes each in turn.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(void)
{
int i;
char string[15] = "Strlwr in C";
for (i=0; i< sizeof(string)/sizeof(char); i++)
{
string[i]=(char)(tolower((int)string[i]));
}
printf("%s\n",string);
return 0;
}
Output:
strlwr in c

If statement looks acceptable, but has error: expected expression before ‘)’ token

I have minimized my code to just what is necessary to reproduce this error. I have what I believe is a perfectly fine if statement, but gcc insists that it is not a valid statement.
#define SOMECHAR *
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
char* my_string = (char*) malloc(sizeof(char[5]));
strcpy(my_string, "aa*a");
int i;
for (i=0; i< sizeof(my_string); i++){
if(strcmp(&my_string[i], SOMECHAR) == 0){
printf("%s", "b");
} else {
printf("%s", &my_string[i]);
}
}
}
First, size_of(my_string) is size of a pointer, not size of array it point to.
Next, strcmp(&my_string[i], SOMECHAR) will be expanded to strcmp(&my_string[i], *), you need to:
#define SOMECHAR "*"
However, I believe you want this instead:
if(my_string[i] == '*'){
putchar('b');
} else {
putchar(my_string[i]);
}
And as M.M said in comment, you leak the memory you allocated
If you really need to define that character, simply do this:
#define SOMECHAR '*'
/*some other code */
if(my_string[i] == SOMECHAR){
putchar('b');
} else {
putchar(my_string[i]);
}
Thanks to your comments and suggestions, I figured out what I really wanted.
The first problem is to #define a character, it needs to be in single quotes. And sizeof() is totally for describing the size of a pointer, not how long it is. I should be using strlen(). Rookie mistake. And my main method here doesn't have a return, so that would be a problem once fixing the error I already had.
But there's a much better way to do what I needed without looping and condition checking. In string.h there is a function called strchr that will return a pointer to the last character in a string before matching a given character. I've modified my code like this:
#define SOMECHAR '*'
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
char* line = (char*) malloc(sizeof(char[5]));
strcpy(line, "aa*a");
int i;
char* ending;
printf("%s\n", line);
ending = strchr(line, SOMECHAR);
ending[0] = '\0';
printf("%s\n", line);
return 0;
}
This terminates a given string at the character before the match. Which is what my assignment requires. Thanks all for your help.

Why isn't isalpha working?

I'm working with C and I need to check that the user inputed second command line argument argv[1] is made up of only alphabetical charchaters and if not, to do what is inside the else loop. I used the is alpha function but when i compile and run the program no matter what my second command line argument is (alphabetical or otherwise), its always executing the "else loop". How do i fix this?
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
int a = argc;
if (a != 2)
{
return 1;
}
string b = argv [1];
int c = strlen(b);
string m;
for (int i = 0; i < c; i++)
{
if (isalpha(b[c]))
{
m = GetString();
}
else
{
printf("Please provide a valid keyword\n");
return 1;
}
}
}
Try replacing
if (isalpha(b[c]))
with
if (isalpha(b[i]))
Currently you are checking the element at the index which is the result of strlen(b) at every iteration of your loop. Because array indices are zero based in C b[strlen(b)] is referencing '\0', the null terminator.
In reference to the Keith Thompson comment below and the answer to this question you should actually be casting the value passed to isalpha to an unsigned char to ensure that undefined behaviour is not invoked.
Thus you should change your code to
if (isalpha((unsigned char)b[i]))
to ensure there is no UB
Use isalpha(b[i]) instead of isalpha(b[c])
like this:
if (isalpha(b[i]))
{
m = GetString();
}

What's wrong in this code?

I was trying to mimic strtok functionality but getting segmentation fault. Please help me out here.
Here is my code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char argv[])
{
int i=0;
char c[]="get the hell out of here";
char *p;
char *temp=(char *)malloc(100);
while(c[i]!='\0')
{
if(c[i]!=' ')
{
*temp=c[i];
temp++;
i++;
}
else
{
*temp='\0';
printf("printing tokenn");
puts(temp);
i++;
temp="";
}
}
return 0;
}
temp="";
This causes temp to point at unmodifiable memory, leading to a fault the next time you try to modify through it. You wanted to restore temp to the value you got from malloc (which you forgot to save).

Resources