Runtime error: reading uninitialized value, how can I fix it? - c

This function is basically just supposed to compare 2 strings and return their ASCII difference if they are different. It works perfectly fine when I compile it with the GCC compiler, but when I run it through the online compiler that is used to upload our classes homework, I get this error message:
Error near line 98: Reading an uninitialized value from address 10290
Line 98 is marked in the below code. I am not quite sure what the problem is and how I'm supposed to fix it. Does anyone have an idea?
int stringCompare(char * pStr1, char * pStr2) {
int n = 100;
int difference;
for (int i = 0; i < n; i++) {
difference = pStr1[i] - pStr2[i]; // line 98
if (difference != 0) {
return difference;
}
}
return difference;
}

Your code can skip over EOLN, if string equals, and try to compare memory after end of lines. To fix this, you need instantly return, if both string equals, and you see EOLN char '\0' in both strings at position i. Try my fix:
int stringCompare(char * pStr1, char * pStr2) {
int n = 100;
int difference;
for (int i = 0; i < n; i++) {
difference = pStr1[i] - pStr2[i];
if (difference != 0 || pStr1[i] == '\0') {
return difference;
}
}
return difference;
}

The problem in your code is that you fail to check the real length of the strings before indexing them. You are iterating with i from 0 to 99, but you do not check for the NUL terminator (\0) that marks the end of a string and therefore your for loop goes beyond the end of the string resulting in undefined behavior accessing memory that is not supposed to (which is what the error is telling you).
The correct way to iterate over a string, is not to loop a fixed amount of cycles: you should start from index 0 and check each character of the string in the loop condition. When you find \0, you stop. See also How to iterate over a string in C?.
Here's a correct version of your code:
int stringCompare(char *pStr1, char *pStr2) {
size_t i;
for (i = 0; pStr1[i] != '\0' && pStr2[i] != '\0'; i++) {
if (pStr1[i] != pStr2[i])
break;
}
return pStr1[i] - pStr2[i];
}
You could even write this more concisely with a simple while loop:
int stringCompare(char *pStr1, char *pStr2) {
while (*pStr1 && *pStr1 == *pStr2) {
pStr1++;
pStr2++;
}
return *pStr1 - *pStr2;
}
Of course, both the above functions expect two valid pointers to be passed as arguments. If you also want to allow invalid pointers you should check them before starting the loop (though it does not seem like you want to do that from your code).

Related

Can someone please explain why I'm getting a seg fault error

This code compiles successfully but when I debug it shows a SIGSEV seg fault error. Can someone help please?
char *_strdup(char *str)
{
int i, size = 0;
char *mp;
if (str == NULL)
{
return (NULL);
}
for (; str[size] != '0'; size++)
mp = malloc(size * sizeof(str) + 1);
/* + 1 to get last part of the str */
if (mp == 0)
{
return (NULL);
}
else
{
for (; i < size; i++)
{
mp[i] = str[i];
}
}
return (mp);
}
First, just because it compiles successfully, this does not mean that your code is correct. It just means that syntactically the compiler is fine. I hope you use the maximum warning level and correct your code until all warnings and errors are gone.
You have multiple problems:
You seem to look for the terminating end-of-string marker. But instead of the correct '\0' you typed '0'. This can lead to a much too big size, depending where a zero digit is found. Depending on your system, a segmentation fault is also possible.
sizeof is an operator that yields the size of its argument, in your case the size of a pointer. str is of type char *. Effectively you allocate too much, but this is harmless.
The for loop uses the memory allocation as its body. I'm sure you didn't mean this, but there is no empty statement. So you are allocating multiple memory spaces, which are leaks in the end.
An empty statement is a single semicolon or an empty pair of curly braces.
What you most probably want to achieve is to find the number of characters that str points to. You can get it by calling strlen(str).
i is not initialized, it can have any value. This can lead to a segmentation fault, if it starts with a negative value.
You did not add the end-of-string marker in the duplicate. Depending on the other code we don't see, this can lead to segmentation faults.
This is a possible solution without calling strlen():
char *_strdup(const char *str)
{
int i;
int size;
char *mp;
if (str == NULL)
{
return NULL;
}
for (size = 0; str[size] != '\0'; size++)
{
/* just looking for the end of the string */
}
size++;
/* + 1 for the end-of-string marker */
mp = malloc(size);
if (mp == NULL)
{
return NULL;
}
for (i = 0; i < size; i++)
{
mp[i] = str[i];
}
return mp;
}
I made a bit more:
Use separate variable definitions, it avoid errors and eases maintenance.
return is not a function and needs no parentheses for its expression.
Put the initialization of the index variable where it belongs, in the initializing statement of for. This way everything about this index is at one place.
Consider the end-of-string marker by incrementing size. This eases the following code.
Since sizeof (char) is 1, it can be ommitted at the calculation of the needed memory size.
Compare mp with NULL instead of 0. It is a pointer, and this is C, not C++.
Your variable i has been declared but not initialized so a random number is used in your for(; i < size;
Just add int i = 0, size = 0; at the beginning or change your for statement to for(i = 0; i < size; i++)
This was the reason for your segmentation fault. Some other issues:
As mentioned in comments string termination character is not '0'. It's either 0 or '\0'.
You are calling malloc on each iteration of your for statement. This causes memory leak.Just call it once after you got your string size right. This is fixed by putting a semicolon after the for.
Maybe something like this.
char *_strdup (char *str)
{
int i, size;
char *mp;
if (str == NULL)
{
return (NULL);
}
for (size = 0; str[size] != 0; size++);
mp = malloc (size * sizeof (str) + 1);
/* + 1 to get last part of the str */
if (mp == 0)
{
return (NULL);
}
else
{
for (i = 0; i < size; i++)
{
mp[i] = str[i];
}
}
return (mp);
}

Reversing a string without two loops?

I came up with the following basic item to reverse a string in C:
void reverse(char in[], char out[]) {
int string_length = 0;
for(int i=0; in[i] != '\0'; i++) {
string_length += 1;
}
for(int i=0; i < string_length ; i++) {
out[string_length-i] = in[i];
}
out[string_length+1] = '\0';
}
Is there a way to do this in one for loop or is it necessary to first use a for length to get the string length, and then do a second one to reverse it? Are there other approaches to doing a reverse, or is this the basic one?
Assuming you can't use functions to get the string length and you want to preserve the second loop I'm afraid this is the shortest way.
Just as a side-note though: this code is not very safe as at for(int i=0; in[i] != '\0'; i++) you are not considering cases where the argument passed to parameter in is not a valid C string where there isn't a single \0 in all elements of the array pointed by in and this code will end up manifesting a buffer over-read at the first for loop when it will read beyond in boundaries and a buffer overflow in the second for loop where you can write beyond the boundaries of out. In functions like this you should ask the caller for the length of both arrays in and out and use that as a max index when accessing them both.
As pointed by Rishikesh Raje in comments: you should also change the exit condition in the second for loop from i <= string_length to i < string_length as it will generate another buffer over-read when i == string_length as it will access out by a negative index.
void reverse(char *in, char *out) {
static int index;
index = 0;
if (in == NULL || in[0] == '\0')
{
out[0] = '\0';
return;
}
else
{
reverse(in + 1, out);
out[index + 1] = '\0';
out[index++] = in[0];
}
}
With no loops.
This code is surely not efficient and robust and also won't work for multithreaded programs. Also the OP just asked for an alternative method and the stress was on methods with lesser loops.
Are there other approaches to doing a reverse, or is this the basic one
Also, there was no real need of using static int. This would cause it not to work with multithreaded programs. To get it working correct in those cases:
int reverse(char *in, char *out) {
int index;
if (in == NULL || in[0] == '\0')
{
out[0] = '\0';
return 0;
}
else
{
index = reverse(in + 1, out);
out[index + 1] = '\0';
out[index++] = in[0];
return index;
}
}
You can always tweak two loops into one, more confusing version, by using some kind of condition to determine which phase in the algorithm you are in. Below code is untested, so most likely contains bugs, but you should get the idea...
void reverse(const char *in, char *out) {
if (*in == '\0') {
// handle special case
*out = *in;
return;
}
char *out_begin = out;
char *out_end;
do {
if (out == out_begin) {
// we are still looking for where to start copying from
if (*in != '\0') {
// end of input not reached, just go forward
++in;
++out_end;
continue;
}
// else reached end of input, put terminating NUL to out
*out_end = '\0';
}
// if below line seems confusing, write it out as 3 separate statements.
*(out++) = *(--in);
} while (out != out_end); // end loop when out reaches out_end (which has NUL already)
}
However, this is exactly as many loop iterations so it is not any faster, and it is much less clear code, so don't do this in real code...

How do I replace all occurrences in an array with another array in C

I want to replace all occurrences in an array (string) with another array.
I have a code that:
stores the string in an array in which the replacing is to take place output[],
another array that stores the string to be searched for as replace[] and a third array called toBeReplacedBy and the replacing of the first occurrence works just fine but it skips the other occurrences in the output
for example:
replace[]:
abc
toBeReplacedBy[]:
xyz
output[]:
abcdefabc
becomes
xyzdefabc
but it should become:
xyzdefxyz
I suspect the problem lies with the replacer code :
//the replacer
for (i = 0; i<80; i++) {
if (output[i] == replace[i])
output[i] = toBeReplacedBy[i];
}
//debug purpose
puts("output[]:\n");
puts(output);
return 0;
}
What have I done wrong here and how could I get it to replace all occurrences in the array.
please be aware that I only wish to use stdio.h to do this
thabks in advance
Never iterate further than the array length. This leads to undefined and possibly dangerous behaviour. If you only expect strings, use something like:
int i = 0;
while(output[i] != '\0')
{
// your logic here
i++;
}
Additionally you want to check for concurrent appearances of the same characters. But in your code you only check the first three characters. Everything after that is undefinded behaviour, because you cannot know what replace[3] returns.
Something similar to this could work:
int i = 0;
int j = 0;
int k;
while(output[i] != '\0')
{
if (output[i] == replace[j])
j++;
else
j = 0;
// replace 3 with the array length of the replace[] array
if (j == 3)
{
for(k = i; j >= 0; k-- )
{
output[k] = toBeReplacedBy[j]
j--
}
j = 0;
}
i++;
}
But please check the array boundaries.
edit: Additionally as Nellie states using a debugger would help you to understand what went wrong. Go through your program step by step and look how and when values change.
First advice is to try to debug your program if it does not work.
for (i = 0; i<80; i++) {
if (output[i] == replace[i])
output[i] = toBeReplacedBy[i];
}
There are two problems in this loop.
The first is that are iterating until i is 80. Let's look what happens when i becomes 3. output[3] in case of abcdefabc is d, but what is replace[3]? Your replacement array had only 3 letters, so you have to go back in the replacement array once you finish with one occurrence of it in the original string.
The second is that you check letter by letter.
Say you original array, which you named output somehow was abkdefabc, first three letters do not match your replacement string, but you will check the first two letters they will match with the replacement's first two letters and you will incorrectly change them.
So you need to first check that the whole replacement string is there and only then replace.
You should use strlen() to know length of your array or iterate until you reach the end of a your array ('\0').
'\0' and strlen are only available for array of char.
Your loop should looks like this :
int i = 0;
int len = strlen(my_string);
while (i < len)
{
//logic here
i = i + 1;
}
OR
int i = 0;
while (my_string[i] != '\0')
{
// logic here
i = i + 1;
}

segfault on EOF while loop in c

We've narrowed down the issue to this function. This one's meant to take in a a group of words to be searched for like:
fish
john
miss
nope
that appear immediately after an NxN grid to search, and extend to the end of the file.
I'm attempting to put these words into a 2D array-like structure using pointers, and she's giving me a segmentation fault.
Help?
Here's the code:
int acceptItems(char** items)/*Function reads in 2D array of items to be searched for*/
{
int row = 0;/*row, col keep track of position*/
int col = 0;
int numWords;/*Number of words to be searched for*/
int end = 1;/*1 means continue, 0 means end*/
char c;/*Temporary char for input*/
while(end == 1)
{
c = getchar();
if(c == EOF)/*Case ends repetition at end of file*/
{
end = 0;
}
else if(c == '\n')
{
items[row][col] = '\0';
row++;
col = 0;
}
else
{
items[row][col] = c;
col++;
}
}
numWords = row + 1;
return numWords;
}
Thanks!
Can't be 100% sure since you haven't posted your function call, but your items array is probably too small. You are going out of bounds when you try to set items[row][col].
1) In main(), insure items is declared as pointer, not int.
// char items; (from comment)
char** items; (** may or may not be missing from your comment. #Red Alert)
2) Declare ch as int. getchar() returns 256 different char and EOF. To distinguish these 257 different results, do not use char, but int.
// char c;
int c;
...
c = getchar();
3) Upon detecting EOF, terminate the current string. ( I think this is it. By not terminating this line, using numWords = row + 1 and your last text line not ending with a \n, the terminator is never set when printing last line, which has no \0 leads down to the scary place of UB.)
if(c == EOF)/*Case ends repetition at end of file*/
{
items[row][col] = '\0';
end = 0;
}
4) Add test to insure you are not writing out of bounds. The is the 2nd idea that somewhere code has boldly gone where no code has gone before.
if (row >= 100 || col >= 100) HandleError();
items[row][col] = ...
5) Suggest changing numWords count.
numWords = row;
if (col > 0) numWords++;
If you declare a 2D array outside of function acceptItems, and then pass it as an argument when you call this function, then you need to provide (in the function's declaration) at least the "lower" dimension:
int acceptItems(char items[][COLS])
You can also provide both dimensions, although you don't have to:
int acceptItems(char items[ROWS][COLS])
The general rule for any type of array, is that you have to provide all dimensions except for the "highest":
int func(int arr[][S2][S3][S4][S5])
BTW, function getchar returns an int (in order to allow the end-of-file indication). So you should basically use int c instead of char c (I don't think that you will ever have c == EOF otherwise).

Pointers to string C

trying to write function that returns 1 if every letter in “word” appears in “s”.
for example:

containsLetters1("this_is_a_long_string","gas") returns 1
containsLetters1("this_is_a_longstring","gaz") returns 0
containsLetters1("hello","p") returns 0
Can't understand why its not right:
#include <stdio.h>
#include <string.h>
#define MAX_STRING 100
int containsLetters1(char *s, char *word)
{
int j,i, flag;
long len;
len=strlen(word);
for (i=0; i<=len; i++) {
flag=0;
for (j=0; j<MAX_STRING; j++) {
if (word==s) {
flag=1;
word++;
s++;
break;
}
s++;
}
if (flag==0) {
break;
}
}
return flag;
}
int main() {
char string1[MAX_STRING] , string2[MAX_STRING] ;
printf("Enter 2 strings for containsLetters1\n");
scanf ("%s %s", string1, string2);
printf("Return value from containsLetters1 is: %d\n",containsLetters1(string1,string2));
return 0;
Try these:
for (i=0; i < len; i++)... (use < instead of <=, since otherwise you would take one additional character);
if (word==s) should be if (*word==*s) (you compare characters stored at the pointed locations, not pointers);
Pointer s advances, but it should get back to the start of the word s, after reaching its end, i.e. s -= len after the for (j=...);
s++ after word++ is not needed, you advance the pointer by the same amount, whether or not you found a match;
flag should be initialized with 1 when declared.
Ah, that should be if(*word == *s) you need to use the indirection operator. Also as hackss said, the flag = 0; must be outside the first for() loop.
Unrelated but probably replace scanf with fgets or use scanf with length specifier For example
scanf("%99s",string1)
Things I can see wrong at first glance:
Your loop goes over MAX_STRING, it only needs to go over the length of s.
Your iteration should cover only the length of the string, but indexes start at 0 and not 1. for (i=0; i<=len; i++) is not correct.
You should also compare the contents of the pointer and not the pointers themselves. if(*word == *s)
The pointer advance logic is incorrect. Maybe treating the pointer as an array could simplify your logic.
Another unrelated point: A different algorithm is to hash the characters of string1 to a map, then check each character of the string2 and see if it is present in the map. If all characters are present then return 1 and when you encounter the first one that is not present then return 0. If you are only limited to using ASCII characters a hashing function is very easy. The longer your ASCII strings are the better the performance of the second approach.
Here is a one-liner solution, in keeping with Henry Spencer's Commandment 7 for C Programmers.
#include <string.h>
/*
* Does l contain every character that appears in r?
*
* Note degenerate cases: true if r is an empty string, even if l is empty.
*/
int contains(const char *l, const char *r)
{
return strspn(r, l) == strlen(r);
}
However, the problem statement is not about characters, but about letters. To solve the problem as literally given in the question, we must remove non-letters from the right string. For instance if r is the word error-prone, and l does not contain a hyphen, then the function returns 0, even if l contains every letter in r.
If we are allowed to modify the string r in place, then what we can do is replace every non-letter in the string with one of the letters that it does contain. (If it contains no letters, then we can just turn it into an empty string.)
void nuke_non_letters(char *r)
{
static const char *alpha =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
while (*r) {
size_t letter_span = strspn(r, alpha);
size_t non_letter_span = strcspn(r + letter_span, alpha);
char replace = (letter_span != 0) ? *r : 0;
memset(r + letter_span, replace, non_letter_span);
r += letter_span + non_letter_span;
}
}
This also brings up another flaw: letters can be upper and lower case. If the right string is A, and the left one contains only a lower-case a, then we have failure.
One way to fix it is to filter the characters of both strings through tolower or toupper.
A third problem is that a letter is more than just the 26 letters of the English alphabet. A modern program should work with wide characters and recognize all Unicode letters as such so that it works in any language.
By the time we deal with all that, we may well surpass the length of some of the other answers.
Extending the idea in Rajiv's answer, you might build the character map incrementally, as in containsLetters2() below.
The containsLetters1() function is a simple brute force implementation using the standard string functions. If there are N characters in the string (haystack) and M in the word (needle), it has a worst-case performance of O(N*M) when the characters of the word being looked for only appear at the very end of the searched string. The strchr(needle, needle[i]) >= &needle[i] test is an optimization if there are likely to be repeated characters in the needle; if there won't be any repeats, it is a pessimization (but it can be removed and the code still works fine).
The containsLetters2() function searches through the string (haystack) at most once and searches through the word (needle) at most once, for a worst case performance of O(N+M).
#include <assert.h>
#include <stdio.h>
#include <string.h>
static int containsLetters1(char const *haystack, char const *needle)
{
for (int i = 0; needle[i] != '\0'; i++)
{
if (strchr(needle, needle[i]) >= &needle[i] &&
strchr(haystack, needle[i]) == 0)
return 0;
}
return 1;
}
static int containsLetters2(char const *haystack, char const *needle)
{
char map[256] = { 0 };
size_t j = 0;
for (int i = 0; needle[i] != '\0'; i++)
{
unsigned char c_needle = needle[i];
if (map[c_needle] == 0)
{
/* We don't know whether needle[i] is in the haystack yet */
unsigned char c_stack;
do
{
c_stack = haystack[j++];
if (c_stack == 0)
return 0;
map[c_stack] = 1;
} while (c_stack != c_needle);
}
}
return 1;
}
int main(void)
{
assert(containsLetters1("this_is_a_long_string","gagahats") == 1);
assert(containsLetters1("this_is_a_longstring","gaz") == 0);
assert(containsLetters1("hello","p") == 0);
assert(containsLetters2("this_is_a_long_string","gagahats") == 1);
assert(containsLetters2("this_is_a_longstring","gaz") == 0);
assert(containsLetters2("hello","p") == 0);
}
Since you can see the entire scope of the testing, this is not anything like thoroughly tested, but I believe it should work fine, regardless of how many repeats there are in the needle.

Resources