c string array comparison - c

Working in C, I'm filling an array with char* return values from a function
char* files[4][12];
int i = 0;
for (;;)
{
char* file = get_value();
strcpy(files[i],file);
i++;
if (i > 4 || external_condition)
break;
}
// When I break out of
// my for loop the following
// code executes
for (i = 0; i < 5; i++)
{
if (files[i] != NULL)
manipulate(files[i]);
}
My problem is that if I break out of the first for loop without assigning values to all elements of files, my comparison in the second for loop fails. If only files[0] and files[1] have content, the loop processes files[2],file[3] and files[4] anyway.

files is declared as an "array of arrays of pointers to char". Or if you prefer, as a two-dimensional array of pointers to char.
So files[i] is of type "array of pointers to char" but you use it as just a "pointer to char". That is wrong.
That said, it is not clear what you want to do... maybe just:
char files[5][13];
will make more sense. 13 because you likely need 13 char strings (8.3 are 8+3+1=12 plus 1 for the ending NUL), and you seem to use 5 of them. Then initialize them to zero:
memset(files, 0, sizeof(files));
And use the check:
if (files[i][0])
to check if a text is initialized.

char* files[4][12] is a 2D array of char *, not char. Perhaps you meant your code to be as follows... I suggest you listen to what others have said. I'm merely posting a shortened version that still works.
char files[5][12] = { { 0 } };
int i = 0;
do {
strcpy(files[i], get_value());
} while (++i <= 4 && !external_condition);
while (i) {
manipulate(files[--i]);
}

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.

New Arrays and Pointers

I am having a bit of trouble because I am new to C and am not even sure if what I want to do is possible.
I am passing an array called args into a function. Within the function I am also creating a 2D array called arrayOfArgs. What I want to do is to put certain values from args into specific locations in the 2D array called arrayOfArgs.
This is my code so far:
int do_command(char **args){
//this is usually a changing variable depending on the situation, but I've hard coded it to make sense
int commands = 3;
char **arrayOfArgs[commands][10];
//counts which column in arrayOfArgs we are on
int commandNum = 0;
//Counts which part of a command we are on
int count = 0;
//Array Counters
int i = 0;
int j;
//Go through args until we reach the end
while (args[i] != NULL){
if(!strcmp(args[i], "|")){
arrayOfArgs[commandNum][count] = args[i];
count++;
}
else if (strcmp(args[i], "|")) {
count = 0;
commandNum++;
}
//Looking at the next value in args
i++;
}
I'm running into problems because the only thing being put into arrayOfArgs is gibberish. I'm sure I've done something wrong with either the way the arrays are pointing, the way arrayOfArgs was created, or both.
OR is it even possible to go from a 1D to a 2D array like I'm trying?
I'm pretty sure there is a NULL there because earlier I call this loop and it works:
for(i = 0; args[i] != NULL; i++) {
printf("Argument %d: %s\n", i, args[i]);
}
Thanks!
First of all, how are you verifying that the contents of arrayOfArgs are gibberish? Are you printing out the contents with printf? Are you using a debugger?
Second of all, what is each element of arrayOfArgs supposed to store? Is it supposed to be a 3-element array of strings 9 characters or less? Is it supposed to be a 3x10 element array of pointers to char? Is it supposed to be a 3x10 element array of pointers to pointers to char?
In the line
arrayOfArgs[commandNum][count] = args[i];
the expression args[i] has type char *; that strongly implies that you intend for each element of arrayOfArgs to store a char *. In that case, change the declaration of arrayOfArgs to
char *arrayOfArgs[commands][10];
(I read this as each command can have up to 9 arguments).
IOW, it looks like you're trying to store something like
col 0 1 2 3 4 5 6 7 8 9
row
0 "cmd" "a1" "a2" NULL NULL NULL ...
1 "cmd" "a1" NULL
...
by storing pointers to each string in the 2D array.
If that's the case, then you need to change the declaration of arrayOfArgs as I said above. If that's not the case, then you need to give us an example of what arrayOfArgs is supposed to look like.
So I see some issues...
First I believe you mean to have char arrayOfArgs[commands][10]; as you want a 2D array of 10 character strings (i assume this is what you want)... otherwise it would be a 2D array of an array of string pointers (so like a 4D array of characters :P)
Next the code you have created will only copy into each of those command buffers the character | if the argument itself is |
If you are looking for the contents between the pipe symbols look into the function strchr and strncpy, with these functions I would loop through each arg and if and argument is the pipe symbol, |, find the next argument with the pipe symbol, loop through the arguments in between and then copy the characters in between into a dynamically allocated char array
Also, in your comments you say that the commands array will be dynamic... the way you have your program structured the data is statically allocated on the stack. To make this dynamic you will need to dynamically allocate onto the heap i.e. malloc/calloc
The problem is here:
char **arrayOfArgs[commands][10];
This declares an two dimension array of double char pointers, which is not what you want.
Try the following instead:
char *arrayOfArgs[commands][10];
EDIT: changed to char* because after examine the code I found the OP assigning a pointer to a character array to arrayOfArgs. Thanks to #wildplasser for notifying.
I figured it out. I decided to go a 1D array route rather than a 2D way. The code now takes args, which were split before as "ls" "-l" "|" "wc" and separates them based on if there is a pipe into a new array called "arrayOfCommands". The contents of arrayOfCommands are then, once this is through, "ls -l" and "wc". Here's the code:
int do_command(char **args,) {
const int commands = 2;
int i = 0;
int commandNum = 0;
int firstCommand = 1;
char *arrayOfCommands[commands];
//Go through args until we reach the end
while (args[i] != NULL){
//First case
if(firstCommand){
arrayOfCommands[commandNum] = args[i];
firstCommand = 0;
}
//Rest of the cases
else{
//if we find something that's not a pipe...
if(strcmp(args[i], "|")){
//Add a space to what was a previous part of the same command
arrayOfCommands[commandNum] = strcat(arrayOfCommands[commandNum]," ");
arrayOfCommands[commandNum] = strcat(arrayOfCommands[commandNum],args[i]);
}
//But if we do find a pipe...
else if (!strcmp(args[i], "|")) {
//We know it's time for a new command
commandNum++;
firstCommand = 1;
}
}
//Looking at the next value in args
i++;
}
for(i = 0; i < commands; i++)
printf("Command #[%d]: %s\n", i, arrayOfCommands[i]);
}

assigning a char buffer to an array of pointers

gcc 4.4.4 c89
warning assignment makes integer from pointer without a cast
**devices = device_buff;
warning: value computed is not used
*devices++;
I get the above warnings with the code below. What I am trying to do is get an input from the user. And assign that char array to an array of pointers. So my array of pointers will contain all the devices entered. However, I am getting a UB on this line:
**devices = device_buff;
Many thanks for any advice,
static void device_input()
{
#define DEVICE_SIZE 80
char device_buff[DEVICE_SIZE] = {0};
char **devices = NULL;
size_t i = 0;
for(i = 0; i < 3; i++) {
printf("Enter device name: ");
fgets(device_buff, (size_t)DEVICE_SIZE, stdin);
**devices = device_buff;
*devices++;
}
/* NULL terminate last element */
*devices = NULL;
printf("Display devices\n");
while(*devices != NULL) {
printf("Device [ %s ]\n", *devices++);
}
}
**devices is a char, device_buff is an array of char. The two types are incompatible.
Even if you fix the compiler errors (as described by others), what you are trying to do won't work. You are calling fgets() on the same device_array each time, so each time it's called, it will overwrite what was stored there previously.
Possible solutions include using multiple character arrays (e.g. char device_buff[3][DEVICE_SIZE]) or one long array, and advancing a pointer each time you call fgets().
You are dereferencing a null pointer. Nothing good can come out of that
char** devices = NULL;
initializes the pointer to NULL. It's never set to anything else and then dereferenced (twice).
Pointers are considered hard and it's rather impossible to use them, if one doesn't understand exactly, what he/she is doing. I think there are two options in your scenario. You can store the names in one char array, one adjacent to another and keep an array of pointers pointing to the beginnings of those names or you can use an array of char arrays (two dimensional array) to store the names "separately" each one in another array. I think the second way is much simpler and you should start from getting it working.
You can define the array like this
#define NUM_OF_NAMES 3
char devices[NUM_OF_NAMES][DEVICE_SIZE] = {0};
now devices[0], devices[1] and devices[2] are all char arrays of type char[DEVICE_SIZE].You can use each of them, like the buffer previously.
You must use dynamic or predefined allocation for your Buffer-ARRAY.
The Endmarker in the example is an empty String not a NULL-Pointer.
#define DEVICE_SIZE 80
typedef char DBuff[DEVICE_SIZE];
static void device_input()
{
#define MAXB 3
DBuff device_buff[MAXB+1];
DBuff *devices=device_buff;
size_t i = 0;
for(i = 0; i < MAXB; i++,devices++) {
printf("Enter device name: ");
fgets(*devices, (size_t)DEVICE_SIZE, stdin);
}
**devices=0;
devices=device_buff;
printf("Display devices\n");
while( **devices ) {
printf("Device [ %s ]\n", *devices++);
}
}

C: passing arrays to another method properly

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

Resources