String example doesn't behave as expected - c

I'm learning C and I've been following the "Head First C" book. I arrived at an example where this is the resulting code:
#include <stdio.h>
#include <string.h>
char tracks[][80] = {
"I left my heart in Harvard Med School",
"Newark, Newark - A wonderful town",
"Dancing with a Dork",
"From here to maternity",
"The girl from Iwo Jima",
};
void find_track(char search_for[])
{
int i;
for(i = 0; i < 5; i++) {
if (strstr(tracks[i], search_for))
printf("Track %i: '%s'\n", i, tracks[i]);
}
}
int main()
{
char search_for[80];
printf("Search for: ");
fgets(search_for, 80, stdin);
find_track(search_for);
return 0;
}
I've double, triple, and quadruple checked. This code is exactly the same as the example in the book. At first I made it my own so that I'm not just copying without learning, but when I didn't work I literally copied it to make sure. The code askes for a string, and then prints any tracks containing the string you gave it. In the book, when the example code is run, the string given was "town", and the code prints "Newark, Newark - A wonderful town." However, when I do the exact same thing it prints nothing but a newline.
I can't stress enough, this code is exactly the same as in the book, yet it behaves differently. Can someone see why?

The problem is in this statement
fgets(search_for, 80, stdin);
Function fgets includes in the string the new line character. You have to remove it from the string. For example
size_t n = strlen( search_for );
if ( n != 0 && search_for[n-1] == '\n' ) search_for[n-1] = '\0';
From the C Standard
Description
2 The fgets function reads at most one less than the
number of characters specified by n from the stream pointed to by
stream into the array pointed to by s. No additional characters are
read after a new-line character (which is retained) or after
end-of-file. A null character is written immediately after the last
character read into the array.

Related

strstr function and multi dimensional arrays in c

#include <stdio.h>
#include <string.h>
void find_track(char *search_for);
char tracks[][80] = {
"I left my heart in Harvard Med School",
"Newark, Newark - a wonderful town",
"Dancing with a Dork",
"From here to maternity",
"The girl from Iwo Jima"
};
int main() {
char *to_search_str;
printf("Search for: ");
fgets(to_search_str, 80, stdin);
find_track(to_search_str);
return 0;
}
void find_track(char *search_for) {
int i;
for (i=0; i<5; i++) {
if (strstr(tracks[i], search_for)) {
printf("Track %d: '%s'\n", i, tracks[i]);
}
}
}
The program is supposed to search for a string in every string in the tracks multi dimensional array but the strstr() function in the find_track is always returning null no matter the input (even if we input the a sub string of a string from tracks multi dimensional array). I don't know why this is happening?
EDIT:
After correction
#include <stdio.h>
#include <string.h>
void find_track(char *search_for);
char tracks[][80] = {
"I left my heart in Harvard Med School",
"Newark, Newark - a wonderful town",
"Dancing with a Dork",
"From here to maternity",
"The girl from Iwo Jima"
};
int main() {
char to_search_str[80];
printf("Search for: ");
fgets(to_search_str, 80, stdin);
to_search_str[strlen(to_search_str)-1] = '\0';
find_track(to_search_str);
return 0;
}
void find_track(char *search_for) {
int i;
for (i=0; i<5; i++) {
if (strstr(tracks[i], search_for)) {
printf("Track %d: '%s'\n", i, tracks[i]);
}
}
}
Output
Most likely the issue with input via fgets().
You are reading into an uninitialized pointer to_search_str, which does not point to a valid memory location. In this case, you can simply change this to an array, like char to_search_str[80] = {0}; and get done with it.
You need to trim the trailing newline that's stored in the input buffer.
From the man page, (emphasis mine)
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte ('\0') is stored after the last character in the buffer.
A quick way of getting that done is to_search_str[strcspn(to_search_str, "\n")] = 0;, but there are more robust ways mentioned in this other answer
You aren't allocating to_search_str pointer, the char * pointer you pass to fgets as the destination buffer. Being it actually uninitialized, this causes undefined behavior that normally ends with a program crash.
You just need to allocate it, statically or dynamically.
The simplest solution consists in just defining a static array in the stack:
#include <string.h>
#define LEN 80
int main() {
char to_search_str[LEN];
printf("Search for: ");
fgets(to_search_str, LEN, stdin);
/* Remove trailing newline from the string to search */
to_search_str[strcspn(to_search_str, "\n")] = 0;
find_track(to_search_str);
return 0;
}
The size of the array is 80 because you use this number as the size parameter in fgets. Please note the use of a #define for the constant 80, making possible to change it in a easier way.
The dynamic allocation in the heap involves the use of malloc() function (and free() as soon as the array is not needed anymore):
#include <string.h>
#define LEN 80
int main() {
char * to_search_str = malloc(LEN);
printf("Search for: ");
fgets(to_search_str, LEN, stdin);
/* Remove trailing newline from the string to search */
to_search_str[strcspn(to_search_str, "\n")] = 0;
find_track(to_search_str);
free(to_search_str);
return 0;
}
Note: since fgets retains trailing newline ``\n'` in the output buffer, we have to remove it. I used the clever oneliner solution described here.
char *to_search_str; is an uninitialized pointer, writing to it will result in undefined behavior. You have to allocate memory or use an array instead char to_search_str[100]; for example.
Also don't forget that fgets will also read the newline into the buffer, which you have to remove.
This code snippet in main
char *to_search_str;
printf("Search for: ");
fgets(to_search_str, 80, stdin);
invokes undefined behavior because the pointer to_search_str is not initialized and has indeterminate value.
It seems you at least mean
char to_search_str[80];
printf("Search for: ");
fgets(to_search_str, 80, stdin);
The function fgets can append the new line character '\n' to the entered string.
You need to remove it for example the following way
to_search_str[ strcspn( to_search_str, "\n" ) ] = '\0';
The function find_track should be declared at least like
void find_track( const char *search_for);
Though it is a bad idea when a function definition relies on global variables.
Also the approach of finding relevant strings is not good. For example the user can enter a string that contains only one character 'a'. In this case all records will satisfy the condition. You should check that the searched string forms a word (a sequence of characters separated by spaces) in a string in the array.

fgets -difference between array size and int num

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char s[2];
printf("Enter some things here: ");
fgets(s, 10, stdin);
// strrev(s);
printf("\nText: %s", s);
printf("\nsize: %d\n", sizeof(s));
for(int i = 0; i < 10; i++)
printf("%c\n", s[i]);
return 0;
}
//CONSOLE:
/*Enter some things here: abcdefghiklmn
Text: abcdefghi
size: 2
a
b
(this is a symbol)
g
h
i
*/
A problem using fgets when i make the "INT" that fgets read higher than size of the arra (pic)
So where do other letters go?, what truly happens in the memmory ??
thanks everyone!
**update:
Tks everyone. Btw, i noticed that regardless of any "string" and size of s (smaller than the "int" - basically just some testing), the output will be
1. those that stored in array s
2. weird symbol ( maybe it's the "\0" to construc a string )
3. blank blank blank...
4. last part of my inputed String before.
Is there any concidences here ? Just some curiosity, tks all again ^^
Your string has a size of 2. so it can only collect from the user 1 character because after it, it has to put a '\0' for marking the end of the string.
if you want to put 10 char - try s[11].
like this you can collect 10 chars and then the last char (s[10]) will be '\0' (will be inputed there automatically).

Trying to return the char in the middle of an input (char array) gives "segmentation fault (core dumped)"? [duplicate]

This question already has answers here:
Why does C's printf format string have both %c and %s?
(11 answers)
Closed 4 years ago.
In a nutshell, I have to be able to return the character in the middle of an input (char array) for part of our first C assignment. What I have so far, however, is code that returns "Segmentation fault (core dumped)". I read into this a little bit, and learned that essentially I may be trying to access/modify data that is "not available to me", so-to-speak. Here is my code:
#include <stdio.h>
#include <string.h>
char input[30];
int inputLen;
char midChar;
int main()
{
printf("Type in some text, and the press the Return/Enter key: ");
fgets(input,sizeof(input),stdin);
printf("\nYour input: %s",input);
inputLen = strlen(input)-1;
printf("Length of your input is %d characters.",inputLen);
if((inputLen % 2) == 0) {
midChar = input[(inputLen/2)+1]; // >>> PROBLEM HERE <<<
}
else {
midChar = input[((inputLen+1)/2)+1]; // >>> PROBLEM HERE <<<
}
printf("%s",midChar);
return 0;
}
The two lines with >>> PROBLEM HERE <<< are the lines which I believe I've narrowed down to be the source of the problem.
Please Note: I have taken an introductory class in Java, and last semester took a class half-devoted to MATLAB, so I do have a little bit of programming intuition -- However, I am a 100% beginner in C, so I would appreciate some clear elaboration behind any help you guys may offer. I am not familiar with most functions/syntax unique to C, so I'm sure there will be cringe-worthy lines of code above for those well-versed in this language. If this is the case, feel free to include any other tips in your answers. Thanks!
You're printing a char with %s, so the program is treating your input as a pointer (to a char array). It's not a valid such thing.
You meant %c for a single character.
Your compiler should tell you about this. Turn warnings on!
A late addition:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// This will be the default value if a string of length 0 is entered
char midChar = 0;
int inputLen;
int bufferLen = 31;
char* input = (char*)malloc(sizeof(char) * bufferLen);
printf("Type in some text, and the press the Return/Enter key: ");
fgets(input, bufferLen, stdin);
printf("\nYour input: %s", input);
inputLen = strlen(input);
if (input[inputLen - 1] == '\n') {
inputLen--; // ignore new line character
}
printf("Length of your input is %d characters.\n", inputLen);
if (inputLen > 0) {
midChar = input[inputLen / 2]; // take right of middle for even number
}
printf("%c\n", midChar);
return 0;
}
In your previous post you used sizeof(input) which is not recommended for reasons described in this post. It is better practice to hold the length of the array in a separate variable, here bufferLen.
Also the use of global variables here input inputLen midChar is generally discouraged as they lead to unexpected behaviour during linking and make program flow harder to understand.
I initialised the memory for the buffer dynamically so the bufferLen could be changed in the program.
When computing the length of the input one must consider the newline character \n which is retained if a small enough string is entered but not if the entered string exceeds the bufferLen.
For strings with even lengths I arbitrarily took the character to the right. The inputLen zero case is also handled.
This whole answer is only an addition to the first one which already found the bug correctly, because I was late to the party.
Other than print char problem, I think there is also a problem at where you indicated.
ex. if input string is abc, inputLen will be 3, midchar index should be at 1 since array index in C start from 0. However ((inputLen+1)/2)+1 gives 3. This probably won't directly cause the segfault but will give wrong answer.
You can replace
if((inputLen % 2) == 0) {
midChar = input[(inputLen/2)+1]; // >>> PROBLEM HERE <<<
}
else {
midChar = input[((inputLen+1)/2)+1]; // >>> PROBLEM HERE <<<
}
with
midChar = input[inputLen/2];
since C will truncate when doing integer division.
a b c -> 3/2 = 1
[0] [1] [2]
a b c d -> 4/2 = 2
[0] [1] [2] [3]
Other than that, you also need to make sure the inputLen is not 0
Although "pretty lady" (#LightnessRasesInOrbit) up here is correct, let me explain what is happening when you do this:
printf("%s\n", charVar);
or this:
printf("%s\n", intVar);
or this:
printf("%s\n", floatVar);
Or when you print things using pritnf() with %s. You have to understand how does printf ("%s", string) work!! So when printf gets %s it looks for C string or in other words, character array terminated with '\0'. If it does not '\0' it will segfault. In depth, printf() works like this:
char name[4];
printf("Hello ", name);
now printf does following:
gets the size of 1st variable ("Hello")
gets the size of 2nd variable (name) How? Simple by this loop:
int varSize;
for (varSize = 0; varSize != '\0'; ++varSize);
moves "Hello" into buffer
Determine the size of second parameter. How, by doing this:
does following
if ("%d")
// read intVar and attach it to the buffer
if ("%f")
// read floatVar and attach it to the buffer
if ("%s")
for (int i = 0; stringVar[i] != '\0'; ++i)
// push each char into the buffer
So I hope you see what is happening if one of for() loops does not find '\0' character. If you do good if you don't well it continues reading through until it segfaults.
NOTE:
This is oversimplified pseudo code on how printf() works and is not actual implementation, this is only for OP to understand what is going on.

Book mistake? (Head first C)

In the book Head First C, fist edition, there is this progam:
#include <stdio.h>
#include <string.h>
char tracks[][80] = {
"I left my hearth in Harvard Med School",
"Newark, NewarK - a wonderful town",
"Dancing with a Dork",
"From here to maternity",
"The girl from Iwo Jima",
};
void find_track(char search_for[]) {
int i;
for(i = 0; i < 5; i++) {
if (strstr(tracks[i], search_for)) {
printf("Track %i: '%s'\n", i, tracks[i]);
}
}
}
int main() {
char search_for[80];
printf("Search for: ");
fgets(search_for, 80, stdin);
find_track(search_for);
return 0;
}
However, when compiled and tested, you don't get the expected results. After breaking my head for a bit, I decided to look for the documentation for fgets, I discovered that the function reads up to an including a newline character, which is why no matter what I search for, I never get the expected result. However, in the book they say the program works when tested. Is this a book error, or am I missing something?
PS. the error can be easily fixed using scanf, which becomes obvious once you know why the program doesn't work as expected.
PS2. I remember C++ has some syntax to ignore the newline character. Does C have something similar?
I also encountered the error, but the newest version has corrected the error. In fact, the function 'char *fgets(char *s, int size, FILE *stream) works like what you said. It only read up to size-1. If it encounters a null character('\0'), it only add ('\0') to the buffer. So when we used the fuction find_track(), we can't get anything because of search_for has contained ('\0'). There are some solutions to handle it.
scanf("%79s", search_for). Attention, don't only use %s because you wil encounter the same situation like fgets().
scanf("%79[^\n]", search_for), it has some difference with the former one. The advantage that you can avoid the blank problem.
PS: The first one, you can add a new fuction to avoid the blank problem. Like this:
void remove_tail_newline(search_for)
{ size_t len = strlen(str);
if(len > 0 &&str[len-1] =='\n') str[len -1] ='\0';
}
You're right; this is indeed an error in the book.
I recommend against using scanf for user input. It is hard to use correctly, error detection is non-trivial (if it is possible at all), and error recovery is impossible.
All user input should be done by reading a line first, then analyzing it afterwards (e.g. using strtol or sscanf). Reading a line can be done with fgets, but unfortunately there's no syntax or feature for ignoring the \n.
What you can do instead is:
if (fgets(line, sizeof line, stdin)) {
line[strcspn(line, "\n")] = '\0'; // remove trailing '\n', if any
do_stuff_with(line);
}

C working with strings and SEGFAULT

Hello and sorry for my bad english.
I am starting with C language, but I didnt get pointers well...
I searched for similar topics, but I didnt get it from them, so I created own topic.
I have got main function, where I call function newSpeak.
There is my code of newSpeak, but there isnt everything...
char * newSpeak ( const char * text, const char * (*replace)[2] )
{
int i;
char * alpha;
for(i=0;i<4;i++)
{
alpha=strstr(text, replace[0][4]);
if(alpha[0])
strncpy (alpha,replace[1][0],10);
}
return 0;
}
Thanks for answering
EDIT: I have found source of the problem.
It works, when I dont use for cycle and run it once. But it doesnt work even when the condition in for cycle is i<1, which should make it run only once...This is strange for me...
alpha=strstr(text, replace[0][4]);
if(alpha[0])
// looks crashy
man strstr:
These functions return a pointer to the beginning of the substring,
or NULL if the substring is not found.
EDIT:
It is difficult to tell what you are trying to do, but below find an arbitrary adaptation of your code. If it were my program, I would write it very differently. I mention that because I do not want someone to read this and think it is the way it should be done.
#include <stdio.h>
#include <string.h>
void newSpeak (char *text, const char *replace[4][2])
{
int i, j;
char *alpha;
for (i = 0; i < 4; i++) {
if (alpha = strstr(text, replace[i][0])) {
for (j = 0; alpha[j] && replace[i][1][j]; j++)
alpha[j] = replace[i][1][j];
}
}
}
int main ()
{
char buf[100] = "abc";
const char *replace[4][2] = {
{ "a", "e" },
{ "b", "f" },
{ "c", "g" },
{ "d", "h" },
};
newSpeak(buf, replace);
puts(buf);
}
The line
strncpy (alpha,replace[1][0],10);
should have generated a compiler warning (and NEVER ignore compiler warnings). The function prototype is
char *strncpy( char *dest, char *source, int n);
But you are passing it replace[1][0] which is a character. It might work if you passed
strncpy( alpha, &replace[1][0], 10);
Even then I still worry. It could be that since alpha is pointing to a block of memory in the block pointed to by text which is a const char*, that you are not allowed to modify that memory.
EDIT I think my first point is wrong - I misread your prototype. But I'm pretty sure the second point is valid (and probably the reason for the segfault).
second edit
It is possible that text does not have sufficient memory allocated to have 10 characters copied into it from replace. Realize that the thing you are matching against (replace[0][4]) and the thing you are copying (replace[1][0]]) are not the same thing; also, you are looping over i but not using that value ... makes me wonder if there is a typo (I am not clairvoyant and cannot figure out what you wanted to change from loop to loop).
You need to check the size of the thing you are copying into:
strncpy(alpha, replace[1][0], (strlen(alpha)<10)?strlen(alpha):10);
would ensure you are copying no more than 10 characters, and no more than there's space in alpha.
This is "on top of" everything else already pointed out (of which using if (alpha!=NULL) instead of if(alpha[0]) is a big one.)
EDIT 3 - I think I figured out the majority of the problems with your code now... see http://codepad.org/YK5VyGAn for a small "working" sample.
Issues with your code included:
You declare text as const char*, then proceed to modify it
You declare replace as const char* (*replace)[2], then address element replace[0][4] (4 > 2...)
You assign the return value of strstr to alpha; this could be NULL (no match), yet you test for alpha[0] (which will fail if alpha == NULL).
When you copied the replacement string, you copied "up to 10 characters" - regardless of whether (a) the target string could accommodate this, and (b) the source string had this many characters. The result might be that you copy the full source string (including the terminating '\0') so that you will not find another match afterwards (you have "deleted" the rest of the string). And then you will run into the "strstr returns NULL" error...
Not sure (without seeing your input string or "replace" strings) which of these actually caused your code to fail - I have written a small program that addresses all of these mistakes. You can find it at http://codepad.org/4jSOnmPy - reproduced here:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MIN(a,b) (a>b)?(b):(a)
char * newSpeak (const char *text, const char *(*replace)[5] ){
int ii=0, n;
char *alpha, *beta;
printf("length of input string is %d\n", strlen(text));
beta = malloc(strlen(text)+1);
printf("allocated %d bytes\n", strlen(text)+1);
fflush(stdout);
strcpy(beta, text);
printf("copy OK: beta now %s\n", beta);
fflush(stdout);
for(ii = 0; ii < 4; ii++) {
// alpha=strstr(beta, replace[0][0]);
alpha=strstr(beta, "a");
printf("alpha is '%s'\n", alpha);
fflush(stdout);
if(alpha!=NULL) {
char *rs;
rs = replace[1][ii];
printf("ii = %d; alpha now: '%s'\n", ii, alpha);
fflush(stdout);
n = MIN(strlen(alpha), strlen(rs));
printf("n is now %d\n", n);
fflush(stdout);
printf("going to copy at most %d characters from '%s' into '%s'\n", n, rs, alpha);
fflush(stdout);
strncpy (alpha,rs,n);
printf("beta is now '%s'\n", beta);
fflush(stdin);
}
else printf("no match found\n");
}
return beta;
}
int main(void) {
char* r[2][5]={{"a","b","c","d", "e"}, {"o","e","i","u","s"}};
char* myText = "this is a vary sally strang";
printf("NewSpeak: %s\n", "hello world");
printf("converted: %s\n", newSpeak(myText, r));
return 0;
}
Output:
NewSpeak: hello world
length of input string is 27
allocated 28 bytes
copy OK: beta now this is a vary sally strang
alpha is 'a vary sally strang'
ii = 0; alpha now: 'a vary sally strang'
n is now 1
going to copy at most 1 characters from 'o' into 'a vary sally strang'
beta is now 'this is o vary sally strang'
alpha is 'ary sally strang'
ii = 1; alpha now: 'ary sally strang'
n is now 1
going to copy at most 1 characters from 'e' into 'ary sally strang'
beta is now 'this is o very sally strang'
alpha is 'ally strang'
ii = 2; alpha now: 'ally strang'
n is now 1
going to copy at most 1 characters from 'i' into 'ally strang'
beta is now 'this is o very silly strang'
alpha is 'ang'
ii = 3; alpha now: 'ang'
n is now 1
going to copy at most 1 characters from 'u' into 'ang'
beta is now 'this is o very silly strung'
converted: this is o very silly strung
Note - I added lots of "useless" output, including fflush(stdout); statements. This is a good way to ensure that debug printout shows you exactly how far into a program you got, and what was going on before it crashed - without the fflush it's possible you are missing many lines of output (because they never "made it to the screen").
It's obvious from the above that if your replacement strings are a different length than the string they replace, you will get some strange overwriting (I left both search and replace string length at 1 but there is no reason why that should be so).
I hope this helps!

Resources