i am kind of new in c and i am trying to figure things out.
my question is, what i need to have in the place of '/0' , in order for it to skip
the "empty" cells?
i know i could do it the easy way and just have have all the .anoxi values in the condition, but i was just curious.
i have tried putting "", which gives me all the names (doesn't skip any of them) , '' which gives me "[Error] empty character constant" and
null, which gives me "[Error] 'null' was not declared in this scope"
struct t {
char anoxi[10];
char name[10];
char gramma [2];
}
int main() {
struct t array[5][12];
int r;
strcpy(array[4][1].anoxi, "+-1%");
strcpy(array[4][2].anoxi, "+-2%");
strcpy(array[4][5].anoxi, "+-0.5%");
strcpy(array[4][6].anoxi, "+-0.25%");
strcpy(array[4][7].anoxi, "+-1%");
strcpy(array[4][8].anoxi, "+-0.05%");
strcpy(array[0][0].gramma, "M");
strcpy(array[0][1].gramma, "K");
strcpy(array[0][2].gramma, "N");
strcpy(array[0][3].gramma, "O");
strcpy(array[0][4].gramma, "I");
strcpy(array[0][5].gramma, "R");
strcpy(array[0][6].gramma, "L");
strcpy(array[0][7].gramma, "V");
strcpy(array[0][8].gramma, "G");
strcpy(array[0][9].gramma, "A");
strcpy(array[0][10].gramma, "X");
strcpy(array[0][11].gramma, "S");
strcpy(array[1][0].name, "Black");
strcpy(array[1][1].name, "Brown");
strcpy(array[1][2].name, "Red");
strcpy(array[1][3].name, "Orange");
strcpy(array[1][4].name, "Yellow");
strcpy(array[1][5].name, "Green");
strcpy(array[1][6].name, "Blue");
strcpy(array[1][7].name, "Purple");
strcpy(array[1][8].name, "Grey");
strcpy(array[1][9].name, "White");
strcpy(array[1][10].name, "Gold");
strcpy(array[1][11].name, "Silver");
for (r=0; r<12; r++) {
if (array[4][r].anoxi!= '\0') {
printf("%s = %s\n",array[0][r].gramma, array[1][r].name);
}
}
return(0);
}
C has no concept of "empty". Variables in C represent actual physical memory locations, and they contain whatever that memory contains, which is either what they were initialized to contain, or some random value if they were never initialized (note that statics are initialized by default).
This doesn't prevent you from choosing to interpret one of the possible values of a variable as "empty", but that would be your choice, and entirely up to you. You would then have to initialize your variable/array with that value and check for it. Character variables often use the value '\0' for this, which should work for you--just make sure you take care of the difference between single characters and arrays: for example, ... if...gramma[0] == '\0'' ...
There is what you might consider an exception to this: one of the values pointer variables are allowed to take is a value called NULL, which is guaranteed not to point to anything. This is often used to initialize pointer variables but you still have to do the initialization and checking yourself.
Now that you got, it you may want to read my Structs (C), which offers a compact example, that might come in handy*.
First of all, allow me to question the validity of this code. Consider this equivalent example I made:
#include <stdio.h>
struct t {
char anoxi[10];
char name[10];
char gramma [2];
};
int main(void) {
struct t my_array[5][1];
if(my_array[0][0].anoxi != '\0')
printf("%s\n", my_array[0][0].anoxi);
return 0;
}
It will print, in my machine:
gsamaras#gsamaras-A15:~$ gcc -Wall px.c
gsamaras#gsamaras-A15:~$ ./a.out
����
Why?
Because, the memory the array holds is not initialized to anything, so its value is undefined, which invokes UNDEFINED BEHAVIOR!
We could fix this, by initializing every string, like this:
struct t my_array[5][1];
my_array[0][0].anoxi[0] = '\0';
if(my_array[0][0].anoxi[0] != '\0')
printf("edw %s\n", my_array[0][0].anoxi);
Or, as Mike suggested, you could use memset(), like this:
memset (my_array, 0, sizeof (my_array);
Usually we set the value of a variable to a predefined value, which for us, humans tells that this cell/string/whatever is empty.
c does not know that, unless we tell our program to keep an eye out of empty "things". We have to inform our program what is an empty "thing", especially how to identify it!
Here, you have a string and you check array[4][r].anoxi!= '\0', which is always true because the left-hand side is an array, which decays to pointer in this expression, as M.M said.
*I am not writing it here, since the answer is already too long
First of all, initialize the array to blank:
struct t array[5][12] = { 0 };
This means that any members you have not yet assigned contents to will have value 0 (converted to the type of that member). This is so that later on you can see if the member has been assigned something else by checking to see if it is still 0 or not.
Then you can check:
if ( array[4][r].anoxi[0] ) {
// ^^^^
Note that you must check anoxi[0] which is a char object. Checking anoxi, which is an array object, merely checks that the array exists in memory (which it tautologically does), not whether the contents of the array are some particular value.
NB. The != '\0' is redundant, I think it is clearer to omit it but you could use it if you want.
Related
The user specifies the number of lines in the output in the arguments (as the size of the page in pagination), by pressing the key he gets the next lines. How it works now:
Let's say the user chose to receive 1 row at a time:
first string
first string
second string
first string
second string
third string
struct result {
char part[32768];
int is_end_of_file;
};
struct result readLines(int count) {
int lines_readed = 0;
struct result r;
if (count == 0) {
count = -1;
}
while (count != lines_readed) {
while (1) {
char sym[1];
sym[0] = (char) fgetc(file);
if (feof(file)) {
r.is_end_of_file = 1;
return r;
}
strcat(r.part, sym);
if (*"\n" == sym[0]) {
break;
}
}
lines_readed++;
}
return r;
}
int main(int argc, char *argv[]) {
file = fopen(argv[1], "r");
while (1) {
struct result res = readLines(atoi(argv[2]));
printf("%s", res.part);
if (res.is_end_of_file) {
printf("\nEnd of file!\n");
break;
}
getc(stdin);
}
closeFile();
return 0;
}
I know that when I define a struct in the readLines function, it is already filled with previous data. Forgive me if this is a dumb question, I'm a complete newbie to C.
I'm not sure what is the question here, however I'll do my best to address what I understand. I assume the problem lies somewhere around the "previous data" you mentioned in the title and in the comments to the question.
Let's first set an example program:
#include <stdio.h>
struct result {
char part[10];
};
int main (int argc, char *argv[]) {
struct result r;
printf(r.part);
return 0;
}
The variable r has a block scope, so it has automatic storage duration. Since it has automatic storage duration, and no initializer is provided, it is initialized to an indeterminate value (as mentioned by UnholySheep and n. 1.8e9-where's-my-share m. in the comments to the question). I don't yet get all the C intricacies, but based on this, I guess you cannot rely on what the value of r will be.
Now, in the comments to the question you try to understand how is it possible that you can access some data that was not written by the current invokation of your program. I cannot tell you exactly how is that possible, but I suspect it is rather platform-specific than C-specific. Maybe the following will help you:
What is Indeterminate value?
What happens to memory after free()?
Why memory isn't zero out from malloc?
Going further, in the line
printf(r.part);
first we try to access a member part of r, and then we call printf with the value of this member. Accessing a variable of an indeterminate value results in undefined behavior, according to this. So, in general, you cannot rely also on anything that happens after invoking r.part (it doesn't mean there is no way of knowing what will happen).
There is also another problem with this code. printf's first parameter is interpreted as having the type const char *, according to man 3 printf, but there is provided a variable that has the type struct result. Indeed, there is produced the following warning when the code is compiled with gcc with the option -Wformat-security:
warning: format not a string literal and no format arguments [-Wformat-security]
Unfortunately, I don't know C well enough to tell you what precisely is happening when you do such type mismatch in a function call. But as we know that there already happened undefined behavior in the code, this seems less important.
As a side note, a correct invokation of printf could be in this case:
printf("%p", (void *)r.part);
r.part is a pointer, therefore I use the %p conversion specifier, and cast the value to (void *).
Hee guys,
I have been reading a couple of things about pointers and pointees and started getting curious. The only thing I dont understand is how pointers behave in functions, hence the following code:
#include <stdio.h>
int pointeeChanger(char* writeLocation) {
writeLocation = "something";
return 0;
}
int main(void)
{
char crypted[] = "nothing";
char* cryptedPointer = crypted;
pointeeChanger(cryptedPointer);
printf("The new value is: %s", cryptedPointer);
return 0;
}
What my intention to do is to adjust the pointee, "crypted" var, through a pointer given to a function. The only thing is that it is not working. Could you please explain me what is going wrong in my thought process. I am fairly new to C so my errors could be fairly basic.
Thanks in advance!
Greetings,
Kipt Scriddy
C strings are not the best material to learn pointers, because they are implemented as pointers to char. Let's use int instead:
#include <stdio.h>
void pointeeChanger(int* writeLocation) {
// Using dereference operator "*"
*writeLocation = 42; // something
}
int main(void) {
int crypted = 0; // Nothing
pointeeChanger(&cryptedPointer); // Taking an address with "&"
printf("The new value is: %d", crypted);
return 0;
}
This works as expected.
Modifying strings in place is a lot harder, because you are forced to deal with memory management issues. Specifically, the string into which you copy must have enough space allocated to fit the new string. This wouldn't work with "nothing" and "something", because the replacement is longer by two characters.
Short answer: writeLocation is a local variable and is a copy of cryptedPointer. When you modify writeLocation, cryptedPointer is not modified.
If you want to modify cryptedPointer, you have to pass a pointer to it, like so:
#include <stdio.h>
int pointeeChanger(char** writeLocation) { /* Note: char** */
*writeLocation = "something"; /* Note: *writeLocation */
return 0;
}
int main(void)
{
char crypted[] = "nothing";
char* cryptedPointer = crypted;
pointeeChanger(&cryptedPointer); /* Note: &cryptedPointer */
printf("The new value is: %s", cryptedPointer);
return 0;
}
There are other issues with this code though. After the call to pointeeChanger(), cryptedPointer no longer points to the crypted array. I suspect you actually wanted to change the contents of that array. This code fails to do that.
To change the value of crypted[] you will need to use strcpy() or (preferably) strncpy(). Also you will need to watch the size of the crypted[] array - "something" is longer than "nothing" and will cause a buffer overflow unless crypted[] is made larger.
This code will modify the original crypted[] array:
#include <stdio.h>
#include <string.h>
#define MAX_STR_LEN 64
/*
* Only char* required because we are not modifying the
* original pointer passed in - we are modifying what it
* points to.
*/
int pointeeChanger(char* writeLocation)
{
/*
* In C, you need to use a function like strcpy or strncpy to overwrite a
* string with another string. Prefer strncpy because it allows you to
* specify a maximum size to copy, which helps to prevent buffer overruns.
*/
strncpy(writeLocation, "something", MAX_STR_LEN);
/* strncpy doesn't automatically add a \0 */
writeLocation[MAX_STR_LEN] = '\0';
return 0;
}
int main(void)
{
/*
* The +1 is because an extra character is required for the
* null terminator ('\0')
*/
char crypted[MAX_STR_LEN + 1] = "nothing";
pointeeChanger(crypted);
printf("The new value is: %s", crypted);
return 0;
}
It depends slightly on what you actually want to do:
Do you want to change what cryptedPointer is pointing to, or change the content that cryptedPointer is pointing at?
The second can be done by:
strcpy(writeLocation, "something");
Beware that if something is longer than what the original string's size, you'll overflow the buffer, which is a bad thing. So to fix this, you'd have to have char crypted[10] = "nothing";, to make space for the string "something".
You can clearly also do something like:
writeLocation[2] = 'f';
writeLocation[3] = 'f';
and have the printf print "noffing"
but if you want to do the first variant, then you need to pass a pointer to the pointer:
int pointeeChanger(char** writeLocation) {
*writeLocation = "something";
return 0;
}
And then call:
pointeeChanger(&cryptedPointer);
Note that when this returns, cruptedPointer is pointing at a constant string that can't be modified, where your original crypted can be modified.
Consider that Tom is hired by Sally to break knuckles for the mafia.
Pass-by-value: If Sally tells Tom to count the number of knuckles he breaks at work today, then Sally has no way of knowing which number Tom has come up with until he returns from the road. They both have a copy of the number "zero" in their heads to begin with, but Tom's number might increase throughout the course of the day.
Note the word "copy". When you pass-by-value to a function, you're passing a copy of the object. When you modify the object within a function, you're modifying the copy instead of the original.
Pass-by-reference: If Sally tells Tom to tally the number of knuckles he breaks in the sky, then she (and anyone else who's interested) can refer to the sky. By changing the sky, Tom would also be changing Sally's number.
edit: C doesn't have pass-by-reference, though it does have pointers, which are reference types. Passing a pointer is still pass-by-value, and a copy with the same pointer value is still formed. Hence, your assignment is to the copy, not the original.
gcc 4.4.4 c89
I will be adding to this list. So I am looking to NULL terminate it. However, I am getting a warning: "Initialization makes integer from pointer without a cast"
Why does the it mention integer when I haven't used in that structure?
I was thinking in this case arrays and pointers where the same?
struct company_prof
{
char name[32];
size_t age;
char position[32];
} employee[] = {
{ "Job Bloggs", 23, "software engineer" },
{ "Lisa Low" , 34, "Telecomms Technician" },
{ "simon smith", 38, "personal assistist" },
{ NULL , -1, NULL }
};
Many thanks for any suggestions,
You are attempting to initialize a character array with NULL. This does not make any sense. For example, you will get the same warning from
char a[100] = { NULL };
and it makes no sense in exactly the same way.
The "integer" that is mentioned in the diagnostic message is the first element of the character array. char is an integer type in C and when you write
char a[100] = { NULL };
it is an attempt to initialize 0-th element of the array a with NULL. On your platform, NULL is declared as something with pointer type, which is why the diagnostic message is saying that you are attempting to make an integer (a[0]) from a pointer (NULL) without a cast.
An even simpler example might look as follows
char c = NULL;
and it will earn you the same diagnostic message for the very same reasons.
May I ask why you are attempting to initialize a char with NULL? What was your intent?
If you don't intend to write into the name and position arrays after initialization, maybe you should use pointers instead of arrays as in
struct company_prof
{
const char *name;
size_t age;
const char *position;
} employee[] = {
{ "Job Bloggs", 23, "software engineer" },
{ "Lisa Low" , 34, "Telecomms Technician" },
{ "simon smith", 38, "personal assistist" },
{ NULL , -1, NULL }
};
Formally, in this case NULL makes perfect sense. But not in case of array.
But less formally the purpose of that { NULL, -1, NULL } record at the end of the array is not clear to me though. Is is a terminating element of some kind? Why don't you just use the exact size of the array instead of creating a terminating element?
In this case, you're statically allocating the space for the strings in the structure. The space for the strings aren't really a separate entity from the structure, it's just a block of space with in it. So you can't assign a pointer to those fields because they can't refer to anything else -- they're just convenient names for referring to the beginning of those chunks within the structure.
The reason you get the bit about the conversion is that characters (e.g., name[0]) are small integers, and it's trying to stuff the NULL pointer into the first character of each string. That first character is too small to hold a pointer, so it's giving you a warning about that.
You probably want to use "\0" instead of NULL here. That's an empty string or null string which is a bit different from a null pointer.
Edit: An alternative that you might consider for getting the size of the list is not using a sentinel value, but defining a constant that calculates it from the sizes in bytes. A typical construction for that looks like:
static const int num_employees = sizeof(employee) / sizeof(employee[0]);
You're using char[32] as your struct members.
You can't convert from NULL to a char[N]. So whats happening is that NULL is being treated as zero. The warning is coming from the compiler deciding to convert the NULL to a 0.
As the initialisation of the char[N] is occuring inside an array initialising it to 0 is valid, it just sets all the values to zero. e.g.
struct wrapper { char i[2] } w = { 0 }
will set the internal i to {0,0}
This means your memory layout looks like this.
|==============name============||--||==========position===========|
0123456789012345678901234567890101230123456789012345678901245678901
Job.Bloggs0000000000000000000000(23)software.engineer00000000000000 // first
... // rest
00000000000000000000000000000000(-1)0000000000000000000000000000000 // sentinel
This may or may not be what you want .. but it does mean that looping over thises values will not be what you may expect: In particular this will not work:
p = employee;
while( p.name != NULL )
{
printf("%s", p.name);
++p;
};
It will just run off printing garbage from memory and eventually segfault.
You might try something like this instead
p = employee;
while( p.name[0] != 0 )
{
printf("%s", p.name);
++p;
};
But that wont work for names like "\0foo" (Which you might consider invalid anyway...)
char sXSongBuffer[20][30];
sXSongBuffer = {"Thriller", "Don't Stop Till You Get Enough", "Billy Jean"};
Why does this return the error expected expression before ‘{’ token? The reason I want to initialize my array like this is so that I can change its contents like this later:
sXSongBuffer = {"New Song", "More Music From Me"};
You can't assign to arrays in C. C allows initializing arrays with values that are compile-time constants. If you want to change the values later, or set values that are not compile-time constants, you must assign to a particular index of the array manually.
So, your assignment to sXSongBuffer is disallowed in C. Moreover, since sXSongBuffer[0] to sXSongBuffer[19] are arrays too, you can't even say: sXSongBuffer[0] = "New Song";
Depending upon what you want, this may work for you:
/* declare sXSongBuffer as an array of pointers */
char *sXSongBuffer[30] = {
"Thriller",
"Don't Stop Till You Get Enough",
"Billy Jean",
NULL /* set the rest of the elements to NULL */
};
size_t i;
/* and then later in your code */
sXSongBuffer[0] = "New Song";
sXSongBuffer[1] = "More Music From Me";
for (i=2; i < sizeof sXSongBuffer; ++i)
sXSongBuffer[i] = NULL;
But the above only works if you know all your strings at compile time. If not, you will have to decide if you want "big-enough" arrays, or if you need dynamic memory for the strings and/or the number of strings. In both cases, you will want to use an equivalent of strcpy() to copy your strings.
Edit: To respond to the comment:
You're declaring an array of 30 char pointers with the first three elements pointing to buffers the size of the strings, ie the buff pointed to by sXSongBuffer[0] won't hold any string larger than "Thriller" and if he does sXSongBuffer[0] = malloc(32); He'll get a minor memory leek. Also, he'll have to malloc memory for each of the rest of the slots in the array. He should either use 2d char arrays like in the OP + a designated init, or malloc each buffer at run time and copy in the values. He'll also need to remember to free any memory he mallocs.
sXSongBuffer in char *sXSongBuffer[30]; is an array of size 30, with each element being a char *, a pointer to char. When I do:
char *sXSongBuffer[30];
each of those 30 pointers is uninitialized. When I do:
char *sXSongBuffer[30] = { "Thriller", ... };
I set the pointers to different read-only locations. There is nothing preventing me to then "re-point" the pointers somewhere else. It is as if I had:
char *data = "Hello";
printf("%s\n", data);
data = "Hello, world";
printf("%s\n", data);
In the above snippet, I assign data to "Hello" first, and then change it to point to a longer string later. The code I had above in my answer did nothing more than reassign sXSongBuffer[i] to something else later, and since sXSongBuffer[i] is a pointer, the assignment is OK. In particular, sXSongBuffer[0] is a char *, and can point to any valid location that has a char in it.
As I said later in my answer, if the strings aren't known at compile-time, this scheme doesn't work, and one has to either use arrays with "big enough" sizes, or dynamically allocate memory that's big enough.
C does not have general-purpose array literals. The {} list syntax only works when initializing, i.e. when assigning the value in the same statement that declares the variable.
You cannot just write
char sXSongBuffer[20][30];
sXSongBuffer = {"Thriller", "Don't Stop Till You Get Enough", "Billy Jean"};
You must either initialize array at once (but it will containt only 3 items):
char * sXSongBuffer[]= {"Thriller", "Don't Stop Till You Get Enough", "Billy Jean"};
Or either use stnrcpy on every item:
char sXSongBuffer[20][30];
strncpy(sXSongBuffer[0],"Thriller",29);
strncpy(sXSongBuffer[1],"Don't Stop Till You Get Enough",29);
strncpy(sXSongBuffer[2],"Billy Jean",29);
Take a look at Designated Initializers.
#include <stdio.h>
int main (void) {
char a[6][6] = { [2] = "foo", [4] = "bar" };
for (int i=0; i<6; ++i)
printf("%d == %s\n", i, a[i]);
return 0;
}
This is a c99 feature. Compile with:
gcc -W -std=c99 2dInit.c -o 2dInit
This outputs:
0 ==
1 ==
2 == foo
3 ==
4 == bar
5 ==
In your case you want to do:
char sXSongBuffer[20][30] = {
[0] = "Thriller",
[1] = "Don't Stop Till You Get Enough",
[2] = "Billy Jean"
};
/*
* PURPOSE
* Search if a string contains a string and print it out from there
*/
#include <stdio.h>
void searchHaystack(char cHaystack[], char cNeedle[]);
void showResult(int iOffset, char cHaystack[]);
int main() {
// Declarations
char cHaystack[50], cNeedle[50];
// Input
puts("Haystack:");
gets(cHaystack);
puts("Needle:");
gets(cNeedle);
// Call searcher
searchHaystack(cHaystack, cNeedle);
return 0;
}
void searchHaystack(char cHaystack[], char cNeedle[]) {
// Declarations
int iCntr, iCntr2, iFoundOffset;
// Search the haystack for the first letter of the needle
for (iCntr == 0; iCntr < 50 && cHaystack[iCntr] != '\0'; iCntr++) {
if (cHaystack[iCntr] == cNeedle[0]) {
iFoundOffset = iCntr;
for (iCntr2 == 1; iCntr2 < 50 && (cHaystack[iCntr+iCntr2] == cNeedle[iCntr2] || cNeedle[iCntr2] == '\0'); iCntr2++) {
if (cNeedle[iCntr2] == '\0') {
showResult(iFoundOffset, cHaystack);
}
}
}
}
}
void showResult(int iOffset, char cHaystack[]) {
int iCntr;
// Print the substring char by char
for (iCntr == iOffset; iCntr < 50 && cHaystack[iCntr] != '\0'; iCntr++) {
printf("%c", cHaystack[iCntr]);
}
printf("\n");
}
Looking at my debugger I noticed that cHaystack[] and cNeedle[] aren't passed to searchHaystack properly as only the first char is conserved. How do I fix this? I haven't learned about pointers yet.
Also, I'm getting this warning on all three for loops:
statement with no effect
What's up with that?
Actually, the entire array IS being passed, the debugger only shows the first char by default because in C, the system does not know the size of an array. It is something the program has to keep track of. Since you are using strings though, which are typically null terminated, try setting the watch variable "(char*)cHaystack" (without quotes) and see what the debugger shows then.
Also, assignment statements should have one = sign, not the double == sign. So:
for (iCntr = 0; ...
Should be used, NOT:
for (iCntr == 0; ...
Same with the other for loops.
You're starting the loop with iCntr == 0
This is a comparison, so it does not set iCntr to zero.
Use iCntr = 0 (a single equals sign)
The values are passed properly, but your expectation of how your debugger should display them is incorrect. As already mentioned, there is no string type in C. Instead, C uses char* variables -- pointers to characters; your char[] are equivalent to char*.
You know that the pointed-to character is the first character in a longer string, but the debugger doesn't. It displays the character that the pointer points to -- which you know to be the first of a longer string. The debugger, however, only knows it's a char*, and there must be a char to be pointed at, so it displays that char.
These are character arrays not strings. In C string are of type char * and you have to allocate the memory for them.
Of course when you say varname[5] that is the same as saying *(varname+5)
Basically you are going to have to learn about pointers to use strings in C.
EDIT
As pointed out below (and above by me) you can use character arrays like strings in C. HOWEVER, my point is that if you don't learn a little bit about pointers you are going to be in big trouble.
For example:
Not being able to view the string in the debugger.
Not putting a null as the last character in the array and having crazy random bugs
Forgetting that you only allocated X bytes for the array and going over the end
etc.
If you don't understand how pointers work in C, it is really hard -- if not impossible to work with the language.
I expect the prof will cover it next week.
Arrays are not first-class objects in C; when you pass an array as a function parameter, the type of the array expression is implicitly converted from "N-element array of T" to "pointer to T", and its value is set to point to the first element in the array[1].
In the context of a function parameter declaration, int a[] is the same as int *a (but this is true only in the context of a function parameter declaration); your searchHaystack function receives two pointers to char, which correspond to the first elements of the respective arrays. The debugger doesn't show you the whole array, because in the context of the function they are not arrays.
Also, NEVER, NEVER, NEVER USEgets(). Ever. It will introduce a point of failure in your code. Use fgets() instead. C does no bounds checking on arrays. If you call gets() for a buffer sized to hold 10 characters, and the user types in 100 characters, gets() will happily store those extra 90 characters in the memory following your buffer, potentially clobbering something important and leading to a crash or worse (buffer overruns are a common exploit for malware; the Morris worm exploited a call to gets() in sendmail).
The warning is coming from you using == instead of = to assign your loop counters.
The exceptions to this rule are when the array expression is an operand of either the sizeof or address-of (&) operators, or when the array is a string literal being used to initialize another array in a declaration.
The warning is probably caused by
iCntr == 0,iCntr2 == 1, iCntr == iOffset
I guess you were going, in fact, for:
iCntr = 0,iCntr2 = 1, iCntr = iOffset
As for passing the arrays, you could do something like ( using pointers ):
void searchHaystack(char* cHaystack, int cHaystackSize, char* cNeedle, int cNeedleSize )
...
for (iCntr = 0; iCntr < cHaystackSize && cHaystack[iCntr] != '\0'; ++iCntr )