C: passing arrays to another method properly - c

/*
* 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 )

Related

How to put strings from a file into an array in c

So I have this code:
char inte[10];
while(j<noinput) {
fscanf(circuit,"%s",inte);
vararray[count]=inte;
count++;
j++;
}
However when I print the contents of the array like this:
for (h=0;h<noinput+2;h++){
printf("%dth variable: %s\n",h,vararray[h]);
}
The elements past the first two (which are reserved for special elements) are all equal to the LAST string that I had taken in from fscanf earlier. I have no idea how one of the strings from fscanf could be equal to multiple slots in the array when I am only setting
vararray[count]=inte;
Shouldn't this mean that each element of the array will be different since I am incrementing count every time? I am so confused. I also tried doing:
fscanf(circuit,"%s",vararray[count]);
But this also did not work and gave me null elements for certain indexes.
you are doing something too wrong. By "vararray[count]=inte;" you are doing pointer assignment so all of your vararray is getting filled by same string. I am guessing you are new to C so I will answer due to that. Correct way would look something like below
Fixed size solution:
char vararray[ROWCOUNT][BUFFERSIZE];
for(count=0; j<noinput; ++count, ++j) {
fscanf(circuit,"%s",(char*)vararray[count]);
}
With dynamic memory management
char * vararray[ROWCOUNT];
for(count=0; j<noinput; ++count, ++j) {
vararray[count] = (char*)malloc(BUFSIZE);
fscanf(circuit,"%s", vararray[count]);
}
I want to warn you in the way of becoming an expert on C nowadays is somewhat madness , i mean unless you have another choice. Examples below I put and the thing you wrote are completely unsafe and unsecure...
You're not copying the string. Here's what's happening:
char *vararray[462]; // declare an array of string pointers
char inte[10]; // declare a char array (to function as a string)
for (int i = 0; i < 462; i += 1) {
// do something
vararray[i] = inte;
}
This is causing all of the items of vararray to point to the memory also referred to as inte... but you're overwriting that each time! Instead, do something like this:
#include <string.h> // write me at the top, outside main()!
char vararray[462][10]; // declare an array of strings (max length 9)
char inte[10]; // declare a char array (to function as a string)
for (int i = 0; i < 462; i += 1) {
fscanf(circuit,"%10s",inte); // use buffer size to make xscanf safer
strncpy(vararray[i], inte, 9); // copy inte, making sure not to buffer overflow!
vararray[i][9] = '\0'; // if inte is too big, a null byte won't be added to the end of the string; make sure that it's there
}
This copies the string! Your problem should go away when you do this.

what is the value of an empty cell of a 2d array?

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.

Need to change "a" to "e" or "o" depending if "a" position is even or odd using pointers

Need "a"="e" if position is even and "a"="o" if position is odd. I'm new at pointers the program itself is easy to make put i don't understand how pointers work yet.
Heres the pointer.
void word(char *w[100])
{
int n=0;
while(*w[n]=='/0' && n<*w[n]){
if(*w[n]=='a' && n%2==0)
*w[n]='o';
else if(*w[n]=='a' && n%2!=0)
*w[n]='e';
}
n++;
}
When I did it without a pointer the program worked.
Heres the rest of the program.
void main(void)
{
char pr[100];
puts("Choose a word with letter a in it 'a' ");
scanf("%s", &pr);
word(pr);
printf("The changed word is %s", pr);
return 0;
}
How can I make the pointer to work and where did I make a mistake?
When you pass an array to a function, it actually passes a pointer to the first element. If the original array is <type> <name>[<size>], the corresponding type of the argument is <type>*, not <type>*[<size>]. char *w[100] means an array of 100 pointers to characters, not an array of 100 characters as pr is declared in main().
A pointer can be accessed using array notation: p[i] is equivalent to *(p+i). You don't use both array indexing and indirection (unless the array is an array of pointers, and you want to access what each element points to).
You also had your loop test wrong. You should loop as long as the current character of the string is not null, not while it is null, since the null character is at the end of the string. And the n < *w[n] test makes absolutely no sense at all.
So the function should be:
void word(char *w)
{
int n=0;
while(w[n] != '\0'){
if(w[n]=='a' && n%2==0) {
w[n]='o';
} else if(w[n]=='a' && n%2!=0) {
w[n]='e';
}
n++;
}
}
You can optionally declare a pointer argument using array notation, e.g.
void word(char w[])
But this is considered equivalent, it's still really a pointer that's passed. You can put an array size in there, but it's not used for anything.

Count elements of an unknown type array

Hello i am trying to write a function which return the number of elements of the array passed as parameter, the function have to work on an array of any type.
I tried this:
int nb_elems(void* array)
{
void* end = array;
while(*end != NULL) // The last element have to be null, it is not counted.
end++;
return end - array;
}
As you can guess, it doesn't work.
In fact it doesn't even compile, i get these errors:
Error: Illegal indirection. < while(*end != NULL) >
Error: void * : size unknown. < while(*end != NULL) >
Error: void * : size unknown. < return end - array >
First, could you tell me if what i am trying to do is possible?
Second, is the way i am trying to achieve this makes sense, or am i completely missing the point?
Then, why do i get these errors, what does they means?
Thanks for your help!
As C does not pass arrays, nb_elems(void* array) received a pointer. The void *pointer does not know how many elements (nor the type) which its points to.
Since it needs to "work on an array of any type", code needs to define how to compare an arbitrary type with NULL.
To your function, you need to pass the pointer, the size of an array element and a function to use to test against NULL.
int nb_elems(void* array, size_t esize, int (*cmp)(void *ptr)) {
char *ptr = array;
int count = 0;
while (*cmp(ptr) == 0) {
ptr += esize;
count++;
}
return count;
}
because you are checking != NULL i suppose you have an array of pointers. with array of pointer your code could work because you know the size of pointers even if it is a void pointer.
if my assumptions were right you could code it like this:
int nb_elems(void** array) {
void** end = array;
while(*end != NULL) {
end++;
}
return end - array;
}
but this code only works with pointer to pointer or array of pointer.
usage:
int** iptrs = (int**)malloc(3 * sizeof(int*));
iptrs[0] = (int*)malloc(sizeof(int));
*(iptrs[0]) = 42;
iptrs[1] = (int*)malloc(sizeof(int));
*(iptrs[1]) = 23;
iptrs[2] = NULL;
printf("%d", nb_elems(iptrs));
the example prints 2
No, the way your are doing it is not possible, because to do pointer arithmetic (here the ++ and the -) the compiler has to know the size of the base type of the array.
Also void* is a pointer to void, that is to a "non-type". So *end has type void and you can't compare it to anything, in particular not to NULL, which could be a pointer type or an integer. Just use 0 if you compare a base type for being zero.
Before starting to even try to do this for pointers of any type, you should try the same for pointers to a known type, say unsigned or double. If you change your code with that in mind, it has good chances to work.
The reason your code doesn't compile, is because of the fact that the compiler cannot determine the size of a void. Take for example the ++ operator. We could roughly translate this to:
end = end + sizeof(void);
The size of a void is not defined, so the compiler cannot generate any code for this, giving you one error.
Next you try to dereference a void pointer. This would give you something with the type void. But since a void does not represent anything (it represents nothing), the compiler cannot tell what this should be. You then use the equality operator to compare nothing to NULL. You cannot compare nothing to something, so this will generate an error.
In C, it is impossible to implement a function that would do something like this, simply because of the fact that you cannot tell what the type of the void* will be. You at least need to know the type your passed to your function and what type-specific value will be used to terminate the array. C does not offer this functionality, making the implementation of such a function impossible.
If you have an actual array, not a pointer, you can use sizeof(array) / sizeof(array[0]) to determine the number of elements in it.

Why can't I initialize an array of cstrings like this?

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"
};

Resources