Question about a pointer to an array of structs

K

Kieran Simkin

Hi,
I wonder if anyone can help me, I've been headscratching for a few hours
over this.

Basically, I've defined a struct called cache_object:
struct cache_object {
char hostname[HOSTSIZE+1];
char ipaddr[IPSIZE+1];
};

I plan to store an array of these which'll contain a cached list of
hostnames and IP addresses for a resolver part of my code.

To complicate matters, the array has to be available to child processes via
shm. Now, I'm pretty new to shm so I'm leaving that bit till last. Anyway,
in order to work with shm I need a pointer to an array of my struct
"cache_object". I've defined my pointer like this:

struct cache_object (*cache)[CACHESIZE];

HOSTNAME/IPSIZE/CACHESIZE are all #defined with integer values.

Now before I start work on the shm code I'm testing things with malloc()
because malloc will return a pointer to a piece of memory in the same way as
shmat(). (actually I wrote a load of shm code, couldn't get it to work and
removed it all in an attempt to get back to the root of the problem).

The problem I'm having, and this happened with my shm code as well was that
I can't seem to figure out how much memory I should be allowing myself to
store this array of structs. Here's the code I've got:

cache = malloc(sizeof(struct cache_object[CACHESIZE]));
as a sidenote, what should I typecast this to? I've tried:
cache = (struct *cache_object[CACHESIZE]) malloc(sizeof(struct
cache_object[CACHESIZE]));

but I get "syntax error before '*' token" from gcc.. I've tried:
cache = (struct cache_object[CACHESIZE]) malloc(sizeof(struct
cache_object[CACHESIZE]));
but I get "cast specifies array type"

Anyway, sizeof() should return the number of bytes I need to allocate in
order to store a CACHESIZE sized array of cache_object structs, correct?
Well, I thought so..

Running the following loop:

for (c=0;c<CACHESIZE;c++) {
printf("cache: %i ipaddr: %s hostname:
%s\n",c,cache[c]->ipaddr,cache[c]->hostname);
}

With these #defines:
#define HOSTSIZE 255
#define IPSIZE 255
#define CACHESIZE 10

Produces the following output:

cache: 0 ipaddr: hostname:
cache: 1 ipaddr: hostname:
cache: 2 ipaddr: hostname:
cache: 3 ipaddr: hostname:
Segmentation fault (core dumped)

Strangely, decreasing CACHESIZE to 5 prevents the segfault and the program
runs as expected:

$ ./caching_logger
foo 127.0.0.1 foo
cache: 0 ipaddr: hostname:
cache: 1 ipaddr: hostname:
cache: 2 ipaddr: hostname:
cache: 3 ipaddr: hostname:
cache: 4 ipaddr: hostname:

Any help with this would be very greatly appreciated, it's been driving me
nuts all day.

Incase anyone's interested, the code I'm working on sits on the end of a log
pipe from apache splitting out log lines for each vhost and logging them to
separate files, it also resolves IP addresses in the log lines to dns wait
time in apache itself. The main purpose of the program however is to
overcome apache's open file descriptor limit by requiring only one pipe to
this program - the program itself closes and re-opens each log file between
writes; less efficient, but necessary in the situation I run.

Below is the code I have so far (fork commented out to remove the need for
shm):

#include <stdlib.h>
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/socket.h>
#include <signal.h>

#define HOSTSIZE 255
#define IPSIZE 255
#define CACHESIZE 10

struct cache_object {
char hostname[HOSTSIZE+1];
char ipaddr[IPSIZE+1];
};

struct cache_object (*cache)[CACHESIZE];
int *cachecount;

int main () {
char vhost[HOSTSIZE+1], ip[IPSIZE], line[2046], path[255],
finalhost[HOSTSIZE+1];
FILE *outstream;
struct in_addr iph;
struct hostent *hp;
int c;
signal(SIGCHLD, SIG_IGN);

cache = malloc(sizeof(struct cache_object[CACHESIZE]));
cachecount = (int *) malloc(sizeof(int));
*cachecount=0;
while (!feof(stdin)) {
memset(vhost,0,255);
memset(ip,0,255);
memset(line,0,2046);
memset(path,0,255);
memset(finalhost,0,255);
fscanf(stdin, "%s %s ", vhost, ip);
fgets(line, 2046, stdin);
for (c=0;c<CACHESIZE;c++) {
printf("cache: %i ipaddr: %s hostname:
%s\n",c,cache[c]->ipaddr,cache[c]->hostname);
}
// if (fork() == 0) {
sprintf(path,"/home/logs/access_logs/%s",vhost);
if (inet_aton(ip, &iph)) {
if ((hp=gethostbyaddr((const char *)&iph,
sizeof(struct in_addr), AF_INET)) != NULL) {
sprintf(finalhost,"%s",hp->h_name);
} else {
sprintf(finalhost,"%s",ip);
}
} else {
sprintf(finalhost,"%s",ip);
}
sprintf(cache[*cachecount]->ipaddr,"%s",ip);

sprintf(cache[*cachecount]->hostname,"%s",finalhost);
if (*cachecount < CACHESIZE) {
*cachecount = *cachecount + 1;
} else {
*cachecount=0;
}
outstream = fopen(path, "a");
if (outstream != NULL) {
fprintf(outstream,"%s %s",finalhost, line);
fclose(outstream);
}
// exit(0);
// }

}
exit(0);
}




--


~Kieran Simkin
Digital Crocus
http://digital-crocus.com/
 
M

Mike Wahler

Anyway,
in order to work with shm I need a pointer to an array of my struct
"cache_object". I've defined my pointer like this:

struct cache_object (*cache)[CACHESIZE];

OK so far.
HOSTNAME/IPSIZE/CACHESIZE are all #defined with integer values.

Now before I start work on the shm code I'm testing things with malloc()
because malloc will return a pointer to a piece of memory in the same way as
shmat(). (actually I wrote a load of shm code, couldn't get it to work and
removed it all in an attempt to get back to the root of the problem).

The problem I'm having, and this happened with my shm code as well was that
I can't seem to figure out how much memory I should be allowing myself to
store this array of structs. Here's the code I've got:

cache = malloc(sizeof(struct cache_object[CACHESIZE]));

cache = malloc(sizeof *cache);

-Mike
as a sidenote, what should I typecast this to?

Don't cast the return value from malloc(). Just
make sure you've #included <stdlib.h> for malloc()'s
prototype.

-Mike
 
C

Chris Torek

struct cache_object (*cache)[CACHESIZE];

Note that the only real use for this kind of pointer is:

cache = malloc(n * sizeof *cache);
if (cache == NULL) ... handle error ...
... now work with cache[j], where i is in [0..n) and
j is in [0..CACHESIZE) ...

In other words, this pointer points to the first of n arrays,
so that cache is an array. Each such array -- each cache
-- has CACHESIZE elements, and each cache[j] is a "struct
cache_object".

If you want each cache to be a "struct cache_object", rather
than an array (of size CACHESIZE) of such objects, just use:

struct cache_object *cache;

You can then allocate n of them (whether n is CACHESIZE or any
other value) with the exact same allocation pattern:

cache = malloc(n * sizeof *cache);

Calls to malloc() should almost always look just like that -- the
only change might be the variable name, or you might omit "n" if
n is exactly 1:

p = malloc(sizeof *p);
q = malloc(3 * sizeof *q);
*r = malloc(sizeof **r);

There is always one more "*" on the "sizeof" than there is on the
left, so since the last one sets *r (not r) it needs sizeof **r
(not sizeof *r).
#include <sys/types.h>
#include <netinet/in.h>

This code is rather OS-dependent, but there are other "pure C" issues
here. The one that immediately leaped out at me is:
while (!feof(stdin)) {
[code that reads from stdin]

A "while (!feof(...))" loop is almost always wrong. The feof()
function does not even attempt to predict whether EOF will occur
in the future when you try to read. It only "post-dicts" whether
EOF has *already happened*. It does not forecast the earthquake;
it tells you whether the damage you see, upon discovering a problem,
was due to an earthquake.

To read input from anything -- a person (via an "interactive"
device), or a network connection, or a local disk file -- you should
just try to read. If the read fails, *then* you can ask: "did
that read just fail because of EOF?" That is what feof() will tell
you. If the read has not yet failed, do not ask why it failed;
feof() cannot possibly tell you.
 
B

Barry Schwarz

Hi,
I wonder if anyone can help me, I've been headscratching for a few hours
over this.

Basically, I've defined a struct called cache_object:
struct cache_object {
char hostname[HOSTSIZE+1];
char ipaddr[IPSIZE+1];
};

I plan to store an array of these which'll contain a cached list of
hostnames and IP addresses for a resolver part of my code.

To complicate matters, the array has to be available to child processes via
shm. Now, I'm pretty new to shm so I'm leaving that bit till last. Anyway,
in order to work with shm I need a pointer to an array of my struct
"cache_object". I've defined my pointer like this:

struct cache_object (*cache)[CACHESIZE];

This does indeed define a pointer to an array of CACHESIZE struct
chache_object. However, based on your code below, what you really
want is a pointer to a single struct which just happens to be the
first of an array of such struct. The obvious way to do this is
struct chache_object *cache;
HOSTNAME/IPSIZE/CACHESIZE are all #defined with integer values.

Now before I start work on the shm code I'm testing things with malloc()
because malloc will return a pointer to a piece of memory in the same way as
shmat(). (actually I wrote a load of shm code, couldn't get it to work and
removed it all in an attempt to get back to the root of the problem).

The problem I'm having, and this happened with my shm code as well was that
I can't seem to figure out how much memory I should be allowing myself to
store this array of structs. Here's the code I've got:

cache = malloc(sizeof(struct cache_object[CACHESIZE]));
as a sidenote, what should I typecast this to? I've tried:
cache = (struct *cache_object[CACHESIZE]) malloc(sizeof(struct
cache_object[CACHESIZE]));

but I get "syntax error before '*' token" from gcc.. I've tried:
cache = (struct cache_object[CACHESIZE]) malloc(sizeof(struct
cache_object[CACHESIZE]));
but I get "cast specifies array type"

You should very rarely ever need to cast the return from malloc and
this is not one of those times. If you included stdlib.h the compiler
will know that malloc returns a void* and be perfectly capable of
assigning that value to any object pointer variable you want.

While what you have in the first line will work, the recommended
approach is
cache = malloc(sizeof *cache);

If you change the definition of cache as I've recommended, then you
would need
cache = malloc(CACHESIZE * sizeof *cache);
Anyway, sizeof() should return the number of bytes I need to allocate in
order to store a CACHESIZE sized array of cache_object structs, correct?
Well, I thought so..

You thought correctly.
Running the following loop:

for (c=0;c<CACHESIZE;c++) {
printf("cache: %i ipaddr: %s hostname:
%s\n",c,cache[c]->ipaddr,cache[c]->hostname);

Here is your problem.

cache is a pointer to an array of struct. Therefore cache[0] is
the array of struct itself. cache[1] would be a second array of
struct immediately following the first BUT you do not have such an
array. It only gets worse as you go up to cache[9]. All these
references after cache[0] attempt to evaluate to arrays that don't
exist. An overdose of undefined behavior.

As an aside, the reason this does not produce a syntax error is
that in most contexts the evaluation of an array expression (a
reference to an array without the necessary subscripts to specify a
particular element of the array) produces the address of the first
element of the array with type pointer to element. Therefore the
expression cache[c]->ipaddr evaluates exactly the same as
(&cache[c][0])->ipaddr. cache is a pointer to the array, cache[c] is
the array, cache[c][0] is the first element of the array and
&cache[c][0] is the address of the first element with type pointer to
struct. This is the correct type to appear to the left of the ->
operator.

If you change cache as recommended, your for loop will work as you
intend.

If you really want to leave cache as a pointer to an array, your
printf arguments should look like
printf(..., (*cache)[c].ipaddr,(*cache)[c].hostname);
cache is the pointer to the array, *cache is the array, (*cache)[c] is
the c-th element of the array with type struct, and the .ipaddr
selects the member of the struct.
}

With these #defines:
#define HOSTSIZE 255
#define IPSIZE 255
#define CACHESIZE 10

Produces the following output:

cache: 0 ipaddr: hostname:
cache: 1 ipaddr: hostname:
cache: 2 ipaddr: hostname:
cache: 3 ipaddr: hostname:
Segmentation fault (core dumped)

Strangely, decreasing CACHESIZE to 5 prevents the segfault and the program
runs as expected:

One of the more insidious manifestations of undefined behavior is to
appear as if everything is working properly.
$ ./caching_logger
foo 127.0.0.1 foo
cache: 0 ipaddr: hostname:
cache: 1 ipaddr: hostname:
cache: 2 ipaddr: hostname:
cache: 3 ipaddr: hostname:
cache: 4 ipaddr: hostname:

Any help with this would be very greatly appreciated, it's been driving me
nuts all day.

snip code with no additional detail.


<<Remove the del for email>>
 
A

Al Bowers

Kieran said:
Hi,
I wonder if anyone can help me, I've been headscratching for a few hours
over this.

Basically, I've defined a struct called cache_object:
struct cache_object {
char hostname[HOSTSIZE+1];
char ipaddr[IPSIZE+1];
};

I plan to store an array of these which'll contain a cached list of
hostnames and IP addresses for a resolver part of my code.

To complicate matters, the array has to be available to child processes via
shm. Now, I'm pretty new to shm so I'm leaving that bit till last. Anyway,
in order to work with shm I need a pointer to an array of my struct
"cache_object". I've defined my pointer like this:

struct cache_object (*cache)[CACHESIZE];

I believe you want:
struct cache_object *cache;
The problem I'm having, and this happened with my shm code as well was that
I can't seem to figure out how much memory I should be allowing myself to
store this array of structs. Here's the code I've got:

cache = malloc(sizeof(struct cache_object[CACHESIZE]));
as a sidenote, what should I typecast this to? I've tried:
cache = (struct *cache_object[CACHESIZE]) malloc(sizeof(struct
cache_object[CACHESIZE]));

but I get "syntax error before '*' token" from gcc.. I've tried:
cache = (struct cache_object[CACHESIZE]) malloc(sizeof(struct
cache_object[CACHESIZE]));
but I get "cast specifies array type"
You would probably want:
cache = malloc(CACHESIZE * sizeof(struct cache_object));
or
cache = malloc(CACHESIZE * (sizeof *cache));


Instead of one huge block allocation, perhaps you should dynamic
allocate the array in smaller blocks and increase the allocations
as you need. You can define a struct with a member representing
the array pointer and another member representing the number of
elements in the array. You can write functions to maintain the
struct object.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define HOSTSIZE 32
#define IPSIZE 16
#define BLOCK 10

struct cache_object
{
char hostname[HOSTSIZE+1];
char ipaddr[IPSIZE+1];
};

struct cache_obj_arr
{
struct cache_object *array;
size_t count;
};

int addCache(struct cache_obj_arr *p, const char *hname,
const char *ip);
int createCacheArray(struct cache_obj_arr *p, size_t nelement);
void freeCache(struct cache_obj_arr *p);

int main(void)
{
struct cache_obj_arr cache = {NULL};

puts("Attempting to create an array of 12 cache_obj...\n");
if(createCacheArray(&cache, 12))
{
printf("A cache_obj array of %u elements has been created\n",
cache.count);
puts("cache.array points to the array.\n"
"cache.count keeps a count of the number of elements");
freeCache(&cache);
puts("The array was freed(deallocated)\n");
}
else puts("Failure in creating the array");
puts("A new array has been started");
addCache(&cache,"tom.harry.com","192.168.42.42");
printf("cache.array[0].hostname = \"%s\"\n"
"cahce.array[0].ipaddr = \"%s\"\n",
cache.array[0].hostname,cache.array[0].ipaddr);
freeCache(&cache);
return 0;
}

int addCache(struct cache_obj_arr *p, const char *hname,
const char *ip)
{
struct cache_object *tmp;

if(p->count%BLOCK == 0)
{
tmp = realloc(p->array,(p->count+BLOCK)*(sizeof *tmp));
if(tmp == NULL) return 0;
p->array = tmp;
}
strncpy(p->array[p->count].hostname,hname,HOSTSIZE);
p->array[p->count].hostname[HOSTSIZE] = '\0';
strncpy(p->array[p->count].ipaddr,ip,IPSIZE);
p->array[p->count++].ipaddr[IPSIZE] = '\0';
return 1;
}

int createCacheArray(struct cache_obj_arr *p, size_t nelement)
{
size_t cnt;

for(cnt = 0; cnt < nelement;cnt++)
if(!addCache(p,"","")) return 0;
return 1;
}

void freeCache(struct cache_obj_arr *p)
{
free(p->array);
p->array = NULL;
p->count = 0;
return;
}
 
K

Kieran Simkin

Great, thanks everybody. Got it working perfectly and I think I understand
pointers a little better now.
Interesting about casting the return value of malloc().. I've always thought
it was "good practice" to do this..

Again, thanks for your help.


~Kieran Simkin
Digital Crocus
http://digital-crocus.com/

Kieran Simkin said:
Hi,
I wonder if anyone can help me, I've been headscratching for a few hours
over this.

Basically, I've defined a struct called cache_object:
struct cache_object {
char hostname[HOSTSIZE+1];
char ipaddr[IPSIZE+1];
};

I plan to store an array of these which'll contain a cached list of
hostnames and IP addresses for a resolver part of my code.

To complicate matters, the array has to be available to child processes via
shm. Now, I'm pretty new to shm so I'm leaving that bit till last. Anyway,
in order to work with shm I need a pointer to an array of my struct
"cache_object". I've defined my pointer like this:

struct cache_object (*cache)[CACHESIZE];

HOSTNAME/IPSIZE/CACHESIZE are all #defined with integer values.

Now before I start work on the shm code I'm testing things with malloc()
because malloc will return a pointer to a piece of memory in the same way as
shmat(). (actually I wrote a load of shm code, couldn't get it to work and
removed it all in an attempt to get back to the root of the problem).

The problem I'm having, and this happened with my shm code as well was that
I can't seem to figure out how much memory I should be allowing myself to
store this array of structs. Here's the code I've got:

cache = malloc(sizeof(struct cache_object[CACHESIZE]));
as a sidenote, what should I typecast this to? I've tried:
cache = (struct *cache_object[CACHESIZE]) malloc(sizeof(struct
cache_object[CACHESIZE]));

but I get "syntax error before '*' token" from gcc.. I've tried:
cache = (struct cache_object[CACHESIZE]) malloc(sizeof(struct
cache_object[CACHESIZE]));
but I get "cast specifies array type"

Anyway, sizeof() should return the number of bytes I need to allocate in
order to store a CACHESIZE sized array of cache_object structs, correct?
Well, I thought so..

Running the following loop:

for (c=0;c<CACHESIZE;c++) {
printf("cache: %i ipaddr: %s hostname:
%s\n",c,cache[c]->ipaddr,cache[c]->hostname);
}

With these #defines:
#define HOSTSIZE 255
#define IPSIZE 255
#define CACHESIZE 10

Produces the following output:

cache: 0 ipaddr: hostname:
cache: 1 ipaddr: hostname:
cache: 2 ipaddr: hostname:
cache: 3 ipaddr: hostname:
Segmentation fault (core dumped)

Strangely, decreasing CACHESIZE to 5 prevents the segfault and the program
runs as expected:

$ ./caching_logger
foo 127.0.0.1 foo
cache: 0 ipaddr: hostname:
cache: 1 ipaddr: hostname:
cache: 2 ipaddr: hostname:
cache: 3 ipaddr: hostname:
cache: 4 ipaddr: hostname:

Any help with this would be very greatly appreciated, it's been driving me
nuts all day.

Incase anyone's interested, the code I'm working on sits on the end of a log
pipe from apache splitting out log lines for each vhost and logging them to
separate files, it also resolves IP addresses in the log lines to dns wait
time in apache itself. The main purpose of the program however is to
overcome apache's open file descriptor limit by requiring only one pipe to
this program - the program itself closes and re-opens each log file between
writes; less efficient, but necessary in the situation I run.

Below is the code I have so far (fork commented out to remove the need for
shm):

#include <stdlib.h>
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/socket.h>
#include <signal.h>

#define HOSTSIZE 255
#define IPSIZE 255
#define CACHESIZE 10

struct cache_object {
char hostname[HOSTSIZE+1];
char ipaddr[IPSIZE+1];
};

struct cache_object (*cache)[CACHESIZE];
int *cachecount;

int main () {
char vhost[HOSTSIZE+1], ip[IPSIZE], line[2046], path[255],
finalhost[HOSTSIZE+1];
FILE *outstream;
struct in_addr iph;
struct hostent *hp;
int c;
signal(SIGCHLD, SIG_IGN);

cache = malloc(sizeof(struct cache_object[CACHESIZE]));
cachecount = (int *) malloc(sizeof(int));
*cachecount=0;
while (!feof(stdin)) {
memset(vhost,0,255);
memset(ip,0,255);
memset(line,0,2046);
memset(path,0,255);
memset(finalhost,0,255);
fscanf(stdin, "%s %s ", vhost, ip);
fgets(line, 2046, stdin);
for (c=0;c<CACHESIZE;c++) {
printf("cache: %i ipaddr: %s hostname:
%s\n",c,cache[c]->ipaddr,cache[c]->hostname);
}
// if (fork() == 0) {
sprintf(path,"/home/logs/access_logs/%s",vhost);
if (inet_aton(ip, &iph)) {
if ((hp=gethostbyaddr((const char *)&iph,
sizeof(struct in_addr), AF_INET)) != NULL) {
sprintf(finalhost,"%s",hp->h_name);
} else {
sprintf(finalhost,"%s",ip);
}
} else {
sprintf(finalhost,"%s",ip);
}
sprintf(cache[*cachecount]->ipaddr,"%s",ip);

sprintf(cache[*cachecount]->hostname,"%s",finalhost);
if (*cachecount < CACHESIZE) {
*cachecount = *cachecount + 1;
} else {
*cachecount=0;
}
outstream = fopen(path, "a");
if (outstream != NULL) {
fprintf(outstream,"%s %s",finalhost, line);
fclose(outstream);
}
// exit(0);
// }

}
exit(0);
}




--


~Kieran Simkin
Digital Crocus
http://digital-crocus.com/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top