How to access struct in an array of structs - c

In a school assignment, I have to sort elements of struct that are located in binary file. I think I have managed to sort it, but I have a problem with printing the result. I don't know how to access elements of struct because data must be read from file, so I only have address of the first struct in a array. (I think it should stay in array so that I can use qsort.)
This is the main code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "readfile.h"
typedef struct {
char name[32];
double value;
} record;
int nuvu(record* a, record* b){
if(a->name < b->name) return -1;
if(a->name > b->name) return 1;
if(a->value < b->value) return -1;
if(a->value > b->value) return 1;
}
int main()
{
long N;
unsigned char* p = readfile( "d.bin", &N );
char* s;
scanf("%s",&s);
int k= N/sizeof(record);
qsort(p,k,sizeof(record),(int(*)(const void*, const void *))nuvu);
printf???
free(p);
return 0;
}
Additional:
readfile.c
#include "readfile.h"
unsigned char* readfile( char* filename, long* pN )
{
FILE* f= fopen(filename,"rb");
if(f==0){
*pN=-1;
return 0;
}
fseek(f,0,SEEK_END);
*pN=ftell(f);
fseek(f,0,SEEK_SET);
char*p=malloc(*pN);
if(p==0){
*pN=-2;
fclose(f);
return 0;
}
size_t r = fread(p,1,*pN,f);
if(r!=*pN){
*pN=-3;
fclose(f);
free(p);
return 0;
}
fclose(f);
return p;
}
readfile.h
#ifndef __READFILE_H
#define __READFILE_H
#include <stdio.h>
#include <stdlib.h>
unsigned char* readfile(char* filename, long* pN);
#endif /* __READFILE_H */

The biggest confusion you seem to have is "How do I read my array of structs back in?"
unsigned char* p = readfile( "d.bin", &N );
Is no way to begin. The concept of reading records from a binary file into an array of struct, is to read sizeof (struct record) bytes from the file into storage for type struct record. (this will ignore for now the lack of serialization of the data, padding and portability issues, and the fact we are using a typedef).
Knowing the file size, and knowing the sizeof (struct record) allows you to (1) validate the number of records you will read from the file, e.g. (nbytes / sizeof (struct record)) and (2) determine whether there are any stray bytes leftover that will not be part of the read (e.g. if (nbytes / sizeof (struct record) != 0)) which if present you should at least warn about.
Depending on the number of records you have to read and whether there is an upper bound for that number will determine whether you can use a fixed size array (or VLA) or whether you need to dynamically allocate (and reallocate) to address an unknown number of records or to prevent StackOverflow.. Regardless how you handle creating storage for your records -- It is up to you to make sure you do not write beyond the bounds of the storage you create.
Below, for example purposes we will simply work with an array of 100 records. The dividing line between what will fit on the stack and when you need to dynamically allocate will be compiler dependent, but any time you start thinking about tens of thousands of records, you need to consult your compiler documentation and start thinking about dynamic allocation.
fread provides a simple method to read binary records from a file and validate that you actually read the number of records you intended to read. For example, given a declaration of an array of 100 records in rec, you could do something like the following:
enum { MAXC = 32, MAXS = 100 }; /* if you need constants, define them */
...
record rec[MAXS] = {{ .name = "" }}; /* array of 100 records */
...
nrec = nbytes / sizeof *rec; /* number of records based on file size */
/* read / validate nrec records from file */
if (fread (rec, sizeof *rec, nrec, fp) != nrec) {
perror (fn);
return 1;
}
With your records successfully read from your file, using qsort to sort the records (either by name or by value) requires you to understand that the const void * pointers to be compared in your comparison function will be pointer to rec, so you must provide an appropriate cast within your comparison function to access and compare the values. For example to perform a string comparison on name, you can do something similar to the following:
/** record string comparison on name */
int reccmpname (const void *a, const void *b)
{
const record *ra = a,
*rb = b;
return strcmp (ra->name, rb->name);
}
Other than that, the remainder of what your code lack is validation of each step in the process. Always, always, validate the return of any function you use and handle any errors you encounter. A minimal example, without splitting the code up between separate source files could be something like the following. Splitting up into separate source files is left to you.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { MAXC = 32, MAXS = 100 }; /* if you need constants, define them */
typedef struct {
char name[MAXC];
double value;
} record;
/** record string comparison on name */
int reccmpname (const void *a, const void *b)
{
const record *ra = a,
*rb = b;
return strcmp (ra->name, rb->name);
}
int main (int argc, char **argv) {
record rec[MAXS] = {{ .name = "" }}; /* array of 100 records */
size_t nrec = 0; /* number of records from file */
long nbytes = 0; /* number of bytes in file */
char *fn = argc > 1 ? argv[1] : "dat/records.bin";
FILE *fp = fopen (fn, "rb");
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
if (fseek (fp, 0, SEEK_END) == -1) { /* validate seek to end */
perror ("fseek");
return 1;
}
nbytes = ftell (fp); /* number of bytes in file */
if (nbytes == -1) {
perror ("ftell");
return 1;
}
if (fseek (fp, 0, SEEK_SET) == -1) { /* validate seek to start */
perror ("fseek");
return 1;
}
if (nbytes % sizeof *rec != 0) /* does file contain even no. or records? */
fprintf (stderr, "warning: file size not multiple of record size.\n");
nrec = nbytes / sizeof *rec; /* number of records based on file size */
/* read / validate nrec records from file */
if (fread (rec, sizeof *rec, nrec, fp) != nrec) {
perror (fn);
return 1;
}
fclose (fp); /* close file */
printf ("\n=== unsorted records ===\n\n"); /* output unsorted */
for (size_t i = 0; i < nrec; i++)
printf ("%-32s %g\n", rec[i].name, rec[i].value);
qsort (rec, nrec, sizeof *rec, reccmpname); /* qsort records */
printf ("\n=== sorted records ===\n\n"); /* output sorted */
for (size_t i = 0; i < nrec; i++)
printf ("%-32s %g\n", rec[i].name, rec[i].value);
return 0;
}
note: The data file used simply contained 100 struct records with dictionary words as name and random values as value shuffled before being written to the file.
Example Use/Output
$ ./bin/struct_rd_name_val_recs
=== unsorted records ===
Abscess 4.15871e+08
Abject 3.5743e+08
Abo 6.87659e+08
Aboard 2.02028e+09
Abase 3.34319e+08
...
=== sorted records ===
A 3.66907e+08
Aaa 5.59224e+07
Aaas 1.45617e+09
Aardvark 1.72828e+09
Aarhus 1.95723e+09
Let me know if you have any questions.

Use the standard procedure for qsort, don't change its signature. Use strcmp as noted in comments. You have to figure out the logic of how the structure is to be sorted. The example below sorts by record::name, if name is the same, it tests value, in that order:
int nuvu(const void * a_, const void * b_)
{
const record* a = a_;
const record* b = b_;
if(strcmp(a->name, b->name) == 0)
return a->value > b->value;
return strcmp(a->name, b->name);
}
Data is read as bytes in to p, it must be converted to "array of records" record* arr = (record*)p;. The number of items in the array should be filesize/sizeof(record) if everything went according to plan
int main(void)
{
long filesize = 0;
unsigned char* p = readfile("d.bin", &filesize);
if(!p)
return 0;
int count = filesize / sizeof(record);
record* arr = (record*)p;
qsort(arr, count, sizeof(record), nuvu);
for(int i = 0; i < count; i++)
printf("%s %f\n", arr[i].name, arr[i].value);
free(p);
return 0;
}

You can use the [] operator with a pointer:
struct my_struct {
int i, j;
};
struct my_struct * ptr = malloc(sizeof(struct my_struct) * 10);
for(int n = 0; 10 > n; ++ n)
{
ptr[n].i = n;
ptr[n].j = n*2;
}
free(ptr);

Related

Having problems passing in data to a function

Writing a program in C and I am trying to pass two variables into the function kstrextend. Name which is a word or set of characters that is stored in the value kstring and a which is a numeric value, but name is not getting passed into the function at all as far as I can tell and I cannot figure out why. Is something not getting stored correctly? Because the function works just fine I just cannot get name passed in correctly.
Declaration of kstring and name:
kstring name;
char kstring[50];
Typedef:
typedef struct
{
char *data;
size_t length;
} kstring;
Function:
void kstrextend(kstring *strp, size_t nbytes)
{
char *nwData;
int lnth=strp->length;
if(lnth < nbytes)
{
// new array allocate with large size and copy data to new array
nwData = (char *)realloc(strp->data, nbytes);
// call abort in case of error
if(nwData == NULL)
{
abort();
}
//Making strp->data point to the new array
strp->data = nwData;
//Setting strp->length to the new size.
strp->length = nbytes;
for(int i = 0; i <= lnth; i++)
{
printf("\n %s",strp->data);
}
// filled with '\0' in remaining space of new array
for (int lp = lnth; lp < nbytes; lp++)
{
strp->data[lp] = '\0';
printf("\n %s", strp->data[lp]);
}
}
}
Portion of main:
size_t a;
char * k = kstring;
printf("\n Enter number: ");
scanf("%d", &a);
name.data = (char*)calloc(sizeof(k), 1);
strcpy(input, k);
name.length= kstring_length;
kstrextend(&name,a);
First of all, you have misleading variable name kstring. Use something else like kstring_init and assign it a value. I assume you want to initialize the name variable of type kstring with something and then change its length. So this is what it is all about. Then define a constant of type char * and initialize length and data of your kstring with it. Then use realloc to extend the memory of the pointer with the input value a, not with the size of k. That does not make sense. Since the size of k is the size of the pointer, which is constant.
In your function: don't use int if you pass size_t. Use the same datatype where you do the same things.
In your loop from 0 to lnth, you output the same string lnth+1 times, which does not make sense. You probably want to output the characters of the string. So use %c and use an index into the character array and don't set <= lnth but < lnth as upper limit. Take care with data types if signed and unsigned!
Design hint: If you have a if block, that wraps all your code... invert the condition and just exit so that the code is after the if block.
Take care when you work with size_t and int, since int is signed and size_t is not, which can give problems in if statements.
Don't use abort but rather exit. You don't want your program to abort abnormally and core-dump.
A working version of your program is:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct
{
char *data;
size_t length;
} kstring;
kstring name;
char *kstring_init = "blabla";
void kstrextend(kstring *strp, size_t nbytes)
{
char *nwData;
size_t lnth = strp->length;
if ((int) lnth >= (int) nbytes) {
printf("Error, size already larger than requested size.\n");
exit(-1);
}
// new array allocate with large size and copy data to new array
nwData = realloc(strp->data, sizeof(char) * (int) nbytes);
if(nwData == NULL)
{
printf("Error, realloc returned NULL\n");
exit(-1);
}
//Making strp->data point to the new array
strp->data = nwData;
//Setting strp->length to the new size.
strp->length = nbytes;
for(int i = 0; i < lnth; i++)
{
printf("\n %c", strp->data[i]);
}
// filled with '\0' in remaining space of new array
for (int lp = lnth; lp < (int) nbytes; lp++)
{
strp->data[lp] = '\0';
printf("\n %c", strp->data[lp]);
}
}
int main(void)
{
size_t a;
printf("\n Enter number: ");
scanf("%d", &a);
name.length = strlen(kstring_init) + 1;
printf("Length of string is: %d\n", name.length);
name.data = (char*)malloc(sizeof(char) * name.length);
strcpy(name.data, kstring_init);
printf("Old string: %s\n", name.data);
printf("You want to reallocate %d bytes\n", a);
kstrextend(&name, a);
return 0;
}

having trouble acquiring Int from "gets(*buffer)"

So my tutor asked me to acquire an integer for a data struct , but 2 of the int parts im supposed to get , is via gets , im thinking he meant me to use gets(temp) // temp stand for char temp[50] and then perform x=strlen(temp);
here is my work so far :
#include<stdio.h>
#include<conio.h>
#include<string.h>
#define N 20
struct one
{
int ak;
char pin[N];
};
struct two
{
int data,mat[N];
struct one melos,item;
};
int main(void)
{
bool x=true;
int i;
char temp[N];
struct one person;
struct two memb;
/*GETTING VARS*/
gets(person.pin);
gets(memb.item.pin);
gets(memb.melos.pin);
person.ak=(int)gets(temp);
memb.data=(int)gets(temp);
for(i=0;i<N;i++)
scanf("%d",&memb.mat[i]);
scanf("%d",&memb.melos.ak);
scanf("%d",&memb.item.ak);
putchar('\n');
/*CHECKING VARS*/
puts("Posting vars in Data Struct ""one"" \n");
printf("%d\n",person.ak);
puts(person.pin);
puts("Posting vars in Data Struct ""two"" \n");
printf("%d\n",memb.data);
putchar('\n');
for(i=0;i<N;i++)
printf("%d\t",memb.mat[i]);
printf("\n%d\n",memb.melos.ak);
puts(memb.melos.pin);
printf("%d\n",memb.item.ak);
puts(memb.item.pin);
puts("**********************\n---POST_END---");
return 1;
}
The return value of gets() is the pointer to the buffer passed if successful, and NULL if failed. It won't mean the integer read.
Also note that you shouldn't use gets(), which has unavoidable risk of buffer overrun.
You will have to convert the string read to integer.
Try this:
#include<stdio.h>
#include<stdlib.h> /* for using atoi() */
#include<string.h>
#define N 20
/* read one line while checking the size read */
char* safer_gets(char* outbuf, size_t max){
size_t idx = 0;
int input;
if(max == 0) return NULL;
while(idx + 1 < max && (input = getchar()) != EOF && input != '\n'){
outbuf[idx++] = input;
}
if (idx == 0 && input == EOF) return NULL;
outbuf[idx] = '\0';
return outbuf;
}
/* read one line and convert it to integer */
int get_int(void) {
char temp[N];
safer_gets(temp, sizeof(temp));
return atoi(temp);
}
struct one
{
int ak;
char pin[N];
};
struct two
{
int data,mat[N];
struct one melos,item;
};
int main(void)
{
int i;
struct one person;
struct two memb;
/*GETTING VARS*/
safer_gets(person.pin, sizeof(person.pin));
safer_gets(memb.item.pin, sizeof(memb.item.pin));
safer_gets(memb.melos.pin, sizeof(memb.melos.pin));
person.ak=get_int();
memb.data=get_int();
/* latter part omitted */
}
Avoid gets - I'm surprised your class mentions it because it's been removed from all of the recent C language specifications and now deprecated in C++11.
But if you must...
http://www.cplusplus.com/reference/cstdio/gets/
long int getIntegerWithGetS() {
char* end;
char* buffer = calloc( 1024, sizeof(char) ); // 1024 bytes should be enough
long int result;
bool got_result_yet = false;
while( !got_result_yet ) {
gets( buffer ); // DANGER WILL ROBINSON
result = strtol( buffer, &end, 10 );
if( result == 0 ) {
if( end != buffer ) {
result = 0;
got_result_yet = true;
}
} else {
if( result != LONG_MAX && result != LONG_MIN && errno != ERANGE ) got_result_yet = true;
}
}
free(buffer);
return result;
}
Note how I call my code in a loop so we will persist until we get valid input from the user. I start off calling gets (to fill the buffer), then using strtol to convert the human-readable text in buffer to an actual computer int value - note that strtol is more powerful than atoi and uses long int instead of int as its declared type. Then the code checks for the strtol error conditions (in case a user types in "0" when the function returns 0 on failure anyway).
I don't return immediately, because the buffer must be free'd before execution leaves the scope, otherwise that buffer will remain unallocated and so leaked.

Passing pointer of pointers to function, returning both an int and an address (by parameter)

I've got a function which, as is, works correctly. However the rest of the program has a limitation in that I've preset the size of the array (the space to be allocated). Obviously, this is problematic should an event arise in which I need extra space for that array. So I want to add dynamic allocation of memory into my program.
But I'm having an issue with the whole pointer to a pointer concept, and I've utterly failed to find an online explanation that makes sense to me...
I think I'll want to use malloc(iRead + 1) to get an array of the right size, but I'm not sure what that should be assigned to... *array? **array? I'm not at all sure.
And I'm also not clear on my while loops. &array[iRead] will no longer work, and I'm not sure how to get a hold of the elements in the array when there's a pointer to a pointer involved.
Can anyone point (heh pointer pun) me in the right direction?
I can think of the following approaches.
First approach
Make two passes through the file.
In the first pass, read the numbers and discard them but keep counting the number of items.
Allocate memory once for all the items.
Rewind the file and make a second pass through it. In the second pass, read and store the numbers.
int getNumberOfItems(FILE* fp, int hexi)
{
int numItems = 0;
int number;
char const* format = (hexi == 0) ? "%X" : "%d";
while (fscanf(fp, format, &number) > 0) {
++numItems;
return numItems;
}
void read(int *array, FILE* fp, int numItems, int hexi)
{
int i = 0;
char const* format = (hexi == 0) ? "%X" : "%d";
for ( i = 0; i < numItems; ++i )
fscanf(fp, format, &array[i]);
}
int main(int argc, char** argv)
{
int hexi = 0;
FILE* fp = fopen(argv[1], "r");
// if ( fp == NULL )
// Add error checking code
// Get the number of items in the file.
int numItems = getNumberOfItems(fp, hexi);
// Allocate memory for the items.
int* array = malloc(sizeof(int)*numItems);
// Rewind the file before reading the data
frewind(fp);
// Read the data.
read(array, fp, numItems, hexi);
// Use the data
// ...
// ...
// Dealloate memory
free(array);
}
Second approach.
Keep reading numbers from the file.
Every time you read a number, use realloc to allocate space the additional item.
Store the in the reallocated memory.
int read(int **array, char* fpin, int hexi)
{
int number;
int iRead = 0;
// Local variable for ease of use.
int* arr = NULL;
char const* format = (hexi == 0) ? "%X" : "%d";
FILE *fp = fopen(fpin, "r");
if (NULL == fp){
printf("File open error!\n");
exit(-1);
}
while (fscanf(fp, format, &number) > 0) {
arr = realloc(arr, sizeof(int)*(iRead+1));
arr[iRead] = number;
iRead += 1;
}
fclose(fp);
// Return the array in the output argument.
*array = arr;
return iRead;
}
int main(int argc, char** argv)
{
int hexi = 0;
int* array = NULL;
// Read the data.
int numItems = read(&array, argv[1], hexi);
// Use the data
// ...
// ...
// Dealloate memory
free(array);
}
int read(int **array, char* fpin, int hexi) {
int iRead = 0;
int i, *ary;
char *para;
FILE *fp;
fp = fopen(fpin, "r");
if (NULL == fp){
printf("File open error!\n");
exit(-1);
}
para = (hexi == 0) ? "%*X" : "%*d";
while (fscanf(fp, para)!= EOF)
++iRead;
ary = *array = malloc(iRead*sizeof(int));
if(ary == NULL){
printf("malloc error!\n");
exit(-2);
}
rewind(fp);
para = (hexi == 0) ? "%X" : "%d";
for(i = 0; i < iRead; ++i)
fscanf(fp, para, &ary[i]);
fclose(fp);
return iRead;
}
I'd suggest something like this:
int read(int **array_pp, char* fpin, int hexi) {
...
int *array = malloc (sizeof (int) * n);
for (int i=0; i < n; i++)
fscanf(fp, "%X",&array[i]);
...
*array_pp = array;
return n;
}
Notes:
1) You must use "**" if you want to return a pointer in a function argument
2) If you prefer, however, you can declare two pointer variables (array_pp and array) to simplify your code.
I think you wouldn't call it an array. Arrays are of fixed size and lie on the stack. What you need (as you already said), is dynamically allocated memory on the heap.
maybe that's why you didn't find much :)
here are some tutorials:
http://en.wikibooks.org/wiki/C_Programming/Arrays (and following pages)
http://www.eskimo.com/~scs/cclass/int/sx8.html
you got the function declaration correctly:
int read(int **array, char* fpin, int hexi)
What you need to do:
find out how much memory you need, eg. how many elements
allocate it with *array = malloc(numElements * sizeof(int)) (read "at the address pointed by array allocate memory for numElements ints")
now you can (*array)[idx] = some int (read "at the address pointed by array, take the element with index idx and assign some int to it")
call it with int* destination; int size = read(&destination, "asdf", hexi)
hope it helps..

Storing values of an array in one variable

I am trying to use md5 code to calculate checksums of file. Now the given function prints out the (previously calculated) checksum on screen, but I want to store it in a variable, to be able to compare it later on.
I guess the main problem is that I want to store the content of an array in one variable.
How can I manage that?
Probably this is a very stupid question, but maybe somone can help.
Below is the function to print out the value. I want to modify it to store the result in one variable.
static void MDPrint (MD5_CTX* mdContext)
{
int i;
for (i = 0; i < 16; i++)
{
printf ("%02x", mdContext->digest[i]);
} // end of for
} // end of function
For reasons of completeness the used struct:
/* typedef a 32 bit type */
typedef unsigned long int UINT4;
/* Data structure for MD5 (Message Digest) computation */
typedef struct {
UINT4 i[2]; /* number of _bits_ handled mod 2^64 */
UINT4 buf[4]; /* scratch buffer */
unsigned char in[64]; /* input buffer */
unsigned char digest[16]; /* actual digest after MD5Final call */
} MD5_CTX;
and the used function to calculate the checksum:
static int MDFile (char* filename)
{
FILE *inFile = fopen (filename, "rb");
MD5_CTX mdContext;
int bytes;
unsigned char data[1024];
if (inFile == NULL) {
printf ("%s can't be opened.\n", filename);
return -1;
} // end of if
MD5Init (&mdContext);
while ((bytes = fread (data, 1, 1024, inFile)) != 0)
MD5Update (&mdContext, data, bytes);
MD5Final (&mdContext);
MDPrint (&mdContext);
printf (" %s\n", filename);
fclose (inFile);
return 0;
}
Declare an array and memcpy the result.
Example:
unsigned char old_md5_dig[16]; // <-- binary format
...
MD5_CTX mdContext;
MD5Init(&mdContext);
MD5Update(&mdContext, data, bytes);
MD5Final(&mdContext);
memcpy(old_md5_dig, mdContext.digest, 16); // <--
Edit: to compare the previous with the new md5 hash you can use memcmp,
if (memcmp(old_md5_dig, mdContext.digest, 16)) {
// different hashes
}
Just pass a char buffer and its size to this function:
static void MDGen (mdContext, buf, size)
MD5_CTX *mdContext;
char *buf;
size_t size;
{
int i;
int minSize = 33; // 16 pairs of hex digits plus terminator
if ((buf != NULL) && (size >= minSize))
{
memset(buf, 0, size);
for (i = 0; i < 16; i++)
{
snprintf(buf + (i*2), size - (i*2), "%02x", mdContext->digest[i]);
}
}
}
You can define a variable:
char **md5sums;
You will then need to modify MDPrint to instead return a malloced null-terminated string with the 32 hex digits. You can basically reuse your existing loop, but with sprintf instead.
Then have main add each md5sum (a char*) to md5sums. You will need to use realloc to allocate memory for md5sums because you don't know the number of elements up front.
It should be:
static char* MDString (mdContext)
MD5_CTX *mdContext;
{
int i;
char *digest = malloc(sizeof(char) * 33);
if(digest == NULL)
{
return NULL;
}
for (i = 0; i < 16; i++)
{
sprintf(digest + (i * 2), "%02x", mdContext->digest[i]);
}
return digest;
}
Also, you should modify your code by editing your question. And why are you using K&R syntax?
EDIT: I fixed some incorrect counts.
Since you want to duplicate, store, compare, free and probably more the MD5 digest, just create a md5_t type and write appropriate functions to manipulate it, ie :
typedef char md5_t[16];
md5_t *md5_new( MD5_CTX *pMD5Context )
{
md5_t *pMD5 = malloc( sizeof( md5_t ) );
memcpy( pMD5, pMD5Context->digest, 16 );
return pMD5 ;
}
int md5_cmp( md5_t *pMD5A, md5_t *pMD5B )
{
return memcmp( pMD5A, pMD5B, 16 );
}
void md5_print( md5_t *pMD5 )
{
...
}
void md5_free( md5_t *pMD5 )
{
free( pMD5 );
}
And so on ... Next, create a type for your MD5 array and simple functions to manipulate it :
typedef struct md5array_t {
unsigned int uSize ;
md5_t **ppMD5 ;
}
md5array_t *md5array_new()
{
md5array_t *pArray = malloc( sizeof( md5array_t );
pArray->uSize = 0 ;
pArray->ppMD5 = NULL ;
}
md5array_t *md5array_add( md5array_t *pArray, md5_t *pMD5 )
{
pArray->uSize ++ ;
pArray = realloc( pArray, pArray->uSize + sizeof( md5_t * ) );
pArray->ppMD5[ pArray->uSize-1 ] = pMD5 ;
}
md5_t *md5array_get( md5array_t *pArray, unsigned int uIndex )
{
return pArray->ppMD5[ uIndex ];
}
void md5array_free( md5array_t *pArray }
{
/* I let you find what to write here.
Be sure to read AND understand the previous
functions. */
}
To resume : create a type and the functions you need to manipulate it as soon as you want to do more than one operation with a datum. You don't need to create a real, generic type with full-blown functions representing as many operations you can imagine on that type : just code what you need. For example, in the md5array_t, you can add a md5_t * but you cannot delete it (unless you write the function void md5array_del( md5array_t *pArray *, int iIndex ).
P.S. : my C code is here to "illustrate" my point of view, not to be useable by just copying/pasting it as is ...
Store it as a string and then use strcmp() to compare.
Just leave in an array!
You don't have you store it in variable; because it is ALREADY in a variable..
Just create global variable, store MD5 hash in it and compare to it later.
What you need is MD5IsEqual function, which takes 2 arrays like this.
int MD5IsEqual(unsigned char *x, unsigned char* y)
{
int i;
for(i=0; i<16;i++)
if(x[i] != y[i])
return 0;
return 1;
}
Why not make the function
MD5File(char * filename, unsigned char *digest){
/* as before */
memcpy(digest, mdContext->digest, 16);
return;
}
so that outside the function you have access to the digest (after printing it) ?
The digest is just an array of 16 unsigned char...
You know where the sum is stored by the way you print it: through ->digest[i]. Which is defined like
unsigned char digest[16];
So you basically just need to copy these 16 unsigned char in another array of unsigned char (at least 16 unsigned char long). The function memcpy can do it. If you need comparing two sums, you can use memcmp (the comparing size will be 16*sizeof(unsigned char) which is 16, being sizeof(unsigned char) 1.

Copying a file line by line into a char array with strncpy

So i am trying to read a text file line by line and save each line into a char array.
From my printout in the loop I can tell it is counting the lines and the number of characters per line properly but I am having problems with strncpy. When I try to print the data array it only displays 2 strange characters. I have never worked with strncpy so I feel my issue may have something to do with null-termination.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[])
{
FILE *f = fopen("/home/tgarvin/yes", "rb");
fseek(f, 0, SEEK_END);
long pos = ftell(f);
fseek(f, 0, SEEK_SET);
char *bytes = malloc(pos); fread(bytes, pos, 1, f);
int i = 0;
int counter = 0;
char* data[counter];
int length;
int len=strlen(data);
int start = 0;
int end = 0;
for(; i<pos; i++)
{
if(*(bytes+i)=='\n'){
end = i;
length=end-start;
data[counter]=(char*)malloc(sizeof(char)*(length)+1);
strncpy(data[counter], bytes+start, length);
printf("%d\n", counter);
printf("%d\n", length);
start=end+1;
counter=counter+1;
}
}
printf("%s\n", data);
return 0;
}
Your "data[]" array is declared as an array of pointers to characters of size 0. When you assign pointers to it there is no space for them. This could cause no end of trouble.
The simplest fix would be to make a pass over the array to determine the number of lines and then do something like "char **data = malloc(number_of_lines * sizeof(char *))". Then doing assignments of "data[counter]" will work.
You're right that strncpy() is a problem -- it won't '\0' terminate the string if it copies the maximum number of bytes. After the strncpy() add "data[counter][length ] = '\0';"
The printf() at the end is wrong. To print all the lines use "for (i = 0; i < counter; i++) printf("%s\n", data[counter]);"
Several instances of bad juju, the most pertinent one being:
int counter = 0;
char* data[counter];
You've just declared data as a variable-length array with zero elements. Despite their name, VLAs are not truly variable; you cannot change the length of the array after allocating it. So when you execute the lines
data[counter]=(char*)malloc(sizeof(char)*(length)+1);
strncpy(data[counter], bytes+start, length);
data[counter] is referring to memory you don't own, so you're invoking undefined behavior.
Since you don't know how many lines you're reading from the file beforehand, you need to create a structure that can be extended dynamically. Here's an example:
/**
* Initial allocation of data array (array of pointer to char)
*/
char **dataAlloc(size_t initialSize)
{
char **data= malloc(sizeof *data * initialSize);
return data;
}
/**
* Extend data array; each extension doubles the length
* of the array. If the extension succeeds, the function
* will return 1; if not, the function returns 0, and the
* values of data and length are unchanged.
*/
int dataExtend(char ***data, size_t *length)
{
int r = 0;
char **tmp = realloc(*data, sizeof *tmp * 2 * *length);
if (tmp)
{
*length= 2 * *length;
*data = tmp;
r = 1;
}
return r;
}
Then in your main program, you would declare data as
char **data;
with a separate variable to track the size:
size_t dataLength = SOME_INITIAL_SIZE_GREATER_THAN_0;
You would allocate the array as
data = dataAlloc(dataLength);
initially. Then in your loop, you would compare your counter against the current array size and extend the array when they compare equal, like so:
if (counter == dataLength)
{
if (!dataExtend(&data, &dataLength))
{
/* Could not extend data array; treat as a fatal error */
fprintf(stderr, "Could not extend data array; exiting\n");
exit(EXIT_FAILURE);
}
}
data[counter] = malloc(sizeof *data[counter] * length + 1);
if (data[counter])
{
strncpy(data[counter], bytes+start, length);
data[counter][length] = 0; // add the 0 terminator
}
else
{
/* malloc failed; treat as a fatal error */
fprintf(stderr, "Could not allocate memory for string; exiting\n");
exit(EXIT_FAILURE);
}
counter++;
You are trying to print data with a format specifier %s, while your data is a array of pointer s to char.
Now talking about copying a string with giving size:
As far as I like it, I would suggest you to use
strlcpy() instead of strncpy()
size_t strlcpy( char *dst, const char *src, size_t siz);
as strncpy wont terminate the string with NULL,
strlcpy() solves this issue.
strings copied by strlcpy are always NULL terminated.
Allocate proper memory to the variable data[counter]. In your case counter is set to 0. Hence it will give segmentation fault if you try to access data[1] etc.
Declaring a variable like data[counter] is a bad practice. Even if counter changes in the subsequent flow of the program it wont be useful to allocate memory to the array data.
Hence use a double char pointer as stated above.
You can use your existing loop to find the number of lines first.
The last printf is wrong. You will be printing just the first line with it.
Iterate over the loop once you fix the above issue.
Change
int counter = 0;
char* data[counter];
...
int len=strlen(data);
...
for(; i<pos; i++)
...
strncpy(data[counter], bytes+start, length);
...
to
int counter = 0;
#define MAX_DATA_LINES 1024
char* data[MAX_DATA_LINES]; //1
...
for(; i<pos && counter < MAX_DATA_LINES ; i++) //2
...
strncpy(data[counter], bytes+start, length);
...
//1: to prepare valid memory storage for pointers to lines (e.g. data[0] to data[MAX_DATA_LINES]). Without doing this, you may hit into 'segmentation fault' error, if you do not, you are lucky.
//2: Just to ensure that if the total number of lines in the file are < MAX_DATA_LINES. You do not run into 'segmentation fault' error, because the memory storage for pointer to line data[>MAX_DATA_LINES] is no more valid.
I think that this might be a quicker implementation as you won't have to copy the contents of all the strings from the bytes array to a secondary array. You will of course lose your '\n' characters though.
It also takes into account files that don't end with a new line character and as pos is defined as long the array index used for bytes[] and also the length should be long.
#include <stdio.h>
#include <stdlib.h>
#define DEFAULT_LINE_ARRAY_DIM 100
int main(int argc, char* argv[])
{
FILE *f = fopen("test.c", "rb");
fseek(f, 0, SEEK_END);
long pos = ftell(f);
fseek(f, 0, SEEK_SET);
char *bytes = malloc(pos+1); /* include an extra byte incase file isn't '\n' terminated */
fread(bytes, pos, 1, f);
if (bytes[pos-1]!='\n')
{
bytes[pos++] = '\n';
}
long i;
long length = 0;
int counter = 0;
size_t size=DEFAULT_LINE_ARRAY_DIM;
char** data=malloc(size*sizeof(char*));
data[0]=bytes;
for(i=0; i<pos; i++)
{
if (bytes[i]=='\n') {
bytes[i]='\0';
counter++;
if (counter>=size) {
size+=DEFAULT_LINE_ARRAY_DIM;
data=realloc(data,size*sizeof(char*));
if (data==NULL) {
fprintf(stderr,"Couldn't allocate enough memory!\n");
exit(1);
}
}
data[counter]=&bytes[i+1];
length = data[counter] - data[counter - 1] - 1;
printf("%d\n", counter);
printf("%ld\n", length);
}
}
for (i=0;i<counter;i++)
printf("%s\n", data[i]);
return 0;
}

Resources