I'm trying to built a NULL-terminate array of stucts
Here is the code: lzdata.c
#include <stdlib.h>
#include <stdio.h>
#include "nist.h"
int main(int argc,char *argv[])
{
nist_t *nist; /* NIST data */
nist=readnist();
}
The file nist.c
#include <stdlib.h>
#include <stdio.h>
#include "nist.h"
nist_t *readnist()
{
nist_t *nist; /* NIST data */
char line[50];
int len=50;
int i=0;
nist=(nist_t*)malloc(sizeof(nist_t));
while(fgets(line,len,stdin))
{
nist=(nist_t*)realloc(nist,sizeof(nist_t)*(i+1));
sscanf(line,"%s %s %f %lf",nist[i].config,nist[i].term,&(nist[i].j),&(nist[i].level));
++i;
}
nist=(nist_t*)realloc(nist,sizeof(nist_t)*(i+1));
nist[i]=(nist_t)NULL;
return nist;
}
The header file nist.h
:
#ifndef NIST_H
#define NIST_H
typedef struct
{
char config[3];
char term[4];
float j;
double level;
} nist_t;
nist_t *readnist();
#endif
The data file, which will be fed to the application via STDIN:
2s ¹S 0.0 0.000000
2p ³P° 1.0 142075.333333
2p ¹P° 0.0 271687.000000
2p ³P 1.0 367448.333333
2p ¹D 0.0 405100.000000
2p ¹S 0.0 499633.000000
3s ³S 0.0 1532450.000000
3s ¹S 0.0 1558080.000000
3p ¹P° 0.0 1593600.000000
3p ³P° 1.0 1597500.000000
3d ³D 1.0 1631176.666667
3d ¹D 0.0 1654580.000000
3s ³P° 1.0 1711763.333333
3s ¹P° 0.0 1743040.000000
3p ³D 1.0 1756970.000000
3p ³S 0.0 1770380.000000
3p ³P 0.5 1779340.000000
3p ¹D 0.0 1795870.000000
3d ³P° 1.0 1816053.333333
3d ¹F° 0.0 1834690.000000
3d ¹P° 0.0 1841560.000000
...
...
When I compile:
$ cc -O2 -o lzdata lzdata.c nist.c
nist.c: In function ‘readnist’:
nist.c:24:2: error: conversion to non-scalar type requested
I've tried changing the line nist[i]=(nist_t)NULL;
to nist[i]=(nist_t*)NULL;
and I got:
$ cc -O2 -o lzdata lzdata.c nist.c
nist.c: In function ‘readnist’:
nist.c:24:9: error: incompatible types when assigning to type ‘nist_t’ from type ‘struct nist_t *’
I've tried changing the line nist[i]=(nist_t)NULL;
to nist[i]=NULL;
and I got:
$ cc -O2 -o lzdata lzdata.c nist.c
nist.c: In function ‘readnist’:
nist.c:24:9: error: incompatible types when assigning to type ‘nist_t’ from type ‘void *’
There could be a different number of lines in different data files. I'm seeking to build a NULL-terminated array of nist_t
data, so I can process it until I get to the NULL element. Is this possible?
For your compiler the macro NULL
seems to be defined as ((void *) 0)
, that is a generic pointer to zero. Since nist[i]
(for any valid value of i
) is not a pointer you get the errors.
The best way to solve this might be to either pass the pointer from main
by reference into the function and use that, and return the size. Or by passing an integer by reference, and set it to the size.
There is another solution too, and that is to have an array of pointers. Then you need to allocate each nist_t
structure separately, and free them all too. Then you can use NULL
to indicate the end of the array. This is actually how argv
works, it's terminated by a NULL
pointer (so argv[argc]
is always equal to NULL
).
You have an array of structures. NULL
is a null pointer constant. There is no null value for a structure type -- unless you define some distinguished value yourself.
For example, you could modify your nist_t
type so it has a member that indicates whether the entire value is valid or not:
#include <stdbool.h>
typedef struct
{
bool is_valid;
char config[3];
char term[4];
float j;
double level;
} nist_t;
and then mark the end of the array with a nist_t
element with is_valid
set to false
. Or perhaps you can use some invalid value for one of the other members.
This means you'll need to be careful with any code you write that deals with these structures to ensure that is_valid
is initialized correctly, and that you don't try to use the other member values with is_valid
is false
.
Or you can build an array if nist_t*
pointers, setting the terminating element to a null pointer type assigning NULL
to it.
But it might be simpler just to keep track of the number of valid elements in your array of nist_t
objects.
The particular way that NULL
is defined is not very important here. All that really matters is that it's a null pointer constant. Assigning NULL
to a pointer object makes it a null pointer; assigning it to any non-pointer is invalid (though there are cases where the compiler, unfortunately, might let you get away with it).
This works:
nist_t *readnist()
{
nist_t *nist=NULL; /* NIST data */
char line[50]; /* A single line of NIST data */
char config[10]; /* nl quantum numbers */
char term[10]; /* Rotational term */
float j;
double level; /* energy level */
int len=50; /* default length of string */
int i=0; /* counter/index variable */
int n; /* principal quantum number */
char l; /* azimuthal quantum number */
int en=0; /* error number */
while(fgets(line,len,stdin))
{
(void)sscanf(line,"%s\t%s\t%f\t%lf",config,term,&j,&level);
if(NULL==(nist=(nist_t*)realloc(nist,sizeof(nist_t)*(i+1))))
{
en=errno;
fprintf(stderr,"Error %d: memory allocation failure\n",en);
fprintf(stderr,"File:\t\t%s\n",__FILE__);
fprintf(stderr,"Function:\t%s\n",__FUNCTION__);
fprintf(stderr,"Line:\t\t%d\n",__LINE__-6);
exit(en);
};
nist[i].config=(char*)malloc(sizeof(char)*(strlen(config)+1));
nist[i].term=(char*)malloc(sizeof(char)*(strlen(term)+1));
strcpy(nist[i].config,config);
sscanf(config,"%d%c",&n,&l);
nist[i].n=n;
strcpy(nist[i].term,term);
nist[i].j=j;
nist[i].level=level;
++i;
}
nist=(nist_t*)realloc(nist,sizeof(nist_t)*(i+1));
nist[i].config=NULL;
nist[i].term=NULL;
nist[i].j=0.0;
nist[i].level=0.0;
return nist;
}
And here is a routine I used to test it:
nist_t *prtnist(nist_t *nist)
{
int i=0;
while((NULL!=nist[i].config) && (NULL!=nist[i].term))
{
fprintf(stderr,"%s\t%d\t%s\t%4.1f\t%14.8e\n",nist[i].config,nist[i].n,nist[i].term,nist[i].j,nist[i].level);
++i;
}
return nist;
}
Now I've got to figure a way to get the azimuthal quantum number out of the config
string.