search of string in an array of strings - c

i wrote some code that is supposed to find the location of a given string in an array of strings.
problem is- it doesn't give the location. it gives something else.
i understand that probably the problem has to do with the differences between the pointers that are involved- a previous version that dealt with finding the position of a letter in a word worked well.
after a lot of attempts to figure out where is the bug, i ask your help.
kindly, explain me what should be done.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int what (char * token);
main()
{
int i=0;
char string[]="jsr";
char *token;
token=&string[0];
i=what(token);
printf(" location of input is %d \n", i);
return 0;
}
int what (char * token)
{
int i=1;
char *typtbl[]={"mov",
"cmp",
"add",
"sub",
"not",
"clr",
"lea",
};
char * ptr;
ptr=(char *)typtbl;
while (!(strcmp(ptr,token)==0))
{
ptr=(char *)(typtbl+i);
i++;
}
return i;
}

As pointed out, you did not design function what properly. What value should it return if your search function go through all the pointers but does not find the desired string? Typically in that case return -1 would be a choice to indicate nothing found. Also in this case, using a for loop would probably be more suitable, you can just return the index immediately instead of going through all pointers.
int what(char *token)
{
char *typtbl[] = {
"mov",
"cmp",
"add",
"sub",
"not",
"clr",
"lea",
};
for( size_t i = 0; i < sizeof(typtbl)/sizeof(char*); ++i )
{
char *ptr = typtbl[i];
if(strcmp(ptr, token) == 0)
{
return i; // found something
}
}
return -1; // found nothing
}

A cleaner working version.
Main issue is in the (char *)(typtbl+i) replaced by typtbl[i] in the following code. typtbl+i is equivalent to &typtbl[i], so if my memory is good, it's a pointer on the pointer of the string and not the pointer of string itself
I added a NULL at the end of the array to be able to stop if the string is not present and return -1 to clearly say it was not found.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int what(char *token);
int main()
{
int i = 0;
char string[] = "jsr";
i = what(string);
printf(" location of input is %d \n", i);
return 0;
}
int what(char *token)
{
char *typtbl[] = {
"mov",
"cmp",
"add",
"jsr",
"not",
"clr",
"lea",
NULL
};
int i = 0;
while(typtbl[i] && !(strcmp(typtbl[i], token) == 0)) {
++i;
}
if(!typtbl[i])
i = -1;
return i;
}
char *token; token=&string[0]; was useless because string == &string[0].

A few things:
Your main function is missing its return type.
The while loop in what doesn't stop when the element isn't found. Therefore you are reading out of bounds.
This should do the work w/o pointer arithmetic.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int what (char * token);
int main(){
int i=0;
char string[]="jsr";
char *token;
token=&string[0];
i=what(token);
printf(" location of input is %d \n", i);
return 0;
}
int what (char * token){
unsigned int i=0;
char *typtbl[]={"mov",
"cmp",
"add",
"sub",
"not",
"clr",
"lea",
};
unsigned int typtbl_x_size = sizeof(typtbl)/sizeof(typtbl[0]);
char * ptr;
ptr=typtbl[i];
while (!(strcmp(ptr,token)==0)){
i += 1;
if (i >= typtbl_x_size){
printf("element not in list\n");
return -1;
}
ptr=typtbl[i];
}
return i;
}

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.

How to pass 2d array of string to the function and print value of it?

Why it is not working... It should be working, right? gcc have problem with this line, but why?
render_history(history, 2);
Sorry for bothering. I am just a beginner.
#include <stdio.h>
void render_history(char** history, const int entry);
int main()
{
char* history[3][4];
history[0][0] = "1234";
history[1][0] = "5678";
history[2][0] = "9012";
render_history(history, 2); //??
return 0;
}
void render_history(char** history, const int entry)
{
// print "9012"
}
gcc have problem with this line, but why?
Because the type is wrong. char* history[3][4]; can't be passed as char**. They are incompatible types.
Try something like:
#include <stdio.h>
void render_history(char* (*history)[4] , const int entry)
{
printf("%s\n", history[entry][0]);
}
int main()
{
char* history[3][4];
history[0][0] = "1234";
history[1][0] = "5678";
history[2][0] = "9012";
render_history(history, 2);
return 0;
}
As mentioned above double pointer not equal to 2D array.
You can also use pointer to pointer of char. char **history. And with this you have several option:
1) Use compound literals
#include <stdio.h>
void render_history(const char **history, const int entry)
{
printf("%s\n", history[entry]);
}
int main(void)
{
const char **history = (const char *[]) { "1234", "5678", "9012", NULL};
render_history(history, 2);
return 0;
}
If you need change your data later
2) Use dynamic memory allocation with malloc
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void render_history(char **history, const int entry)
{
printf("%s\n", history[entry]);
}
int main(void)
{
char **history = malloc(3 * sizeof(char *));
for (int i = 0; i < 3; ++i)
{
history[i] = malloc(4 * sizeof(char));
}
strcpy(history[0], "1234");
strcpy(history[1], "5678");
strcpy(history[2], "9012");
history[3] = NULL;
render_history(history, 2);
return 0;
}
If you use 2nd option dont forget free memory after use.

Error with the array returning through function

I need to read a word from main function and convert the characters in UCASE if the first character is LCASE and vice versa using the user defined function.I tried ways for returning the array from function but still I am lacking some core ideas. Please debug this program and explain the way it works.
#include <stdio.h>
#include <string.h>
int* low (char str)
{
int i;
for (i=1; i<strlen(str);i++)
{
if(str[i]<91)
{
str[i]=str[i]+32;
}
else
{
}
}
return &str;
}
int* high (char str[50])
{
int i;
for (i=0; i<strlen(str);i++)
{
if(str[i]>91)
{
str[i]=str[i]-32;
}
else
{
}
}
return &str;
}
void main()
{
char str[50];
char* strl;
printf("Enter any string....\n");
scanf("%s",str);
if (str[0]<91)
{
*strl=low(str);
}
else
{
*strl=high(str);
}
printf("Converted string is %s.",*strl);
}
There is already a problem here:
So if you are saying this code is perfect and you want us to debug it and explain how (on earth) this works, then here you go.
In function int* low (char str), you have if(str[i]<91). Thats a problem right there. str is a char received as an argument, and hence str[i] is a straight compile-time error.
Another one to deal with is the return statement.
You have a statement:
return &str;
which would return the address of str, which by the way is a char, whereas function low is supposed to return a pointer to an int.
The same is applicable to high function as well.
Suggestion: Leave aside this bad code and get a beginner level C programming book first. Read it and the try some codes out of it.
A few inputs for improvement: (Which you may not comprehend)
change
void main()
to
int main(void)
Why? Refer this legendary post: What should main() return in C and C++?
Secondly, int both functions you are using strlen() in loop which will always return a fixed value. So, instead of
for (i=0; i<strlen(str);i++)
I'd suggest,
size_t strlength = strlen(str);
for (i=0; i < strlength; i++)
You can try the code and method as below:
#include <stdio.h>
#include <string.h>
char* caseConverter (char *str)
{
int i;
for (i=0; i<strlen(str);i++)
{
if(str[i]>=65 && str[i]<=90)
{
str[i]=str[i]+32; //To lower case
}
else if((str[i]>=97 && str[i]<=122))
{
str[i]=str[i]-32; //To upper case
}
else
printf("%c is not an alphabet \n",str[i]);
}
return str;
}
void main()
{
char inputStr[50]= "Stubborn";
char* opStr= caseConverter(inputStr);
printf("Converted string is %s",opStr);
}

Segmentation Fault in C

My code is giving me a segmentation fault and I can't seem to find what I'm doing wrong:
#include <stdio.h>
#include <string.h>
char find(char name[], char allNames[][10], int length)
{
int i=0;
for (i = 0; i < length; i++) {
if (strcmp(allNames[i],name) == 1) {
printf("%i",i);
return *name;
}
}
return -1;
}
main(){
char allNames[][10] = {"cat","dog","frog","log","bog"};
char name[] = "log";
int length=5;
printf("%s",find(name,allNames,length));
}
I'm really keen to understand all the mechanisms happening here and what I'm doing wrong for tomorrows exam. Thanks for your help!
EDIT: Really Appreciate the answers and information guys! I'm really quite new to C and just getting used to what every thing means. The particular exam question I am looking at is :
(a) The following function is intended to find the string name in the array
allNames. If found, it returns the position of name in the array. If not
found, it returns -1. Modify the code so that it works correctly.
int find(char name[], char allNames[][10])
{
for (i = 0; i < 10; i++) {
if (allNames[i] == name) {
return name;
}
}
return -1;
}
And I'm trying to get a program to work within these parameters. Cheers :)
http://coliru.stacked-crooked.com/a/d400c9a56d732446
#include <stdio.h>
#include <string.h>
char* find(char name[], char allNames[][10], int length)
{
int i=0;
for (i = 0; i < length; i++) {
if (!strcmp(allNames[i],name)) {
printf("%i",i);
return name;
}
}
return NULL;
}
int main(){
char allNames[][10] = {"cat","dog","frog","log","bog"};
char name[] = "log";
int length=5;
printf("%s",find(name,allNames,length));
}
Returning a single char will do you no good if you're trying to return a string. I would also suggest that you return a NULL if you cannot find the string.
Also, include the int before main; this is better style.
The direct reason for your Segmentation Fault here is because the code tried to print the char type with %s(which needs an address value).
void main()
{
char c = 'a';
printf("%s", c); // will cause Segmentation fault here
}
Back to your code, that is
char find(char name[], char allNames[][10], int length)//return char
printf("%s",find(name,allNames,length));
The minimal change to make it work as follows,
1) To return char*
char* find(char name[], char allNames[][10], int length)//return char*
{
int i=0;
for (i = 0; i < length; i++) {
if (strcmp(allNames[i],name) == 0) { // here should 0
printf("%i",i);
return name; // change name* to name
}
}
return NULL; // change to NULL
}
//to print
printf("%s",find(name,allNames,length));
2) to return position value
int find(char name[], char allNames[][10])
{
for (i = 0; i < 10; i++) {
if (allNames[i] == name) {
return i; // here, change to return i
}
}
return -1;
}
//then, you can print like this
printf("find at position: %d",find(name,allNames,length));
//or to print string by
int pos = find(name,allNames,length);
if(pos >= 0)
printf("find the string: %s",allNames[pos]);
This code is wrong on several levels.
gcc -Wall -Wextra reveals:
meh.c:15:1: warning: return type defaults to ‘int’ [-Wreturn-type]
main(){
^
meh.c: In function ‘main’:
meh.c:19:3: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
printf("%s",find(name,allNames,length));
^
meh.c:21:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
What's up with that? Do you compile with warnings enabled?
I am ignoring the lack of indentation.
#include <stdio.h>
#include <string.h>
char find(char name[], char allNames[][10], int length)
What? How about: char *name, **list, int size)
{
int i=0;
Why set it to 0 here?
for (i = 0; i < length; i++) {
if (strcmp(allNames[i],name) == 1) {
printf("%i",i);
return *name;
Have you read strcmp's manpage? It returns ZERO when a string matches, so this code makes no sense.
*name is of type char, but you don't want to return a char. You want to return a pointer, no?
}
}
return -1;
Well, given that you feed that into %s in printf, what do you expect to hapen here? Normally one would return NULL.
}
main(){
This is obsolete syntax, I don't know where you stole it from. Use 'int main(void)'.
char allNames[][10] = {"cat","dog","frog","log","bog"};
Normally people just return such arrays with a NULL pointer, so that these can be iterated over and there is no need to pass anything about the size.
char name[] = "log";
Why not char *name = "log".
int length=5;
Incorrect. It hardcodes the amount of stored strings in allNames table.
printf("%s",find(name,allNames,length));
}

Right Justified Zero filled String in C

I want to right justify a string value with zero filled on left hand side. I have written following code but it prints white space instead of 0.
#include<stdio.h>
int main()
{
char s[4]="PJ";
printf("%04s",s);
}
Output: " PJ"
I need output as "00PJ".
You can do something like this :
#define MIN_LEN 4
if (strlen(s) < MIN_LEN) {
printf("%0*d%s", MIN_LEN-(int)strlen(s), 0, s);
}
else {
printf("%s", s);
}
Don't forget to include <string.h>
Edit :
To explain our discussion about buffer overflow, just try this piece of code :
int main()
{
struct
{
char s[4];
int i;
} test;
test.i = 0x12345678;
strcpy(test.s,"PJHA");
printf("Output =%s\nTest =%x",test.s,test.i);
}
Output :
Output =PJHA
Test =12345600
If you change the size to 5, the code is corrected and the stack following your string is not corrupted.
Here is the short line code answer for my question:-
This will take care of any length of input variable like s = "J", s="JH", s="JHA", s="PJHA"
and corresponding output will be "000J", "00JH", "0JHA", "PJHA" .
#include<stdio.h>
#include<string.h>
int main()
{
char s[4],s2[4];
strcpy(s,"JH");
sprintf(s2,"%04s",s);
memset(s2,'0',4-(int)strlen(s));
printf("Output =%s\n",s2);
}
Output =00JH
Appreciate the above simpler solution while giving an alternative more manual one:
#include<stdio.h>
#include<string.h>
void print(char *s, int ncount)
{
if(s == NULL) return;
int len = strlen(s);
if(len > ncount) printf("%s", s);
else {
for(int i = 0; i < ncount - len; ++i)
printf("0");
printf("%s", s);
}
}
int main()
{
char s[4]="PJ";
print(s, 4);
return 0;
}
#include <stdio.h>
#include <string.h>
int main(){
char s[5]="PJ";
char padding[sizeof(s)] = {0};
int width = sizeof(padding)-1;
memset(padding, '0', width);
width -= strlen(s);
//printf("%.*s%s\n", (int)(4-strlen(s)), "0000", s);
printf("%.*s%s\n", width, padding, s);
return 0;
}

Resources