Allocating a char pointer array - c

I've got a number of files that I am reading the contents into a char array, I would like to store the contents I read from each file in an array of characters so that I could access the separate character arrays (file contents) by index, I am not sure of the syntax on how to do this.
My understanding is that I need to set each of the memory addresses in my patterns array to one memory address per char array; to the char array stored in patternData but I do not know how to set this. What is the syntax that I am missing that I need to get this to happen?
What I've tried
I would have thought that If I was storing a type of char* then I would need a type char** to store the separate arrays of char arrays.
I would access a char* by using the following notation to set the memory address of the pattern index to
&patterns[INDEX] = &pData;
However this does not work. There is a plethora of "char pointer array" questions but I'm not sure of the correct way to do this simple assignment of pData to an index of patterns.
char *tData;
int tLength;
char *pData;
int pLength;
char **patterns;
void ReadFromFile(FILE *f, char **data, int *length) //This is what is passed into function
int main(int argc, char **argv)
{
for (int i = 1; i <= 5; i++)
{
FILE *f;
char fileName[1000];
sprintf(fileName, "file%d.txt", i);
f = fopen(fileName, "r");
if (f == NULL)
return 0;
ReadFromFile(f, &pData, &pLength); //works to here, contents of file assigned to pData
fclose(f);
&patterns[i - 1] = &pData;
}
return 0;
}

This line is incorrect:
&patterns[i - 1] = &pData;
You are trying to assign to the result of the "take an address" operator, which is not possible because it's not an lvalue.
The assignment should be as follows:
patterns[i - 1] = pData;
but you need to allocate patterns before you do this. You can use
patterns = malloc(sizeof(char*)*5);
or simply declare patterns to be an array of five:
char *patterns[5];
This assumes that your ReadFromFile function allocates a char*, and assigns it to the address pointed to by pData. Note that you need to free all pointers that were obtained through malloc/calloc/realloc.

I haven't read particularly carefully, but it strikes me as odd that you're taking the reference of patterns when it's a **. This is in the line:
&patterns[i-1] = &pData;

Related

warning: assignment makes integer from pointer without a cast, sorting arguments

I know this was asked a few times, but I still do not fully understand the issue.
I have an assigment where I need to save and sort the arguments.
int main(int argc, char* argv[])
{
int c;
char* s = malloc(argc * sizeof(char));
extern char *optarg;
extern int optind;
extern int optopt;
while ( (c = getopt(argc, argv, ":adh")) != -1) {
s[argc] = argv;
switch (c) {
case 'a': printf("a\n");
break;
case 'd': printf("d\n");
break;
case 'h': printf("h\n");
break;
}
}
return 0;
}
I know that it has something to do with me saving a pointer to an integer or not doing it.
char* argv[] is what? An array of pointers?
And s is only an array of chars?
char* argv[] is what? An array of pointers?
When such a declaration appears in a function prototype, as in your code, it is equivalent to char **argv. That's a pointer to a pointer to char. In this particular case, the pointed-to pointer is the first element of an array of pointers.
And s is only an array of
chars?
As you have declared an initialized it, s is a pointer to char, and in particular, it points to the first char in a dynamically allocated block. You can use the block via s as if it were an array of char, but it is not technically an array, and s definitely is not an array. Although related, pointers and arrays are very different things.
The compiler should have told you at least on which line the error occurs, but you did not pass that information on to us. Nevertheless, I think I can guess. This line ...
s[argc] = argv;
... is nonsense. Since s is a pointer to char, s[argc] designates one char (and in C, char is among the integer data types). On the other hand, argv is a pointer (to pointer to char). Although C permits pointers to be converted to integers, it is rarely useful to do so, and anyway, a conforming program must use a cast to perform such a conversion. Since you don't actually use s for anything, your best bet is probably to just remove that line.
For what it's worth, if you wanted to set s to point to one of the command line arguments, say the second, the syntax would be
s = argv[2];
(The zeroth element of argv is conventionally the name of the program; the arguments start at index 1.) But if you intend to do that then you don't need to malloc() any memory for s; whatever you do allocate for it is leaked when you assign a new value to it.
char *argv[] is a pointer to an array of strings. AKA char **argv
int argc is an integer count of the number of strings on the command line, including exe name.
Note also that the length of each string in argv[] has nothing to do with the value of argc. The strings can be as short as two characters, such as a\0 or as long as (or longer than) 1000 characters.
So the statement char* s = malloc(argc * sizeof(char)); will not do what you are thinking it will do.
First it is the form of an expression that will create some space for only one string, not argc of them.
Second, if your command line included 3 item, ( argc == 3 ) and one of the arguments was a string of length 9, say arguments, then your malloc statement would create space for 1 string, with only 3 characters. Not big enough to contain the string `arguments\0', not to mention the other two.
Use something like the following to determine the length of the longest of the argc strings:
int len = 0, lenKeep = 0;
for(i=0;i<argc;i++)
{
len = strlen(argv[i] > len)
if(len > lenKeep) lenKeep = len;
}
Now, lenKeep + 1 has been established as the length needed to contain the longest string (+ 1 for the NULL character, which strlen(...) does not include in its count.). This can be used, along with knowing the number of strings, argc, to allocate memory for each of them.
For example:
int main(int argc, char* argv[])
{
...
char **string = Create2DStr(argc, lenKeep + 1);//will create space
//sufficient to contain
//strings of your command line
...
}
Where Create2DStr(...) could be defined as:
char ** Create2DStr(ssize_t numStrings, ssize_t maxStrLen)
{
int i;
char **a = {0};
a = calloc(numStrings, sizeof(char *));
for(i=0;i<numStrings; i++)
{
a[i] = calloc(maxStrLen + 1, 1);
}
return a;
}

How do I change fill this string through a pointer which points to another pointer which then points to this string

I'm not sure sure about how I should be doing this. This is my code below. The function readLine reads the line from a file and stores it in string though ptr2. I have a feeling im messing up the stars. Could someone please explain to me what would be the right way to do this.
int main(int argc, char **argv)
{
FILE *fp;
fp = fopen("testFile.txt","r");
if( fp == NULL ){
printf("Error while opening the file.\n");
}
char string[75];
char *ptr1 = &string[0]; //want ptr1 to point to string
char *ptr2 = &ptr1[0]; //want ptr2 to point to ptr1
readLine(fp,**ptr2); //want to send ptr2 pointer so that function read the file and add it to string can be edited through the pointer.
printf("%s", **string);
fclose(fp);
return 0;
}
readLine(FILE *fp, char **string){
fgets(**string, 75, fp);
}
Edit: I know there are much better ways of doing this. I have done it this way just to help explain the situation i am trying to understand which is how do pass a pointer to a pointer to a function to edit the string.
If you have char *p1 = &string[0], then you only need to assign the value of p1 to the p2: char *p2 = p1.
I think the asterisks are confusing you. If you have an asterisk in variable declaration, like this: char *p2, it means that variable p2 is a pointer, but when you have asterisk in another place, such as *p2 = 'c', the you dereference the pointer, that is, you save the value of 'c' directly to the memory location where p2 points.
So, here is your code, fixed:
int main(int argc, char **argv)
{
FILE *fp;
fp = fopen("testFile.txt","r");
if( fp == NULL ){
printf("Error while opening the file.\n");
}
char string[75];
char *ptr1 = string;
char *ptr2 = ptr1;
readLine(fp, ptr2);
printf("%s", **pbuff); // I don't know what pbuff is
fclose(fp);
return 0;
}
readLine(FILE *fp, char *string){
fgets(string, 75, fp);
}
Keep in mind that I didn't compile this because I can't right now, using my sister's computer and she is not a programmer. :)
The difficulty you are having, and the question, seems to stem from an unfamiliarity you have with pointers, and how they are passed as parameters to a function. When you declare a character array, e.g. char string[75]; the value held by string is the address to the first character in, (or beginning memory address) for, the statically declared 75-char array. (it itself is a pointer).
When you then declare another pointer to the array, all you need do is assign the value held by string (a pointer to the address of string[0]) to any new pointer created. e.g.:
char *ptr1 = string;
char *ptr2 = ptr1;
It is exactly the same as declaring both pointers and then setting them equal to string, e.g.:
char *ptr1, *ptr2;
ptr1 = ptr2 = string;
When you are dealing with pointers, they are nothing more than a variable that holds the address to something else as their value. Therefore, after the assignment above ptr1, ptr2 and string all hold the same value (that being the address to the first character in the 75-char array).
Now when you pass a pointer as a parameter to a function, the function receives a copy of the pointer, just as a function receives a copy of any other variable. This is important. If the pointer is already declared and allocated (either statically or dynamically), you can simply pass a copy of the pointer and the function will still receive its value (the address of the array). There is no need to pass the address of the pointer in that case, (e.g. char **) passing the pointer itself (even though passed as a copy) its value still provides the needed address of the array.
The only case where you need to pass the address of the pointer are in the cases there the pointer is unallocated (i.e. NULL - holding no address), or where the address of the pointer itself is important (i.e. passing a pointer to a linked-list where you intend to change the first node in the list, etc.) In those cases, then for the calling function to maintain a correct reference to the newly created pointer or the new address for a list (if you delete/add a new first node) -- you must pass the address of the pointer to the function. In your case, that is not required.
In fact, in your case, it doesn't matter which you pass to readLine (ptr1, ptr2, or string) as all hold the exact same value -- the beginning address of an allocated array. You are free to read values using either one, as the characters you read will all be stored beginning at the exact same address.
With that in mind, you could do something similar to the following:
#include <stdio.h>
char *readLine (FILE *fp, char *string);
int main (int argc, char **argv) {
char string[75] = {0};
FILE *fp = NULL;
char *fn = argc > 1 ? argv[1] : "testFile.txt"; /* filename */
char *ptr1, *ptr2;
ptr1 = ptr2 = string;
if (!(fp = fopen (fn, "r"))) { /* validate file open */
fprintf (stderr, "error: file open failed '%s'.\n", fn);
return 1;
}
if (readLine (fp, ptr2))
printf ("%s\n", ptr1); /* any reference to string will work */
else
fprintf (stderr, "error: readLine failure.\n");
fclose(fp);
return 0;
}
char *readLine (FILE *fp, char *string)
{
return fgets (string, 75, fp);
}
Sample Input File
$ cat ../dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
Use/Output
$ ./bin/readln ../dat/captnjack.txt
This is a tale
When you declare:
char string[80];
string IS a pointer to the first element in the array of character.
That being said, I don't understand the roles of both ptr1 and ptr2. You can simple use fscanf if you want to read into a string.
char string[80];
FILE * pFile;
pFile = fopen ("myfile.txt","r");
fscanf (pFile, "%s", string);
You'll need to check the return value of fopen and fscanf of course. Regarding the C function readline, it exists in two places libreadline and libedit. If you are implementing your own readLine, it may also have problems.
Addendum: If you want to modify a string inside a function, say readLine
void readLine(char **arr){
*arr = "efgh";
}
int main(int argc, const char** argv){
char *str = "abcd";
printf("%s\n",str);
readLine(&str);
printf("%s\n",str);
return 0;
}
This passess the address of the pointer str (readLine(&str)), and the pointer itself is changed in the function(*arr = "efgh").

Pointers to Pointers: What datatype with variable length strings?

I need to store a list of names in C. I decided a good way to do this would be to use double pointers (or pointers-to-pointers).
So I will have an array of names. Each array value is a pointer to another array: the name, stored as a character array.
The names might look like
unsigned char *name = "Joseph";
etc.
And then I would have a list of names (double pointer), to which I could (dynamically) assign each name (single pointer).
(type) **listOfNames;
My question is: what type should the double pointer be? Since it is a pointer to a pointer, I was thinking that it must be large enough to hold an arbitrary memory address: unsigned long, maybe? Or is there another type meant specifically for double pointers?
Alternatively — is there a better way of storing a variable-length array or variable-length strings?
If you have a collection of unsigned char * values:
unsigned char *name_0 = "Joseph";
unsigned char *name_1 = "Helen";
unsigned char *name_2 = "Maximillian";
Then you can create:
unsigned char *data[] = { name_0, name_1, name_2 };
unsigned char **listOfNames = data;
Note that you need to allocate space for the list of pointers — in my code, that's the data array. You could use malloc() instead, but you'd need to remember to code the matching free() at some point.
Adding appropriate const qualifications is left as an exercise in frustration to the interested reader.
If you are reading a variable length list of names from a file a run time, with (presumably) one name per line, then you'd do best with POSIX getline() and strdup().
There must be a number of other questions that deal with this scenario, so I'll be terse with this code:
char **read_file(FILE *fp)
{
char *buffer = 0;
size_t bufsiz = 0;
char **lines = 0;
size_t n_lines = 0;
size_t n_alloc = 0;
while (getline(&buffer, &bufsiz, fp) > 0)
{
if (n_lines + 1 >= n_alloc)
{
size_t new_num = (n_alloc + 1) * 2;
size_t new_size = new_num * sizeof(*lines);
char **new_lines = realloc(lines, new_size);
if (new_lines == 0)
{
free(buffer);
free(lines);
return(0);
}
lines = new_lines;
n_alloc = new_num;
}
lines[n_lines++] = strdup(buffer); // Includes newline!
}
lines[n_lines] = 0; // Null terminate list of strings
free(buffer); // Release input line's memory
return lines;
}
Note that the code uses plain char and not unsigned char. You face some issues if you use unsigned char because neither getline() nor strdup() expects to work with unsigned char. While you can cast your way around the issue, it is messy to do so.

Sorting an array of strings in C

I have an assignment I've been working on for a few hours now, and I can't seem to get it quite right. The assignment is to take a random number of names (from stdin), sort them, and then output them in alphabetical order. I can't find any sites online that handle this kind of sorting specifically, and have had no luck trying to implement qsort() into my code.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int stringcmp(const void *a, const void *b)
{
const char **ia = (const char **)a;
const char **ib = (const char **)b;
return strcmp(*ia, *ib);
}
void main(int argc, char *argv[])
{
char *input[] = {" "};
char temp[20][20];
int i = 0;
int num = 0;
int place = 0;
int stringlen = sizeof(temp) / sizeof(char);
printf("How many names would you like to enter? ");
scanf("%d", &num);
while (place < num)
{
printf("Please input a name(first only): ");
scanf("%s", input[place]);
printf("The name you entered is: ");
printf("%s\n", input[place]);
place++;
}
//qsort(temp, stringlen, sizeof(char *), stringcmp); <-- just an idea I was messing with
qsort(input, stringlen, sizeof(char *), stringcmp);
printf("Names:\n");
for(i=0; i<place; i++)
printf("%s\n", input[i]);
system("PAUSE");
return(EXIT_SUCCESS);
}
The main problem is, when I go to output my code, I cannot use the char *input variable because of how its declared. The temp[] will display, but will not be sorted by qsort because it is not declared as a pointer. Any ideas?
You can't declare your input array like that. Since you know how many the user requires, you can dynamically allocate the array:
char **input = malloc(num * sizeof(char*));
Likewise, when you read your strings in, they need somewhere to go. Simply passing an uninitialized pointer to scanf is not okay. I suggest you define the maximum length of a name and have a temporary buffer for reading it:
const size_t MAX_NAME = 50;
char name[MAX_NAME];
...
for( i = 0; i < num; i++ )
{
printf("Please input a name(first only): ");
scanf("%s", name);
input[i] = strdup(name);
}
[Note this does not prevent the user from overflowing the 'name' buffer. I used scanf for illustrative purposes only]
You seem to be passing the wrong array length to qsort. Try this:
qsort(input, num, sizeof(char *), stringcmp);
When you are finished, you need to release memory for all the names and the array.
for( i = 0; i < num; i++ ) free(input[i]);
free(input);
could you explain
the ** declarations throughout the code? I'm not sure what they're
used for, although I know the function for stringcmp is a widely used
algorithm, I have no idea how it works; I'm thrown off by the double
de-reference markers.
Yep, in the case where I used it, I am telling C that to get a single character, I have to dereference a pointer twice. When you index a pointer, it's dereferencing. So I allocated an array by requesting a block of memory containing num * sizeof(char*) bytes. Because I assigned that pointer to a char**, the compiler knows that I am pointing to a chunk of memory that contains char* values.
If I ask for input[0] (this is the same as *input) it should look at the very start of that memory and pull out enough bytes to form a char*. When I ask for input[1], it skips past those bytes and pulls out the next bunch of bytes that form a char*. Etc... Likewise, when I index a char*, I am pulling out single characters.
In your stringcmp function, you have the following situation. You passed a void* pointer to qsort so it doesn't actually know the size of the data values stored in your array. That's why you have to pass both the array length AND the size of a single element. So qsort just blindly rips through this arbitrary-length array of arbitrary-sized values and fires off memory addresses that ought to contain your data for comparison. Because qsort doesn't know anything else about your array elements except where they are located, it just uses void*.
But YOU know that those pointers are going to be the memory addresses of two of your array elements, and that your array elements are char*. So you need the address of a char* (hence you cast the pointers to char**). Now you need to dereference these pointers when you call strcmp() because that function requires a char* (ie a value that points directly to the memory containing your string characters). That is why you use the * in strcmp(*ia, *ib).
One quick way to fix your program is to declare input as an array of pointers, like this:
char *input[20];
When you read names in, use tmp[place] for your buffer, and store the pointer into input, like this:
scanf("%19s", tmp[place]);
input[place] = tmp[place];
Now sorting the input should work fine.
This has a limitation of being limited to 20 lines of 20 characters max. If you learned about malloc in the class, you should be able to fix that by allocating your strings and the string array dynamically.

2D array using pointers

I am attempting to read a file with some random names in the format "ADAM","MARK","JESSIE" .....
I have some constraints, the file should be read in a function but should be accessible form the main function with no global variables.
The file size and the no of names in the file are not known.
This is what I have done till now. I have some difficulty with dynamic 2d array as I have not used them much.
/* Function to read from the file */
int read_names(FILE *input, char ***names, int *name_count)
{
int f1,size,count,i,j=0;
char **name_array,*text,pos=0;
/* get the file size */
f1=open("names.txt",O_RDONLY);
size=lseek(f1,0,SEEK_END);
close(f1);
/* Reading all the characters of the file into memory */
//Since file size is known we can use block transfer
text=(char *) malloc(size * sizeof(char) );
fscanf(input,"%s",text);
/* Finding the no of names in the file */
for(i=0;i<size;i++)
{
if(text[i]==',')
count++;
}
printf("No. of names determined\n");
/* Assigning the Name count to the pointer */
name_count=(int*)malloc(sizeof(int));
*name_count=count;
name_array=(char **) malloc(count * sizeof(char *));
for(i=0;i<count;i++)
{
name_array[i]=(char*) malloc(10 *sizeof(char ));
}
for(i=0;i<size;i++)
{
if(text[i]!='"')
if(text[i]==',')
{
**name_array[pos][j]='\0'; //error here
pos++;
j=0;
}
else
name_array[pos][j++]=text[i];
}
printf("Names Counted\n");
printf("Total no of names: %d\n",*name_count);
names=(char ***) malloc(sizeof(char **);
names=&name_array;
return 1;
}
/* Main Function */
int main(int argc, char *argv[])
{
FILE *fp;
char ***names;
int *name_count;
int status;
// Opening the file
fp = fopen("names.txt","r");
// Now read from file
status = read_names(fp,names,name_count);
printf("From Main\n");
fclose(fp);
system("PAUSE");
return 0;
}
I use WxDev and am getting an error "invalid type argument of `unary *' when I try to assign the null character.
Any pointers on how to do this?
Take a closer look at this expression from the line generating the error and think about what it's doing:
**name_array[pos][j]
Remembering that brackets have higher precedence than unary *, this is equivalent to *(*((name_array[pos])[j])), which is 2 subscripts followed by 2 dereferences. That's a total of 4 dereferences, since foo[i] is equivalent to *(foo+i). The declared type of name_array is char **, which means you can only dereference it twice. Think of the type declaration char **name_array as meaning that **name_array has type char, since that's the basis for type declaration syntax (see the History of C, "Embryonic C").
Off topic
Another issue arises on the line:
name_array[i]=(char*) malloc(sizeof(char ));
Here, you're only allocating enough space for a single character in each element of name_array.
**name_array[pos][j]='\0'; \\error here
I see that name_array is declared as
char **name_array
Problem is that
**name_array[pos][j] tries to dereference (twice!!) a character
** (name_array[pos]) [j]; /* name_array[pos] is of type (char*) */
** ( (name_array[pos]) [j] ); /* name_array[pos][j] is of type (char) */
You cannot dereference a character.
Suggestion: simplify your code.
When code doesn't "behave", it usually is not because it is too simple ;)
This might seem pedantic, but it is important. You aren't using a 2D array. In fact, you don't create ANY arrays in your code.
One of the important things to understand is that arrays "decay" into pointers.
link text
Much of the confusion surrounding arrays and pointers in C can be traced to a misunderstanding of this statement. Saying that arrays and pointers are equivalent'' means neither that they are identical nor even interchangeable. What it means is that array and pointer arithmetic is defined such that a pointer can be conveniently used to access an array or to simulate an array. In other words, as Wayne Throop has put it, it'spointer arithmetic and array indexing [that] are equivalent in C, pointers and arrays are different.'')

Resources