Using strstr with 2d array - c

I'm programming in C. I have loaded a 2d array with words. It is called dictionary[][]. I am trying to use strstr to find out how many of the words have a substring in them of "struct". Here is the code. In my function count_matches I try to iterate through dictionary using the strstr function to compare. It always returns null so I never get to ++matchcount. Any ideas why?
Here is the function. inp is "struct" dictionary is the 2d array and n is the number of lines in the array (I.e. how many words are in the array).
int count_matches(char *inp, char dictionary[MAX_LINES][MAX_LEN], int n)
{
int matchcount = 0;
int i;
for (i=0; i < n; ++i)
{
if (strstr(dictionary[i], inp) !=NULL)
{
++matchcount;
}
}
return matchcount;
}

I don't see your problem in the given code snippet.
Think about the following:
Did you mix up MAX_LINES and MAX_LEN?
Is the inp really equal to "struct"?
Did you load the dictionary with at least one word that has the substring "struct"?
Show the code where you make the call to this function if the problem persists.
For example, this works fine:
int main()
{
char dictionary[MAX_LINES][MAX_LEN] = {"hello", "hihello", "bye", "Hello"};
int output = count_matches("hello", dictionary, 4);
printf("%d", output); // Output is 2 as expected
return 0;
}
By the way, you can also consider making the following changes to your code:
MAX_WORDS is more readable than MAX_LINES
You do not necessarily have to write != NULL inside that condition

Related

(C) Can't read string array values between functions

I have one char strings pointers array, lets call it "str_array".
I also have 3 strings:
1."Hello"
2."World"
3."Today"
(Every string ends with \0)
And I have this function, that receives our str_array, the size of it, and another pointers arr that is not releveant for my question.
The problem that I encounter, is that function "scanString" receives NULL, or garbage values, instead of the strings inside str_array.
unsigned int RemoveFromStrArray(char*** str_array, unsigned int str_array_size, char** ptr_to_chars_array)
{
int k = 0;
while (k != str_array_size)
{
// This is the part where im trying to scan str_array strings.
scanString(*str_array[k]);
str_array++;
k++;
}
}
int scanString(char* string)
{
int c = 0;
int counter = 0;
while (string[c] != '\0')
{
if (string[c] == 1)
{
moveOneBack(string, c);
c--;
counter++;
}
c++;
}
return c;
}
I've been trying multiple alternative ways to scan str_array string arrays.
But all of my times I just had to deal with garbage values or NULL strings.
How do I reach str_array strings, that would be passed by reference to scanString?
Picture of what I'm talking about:
BIG THANKS IN ADVANCE!
unsigned int RemoveFromStrArray(char*** str_array
You've got at least one too many indirections there. From your drawing, str_array refers to an array, so it's effectively a pointer, and the values in the array are pointers to char. You don't seem to be changing the array itself, e.g. you're not making str_array refer to some other array of strings, so there's no need to pass the address of the array. So char ** is closer to the type you want.
str_array++;
I'm not sure why you're incrementing str_array and using k as an index into the array. Although it's safe to modify parameters locally like that, it's nice to avoid it so that you can refer to them while debugging. Consider this:
unsigned int RemoveFromStrArray(char** str_array,
unsigned int str_array_size)
{
int k = 0;
while (k != str_array_size)
{
char *string = str_array[k];
scanString(string);
k++;
}
}
That is, the values in str_array are of type char *, so str_array[k] has that type. Copying it into a temporary variable string makes it a little easier to see what's going on. (I removed the last parameter, ptr_to_chars_array, to keep things simple and because it's not used. You'll add it back if you have plans for it, I'm sure.)
A for loop would be a little more compact but otherwise equivalent:
unsigned int RemoveFromStrArray(char** str_array,
unsigned int str_array_size)
{
for (int k = 0; k < str_array_size; k++)
{
char *string = str_array[k];
scanString(string);
}
}

C - how to use strcpy for a 2 dimensional array?

I'm trying to build a 2 dimensional array by using str.cpy, but the program fails. The code receives 4 arrays add copies their content to their matching arrays.
int InsertStudent(char *firstName, char* lastName, char* dynCourses, char *dynGrades,
char firstNames[50][20],
char familyNames[50][20], char courses[50][5][20],
char grades[50][5])
{
int set,
int cset = 0;
for (set = 0; set <= (50); set++)
{
if (firstNames[set][cset] == '\0')
{
strcpy(firstNames[set][cset], firstName);
strcpy(familyNames[set], lastName);
for (cset = 0; cset <= 5; cset++)
{
strcpy(courses[set], dynCourses);
strcpy(grades[set], dynGrades);
}
}
}
return 0;
}
Well clearly the error is using strcpy wrongly.
The correct way would be
strcpy(firstNames[set], firstName);
Also in the loop it should be
for (cset = 0; cset < MAX_COURSES; cset++)
{
strcpy(courses[cset], dynCourses);
strcpy(grades[cset], dynGrades);
}
Note that the idiomatic C loop is for (int i = 0; i < MAX; i++), using < and not <=.
The signature of the strcpy function is
char *strcpy(char * restrict s1, const char * restrict s2);
Earlier you passed in place of s1 a char instead of char*. You must have got some warning (if enabled). If not then turn all compiler flags -Wall -Werror.
if (firstNames[set][cset] == '\0')
But if you are initially checking an uninitilized value with \0. This will higly unlikely will turn out to be false. There is no gurantee that the char array which doesn't contain strings will be initialized with 0 automatically. So make sure you have initialized the char arrays in the callee function like this
char arr[20][50]={0};
The loop is from 0 to MAX_STUDENTS. You are invoking an undefined behavior looping on array index out of bound if MAX_STUDENTS is greater than or equal to 50. Same goes for MAX_COURSES. More clearly the looping would be for (set = 0; set < (50); set++).
Again there will be a lot better way to deal with it if you put the initialization and copy part seperate. Otherwise this will be uneasy to maintain.
Seeing your use courses it is obvious that you want to declare it like this
char courses[5][20];
same goes for the grades array. Here you were trying to copy a string into into a 2d array. Compiler would complain about type incompatibility.
Also in the function you didn't return anything meaningful. The correct way would be to return the index value on which new name or information is added.
Here you are copying content of dynGrades and dynCourses to the array. So they will all contain the same values. Is this what you want? Because then what's the use of keeping 5 seperate char arrays - one could serve the purpose pretty well.

Passing a non-empty string to snprintf causes an unrelated char* array to change addresses

I'm working on the exercises in K&R's book, and I've run into a weird bug while trying to extend 04-06 to allow for variables with string names. Truthfully, I've actually managed to fix the bug (pretty simple - explained below), but I'd like to know why the error was occuring in the first place.
For those unfamiliar with the problem, you're basically asked to create a command-line calculator (using Polish notation) that can store and recall variables with character names.
Here's the relevant code where the issue occurs:
#define MAXOPLEN 1000
int varCount = 1;
char **keys;
char **values;
// changing the declaration to:
// char strOps[][STROPCOUNT] = { ... };
// fixed the issue
char *strOps[STROPCOUNT] = { "dupe", "swap", "del", "print",
"clr", "sin", "cos", "tan",
"exp", "pow", "ln", "log",
"mem", "re"};
main() {
keys = malloc(varCount * sizeof(char[MAXOPLEN]));
keys[0] = "ans";
values = malloc(varCount * sizeof(char[MAXOPLEN]));
values[0] = "0.0";
... // Other stuff related to the program
}
// flag is unrelated to the problem I'm asking about. It just checks to see
// if the variable name used to store value n is 'ans', which is where
// the last returned value is stored automatically
void memorize(char s[], double n, bool flag) {
... // small conditional block for flag
for (i = 0; i < varCount; i++) {
if (equals(keys[i], s)) {
found = True;
// Next line is where the program actually breaks
snprintf(values[i], MAXOPLEN, "%f", n);
break;
}
}
if (!found) {
i = varCount;
varCount++;
keys = realloc(keys, varCount * sizeof(char*));
keys[i] = malloc(sizeof(char[MAXOPLEN]));
keys[i] = s;
values = realloc(values, varCount * sizeof(char*));
values[i] = malloc(sizeof(char[MAXOPLEN]));
snprintf(values[i], MAXOPLEN, "%f", n);
}
}
After compiling and running, the first time you enter in an equation to calculate, everything seems to run smoothly. However, while debugging, I found out that the first three char* in strOps were oddly made to point to different addresses. When trying to save the return value of the equation to "ans", it enters the for-loop in memorize() that tries to see if string s had been used as a key name already. It correctly finds keys[0] to point to a string matching s's value ("ans"), then attempts to convert double n to a string and save it in values[0].
While inside the snprintf() function, the first three char* in strOps are made to point elsewhere inside this method in corecrt_stdio_config.h:
_Check_return_ _Ret_notnull_
__declspec(noinline) __inline unsigned __int64* __CRTDECL __local_stdio_printf_options(void)
{
// Error occurs after this next line:
static unsigned __int64 _OptionsStorage;
return &_OptionsStorage;
}
As commented in the code above, making strOps a 2D array of characters (rather than an array of char pointers) fixed the issue. This makes sense because arrays of characters can't have the values of individual characters changed, but what I don't understand is why the that method in corecrt_stdio_config.h was changing the values of those three pointers in the first place.
Thanks!
Your initializations are incorrect and are causing the change:
keys[0] = "ans";
values[0] = "0.0";
Both "ans" and "0.0" are string literals and cannot be used to initialize the arrays, you need to use strcpy after you allocate.
strcpy (keys, "ans");
strcpy (values, "0.0");
Your other option is to assign one character at a time:
size_t i;
char *p = "ans";
for (i = 0; i < strlen (p); i++)
keys[i] = p[i]; /* copy to keys */
p[i] = 0; /* nul-terminate */
note: these are examples of your errors, you do the same thing throughout your code.

c char ** causing allocation error

I am passing a multidimensional character array to a function in c. The main() or whatever function determines the size (dimensions) of the input array. I am trying to determine the length and width of the array dynamically in my function. So far, I have been able to determine the width dynamically for the first row. However, if I try to use the same logic to determine the length, I am getting access violation error. This is the code I have so far:
char * myfunc( char* in[] )
{
int index=0, cols=0;
while( in[0][index++] != '\0' );
cols = index - 1;
:
:
}
int main()
{
char *in[3] = {"abcd","efgh","ijkl"};
char *out = myfunc( in );
:
:
}
Luckily for me, 'cols' in all 'rows' of the **in are equal. I understand a more robust approach will be to first determine the number of rows and test the number of cols for each row there after. I am causing an access violation error every time I try to do something similar for the rows.
I should mention, I cannot redefine the function to pass the dimensions of the array. I should also mention, this is not homework or work-work, I came across this problem while brushing my c coding skills. Is there a way to determine the number of rows in the case above without causing an access violation error?
With the data you have, it can't be done. But you could add a sentinel like this:
char *in[4] = {"abcd", "efgh", "ijkl", NULL};
and then look for the sentinel (which is effectively what you're doing to count the columns, by looking for the sentinel '\0' in the first string).
char * myfunc( char* in[] )
{
int rows;
for(rows = 0; in[rows]; ++rows)
;
printf("%d\n", rows);
.....
.....
}
int main()
{
char *in[] = {"abcd",, "efgh","ijkl", NULL};
char *out = myfunc( in );
}
It's worthwhile detecting columns only if all strings are of the same length.

How to pass an array to a method in C and edit the contents?

Basically I have this working code that manipulates an array of strings:
for (i = 0; i < numentries; i++)
if (strcmp(compare_str, strings[i]) < 0)
break;
for (j = numentries; j > i; j--)
strcpy(strings[j], strings[j - 1]);
strcpy(strings[i], compare_str);
strcat(strings[i], " ");
strcat(strings[i], whole_str);
numentries++;
I want to make it so I can call to a method to do the manipulation like:
//call to method
compare(strings, numentries, compare_str, whole_str);
numentries++;
//method
void compare(char array[], int entries, char compare[], char whole[]) {
int i, j;
for (i = 0; i < entries; i++)
if (strcmp(compare, array[i]) < 0)
break;
for (j = entries; j > i; j--)
strcpy(array[j], array[j - 1]);
strcpy(array[i], compare);
strcat(array[i], " ");
strcat(array[i], whole);
}
The above code doesn't work, I know you have to use pointers some how much im not exactly sure how. How do I pass my array of characters (strings) so I can manipulate the values inside the array?
I know this is only a code snippet, but I thought it would be enough to solve my problem. if you need more of my program to help me let me know.
It crashes at the line
strcat(array[i], " ");
with the message
Program received signal SIGSEGV, Segmentation fault. 0x75bf8df6 in strcat () from C:\Windows\system32\msvcrt.dll n Single stepping until exit from function strcat, which has no line number information. 0x77776299 in ntdll!LdrGetDllHandleByName () from C:\Windows\system32\ntdll.dll
void compare(char *array[], int entries, char compare[], char whole[]) {
^
Proof: http://ideone.com/6xSaq
I would just use a double pointer (pointer to a pointer). In fact I would just use pointers instead of arrays. Eventually they all boil down to the same thing anyway.
void compare(char **array, int entries, char *compare, char *whole) {
I don't think any of the body needs to be modified.
EDIT:
Also the segmentation fault you are seeing in your code is probably occuring because of insufficient storage space. If you're using malloc for allocation use realloc to allocate enough space for the string concatenation. If you're using stack storage, then allocate enough space for every element of the array, incase you need to perform concatenations.
Given the declaration char strings[30][161];, I think you need the function to be
void compare(char array[][161], int entries, char compare[], char whole[])
because you are not passing an array of pointers to chars, you are passing an 2D array of chars. C needs to know the inner dimension of 2D arrays to do indexing correctly. See this article for details
The alternative to hardcoding sizes would be to add something like this before the function call:
char* stringPtrs[N_STRINGS];
for (i=0;i<N_STRINGS;i++) { stringPtrs[i]=strings[i]; }
compare(stringPtrs, entries, ...);

Resources