testing for string-equality without the string library - c

I have code written that I want to see if two char* 'strings' are equal. I have written print statements in the code to help me debug it.
Basically, the commands array is: [a, b, null]
and the cmd the user input can be anything, but if it is a, b, or null then we want to get inside the if statement.
for (i = 0; i < 3; i++){
printf("cmd.name = : %s\nCommand = %s\n", cmd->name, commands[i]);
if (cmd->name == commands[i]){
printf("inside if\n");
valid = 1;
}
}
Its printing out:
cmd.name = : a
Command = a
cmd.name = : a
Command = b
cmd.name = : a
Command = (null)
So in the first instance a should be equal to a right? Why is it not going inside the if statement?

Since you can't use the string library, you will have to implement your own strcmp function.
A char*, as a type, is only a pointer, i.e. it stores an address. Then the simple '==' will compare addresses, which is not what you want.
A simple strcmp function can be:
int myStrcmp(const char *str1, int size1, const char *str2, int size2)
{
int i = 0;
if(size1 != size2)
return -1 //different strings
for(i = 0; i < size1; i++)
{
if(str1[i] != str2[i])
return -1; //different strings
}
return 0; //same strings
}
The trick here is that you have to know somehow what are the string sizes. If you can't use the string library, I imagine you can't use strlen() as well.

The char* represent a pointer to the first character of a string, so by testing cmd->name == commands[i] you test if name has the same address as commands[i]. To compare two strings you can use the strcmp function of the standard library (string.h) and if the function return 0 your two strings are equal.
Edit : If you can't use the string library then write a simple loop. During your loop, if you find a character different between the two strings you return a wrong value (by example 0) and if you can go to the end of your strings without finding differents characters then you return a true value (by example 1)

Related

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

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).

c array comparison trouble

im writing a program that looks if an entered string a palindrome is
im using c, not c++
i wrote a function to do this, the string that has been entered gets put into an array called data in the main function.
int palindroom(char data) {
length = sizeof(data); //getting the length of the word
for (i = 0; i < length; i++){
j = length-1-i; //inverting the string
resstr[i] = data[j]; //
}
if (data = resstr)
return (1); //returning result
else return (0); }
im getting the c2109 and E0142 error in visual studio but i dont really get what im doeing wrong.
(might be good to know that i just started learning c at school so im a bit new to c)
You are passing a character not a word. You need to pass the character array to pass the word.
To know the length of a null terminated string you would use strlen() function.
To reverse a string you can use strrev but you can do simpler things to know if a string is palindrome or not.
data = resstr is an assignment not comparison. And even if it was == the comparison wouldn't do what you expect. To compare strimgs you need to use strcmp().
A simpler version of what you want to do:-
int pal(char *s)
{
size_t len = strlen(s);
char *rs = s + len - 1;
while (s < rs){
if (*rs != *s)
return 0;
s++;
rs--;
}
return 1;
}
The logic is in case of palindrome the string reads same forward an dbackward.
For example
ABCBA and reverse of it is same. What we do is
ABCBA
| |
s rs <--- same
ABCBA
| |
s rs <--- same
For a nonpalindrome the check would be somethign like this:-
ABCDA
| |
s rs <-- same
ABCDA
| |
s rs <-- not same // return 0
To help you a bit regarding passing char array.
suppose you read a string like this :-
char s[50]; // you will check 49 letter words atmost;
scanf("%49s", s);
if( pal(s) ){
puts("found a palindrome");
}
else {
puts("Non-palindrome");
}
There is a series of problem with your code.
int palindroom(char data)
Here data expects a single character but you pass a pointer to char to it.
length = sizeof(data);
The function you need is not sizeof() but strlen().
if (data = resstr)
Here, first of all '=' is an assignment operator while the operator for comparison is '=='. But, '==' will also not work for strings. You need strcmp() for that.
Reference :
https://www.tutorialspoint.com/c_standard_library/c_function_strcmp.htm
https://www.tutorialspoint.com/c_standard_library/c_function_strlen.htm
Code
int palindroom(char * data) {
length = strlen(data); //getting the length of the word
for (i = 0; i < length; i++){
j = length-1-i; //inverting the string
resstr[i] = data[j]; //
}
if (strcmp(resstr, data) == 0)
return (1); //returning result
else return (0);
}

First Not Repeating Character Code

Here is the question:
Write a solution that only iterates over the string once and uses O(1) additional memory, since this is what you would be asked to do during a real interview.
Given a string s, find and return the first instance of a non-repeating character in it. If there is no such character, return '_'.
And here is my code:
char firstNotRepeatingCharacter(char * s) {
int count;
for (int i=0;i<strlen(s);i++){
count=0;
char temp=s[i];
s[i]="_";
char *find= strchr(s,temp);
s[i]=temp;
if (find!=NULL) count++;
else return s[i];
}
if (count!=0) return '_';
}
I dont know what's wrong but when given an input:
s: "abcdefghijklmnopqrstuvwxyziflskecznslkjfabe"
the output is for my code is "g" instead of "d".
I thought the code should have escaped the loop and return "d" soon as "d" was found.
Thx in advance!!!
In your program, problem is in this statement-
s[i]="_";
You are assigning a string to a character type variable s[i]. Change it to -
s[i]='_';
At the bottom of your firstNotRepeatingCharacter() function, the return statement is under the if condition and compiler must be giving a warning for this as the function is supposed to return a char. Moreover, count variable is not needed. You could do something like:
char firstNotRepeatingCharacter(char * s) {
for (int i=0;i<strlen(s);i++){
char temp=s[i];
s[i]='_';
char *find= strchr(s,temp);
s[i]=temp;
if (find==NULL)
return s[i];
}
return '_';
}
But this code is using strchr inside the loop which iterates over the string so, this is not the exact solution of your problem as you have a condition that - the program should iterates over the string once only. You need to reconsider the solution for the problem.
May you use recursion to achieve your goal, something like - iterate the string using recursion and, somehow, identify the repetitive characters and while the stack winding up identify the first instance of a non-repeating character in the string. It's implementation -
#include <stdio.h>
int ascii_arr[256] = {0};
char firstNotRepeatingCharacter(char * s) {
char result = '-';
if (*s == '\0')
return result;
ascii_arr[*s] += 1;
result = firstNotRepeatingCharacter(s+1);
if (ascii_arr[*s] == 1)
result = *s;
return result;
}
int main()
{
char a[] = "abcdefghijklmnopqrstuvwxyziflskecznslkjfabe";
printf ("First non repeating character: %c\n", firstNotRepeatingCharacter(a));
return 0;
}
In the above code, firstNotRepeatingCharacter() function iterates over the string only once using recursion and during winding up of the stack it identifies the first non-repetitive character. I am using a global int array ascii_arr of length 256 to keep the track of non-repetitive character.
Java Solution:
Time Complexity: O(n)
Space Complexity: with constant space as it will only use more 26 elements array to maintain count of chars in the input
Using Java inbuilt utilities : but for inbuilt utilities time complexity is more than O(n)
char solution(String s) {
char[] c = s.toCharArray();
for (int i = 0; i < s.length(); i++) {
if (s.indexOf(c[i]) == s.lastIndexOf(c[i]))
return c[i];
}
return '_';
}
Using simple arrays. O(n)
char solution(String s) {
// maintain count of the chars in a constant space
int[] base = new int[26];
// convert string to char array
char[] input = s.toCharArray();
// linear loop to get count of all
for(int i=0; i< input.length; i++){
int index = input[i] - 'a';
base[index]++;
}
// just find first element in the input that is not repeated.
for(int j=0; j<input.length; j++){
int inputIndex = input[j]-'a';
if(base[inputIndex]==1){
System.out.println(j);
return input[j];
}
}
return '_';
}

Why is both my input array and output array corrupted when I try to implement a function to reverse the array?

I'm Programming in C, using Linux GCC Compiler. I'm very much new to programming.
I'm confused as to why my in[] char array would be changed at all in the function. Doesn't the code simply count the amount of subscripts in in[] and then copy its contents into out[] but backwards? How is it being changed in the function?
/*reverse in to out*/
void reverse(char in[], char out[]) {
int i, l,b;
b = i = l = 0;
while (in[i] != '\0')
++i;
for(l=i;l > 0; l--) {
in[l] = out[b];
++b;
}
return;
}
Your assignment statement is backwards.
in[l] = out[b];
Means "Assign the value in array out at index b to array in index l". This line should instead be
out[b] = in[l];
And BTW, you don't need an empty return statement, you can simply omit this in a void function.
for(l=i-1;l >= 0; l--) {
out[b] = in[l];
++b;
}
out[b] = '\0';
Four problems fixed: start at i-1, test for l>=0, reverse the assignment, and terminate out with a null character.
Also, a good idea is to use const when a function argument will not be changed. In this case, const char in[] would let you spot the assignment error because the compiler would give you a compile time error.
You can use strlen :
for(l=strlen(in), b=0; l>=0; l--)
out[b++] = in[l];
// ensure null terminated so strlen out works
out[b] = '\0';
This assumes out is wide enough for in AND in is null terminated

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