Discussion:
is it possible to point to a slice of an array without malloc or VLAs?
(too old to reply)
Mark Summerfield
2024-08-28 07:06:43 UTC
Permalink
I'm using getopt_long() to process a command line.
So after a typical call I might have:

argv == {"./efind", "-D", "-x", "one", "two", "three", "four"}
optind == 3

What I'd like to do (without copying or mallocing and without using a VLA)
is to get a pointer to a slice of argv, specifically,
{"one", "two", "three", "four"}.
In Python terms argv[optind:argc].

Is this possible in C?

At the moment I store argv, optind, and argc and handle the slice using a loop:

if (config->optind < config->argc)
for (int i = config->optind; i < config.argc; ++i)
process(config->argv[i]);

This works fine, so really I'm just wondering if there's a nicer way.
Phil Ashby
2024-08-28 08:13:39 UTC
Permalink
I'm using getopt_long() to process a command line. So after a typical
argv == {"./efind", "-D", "-x", "one", "two", "three", "four"} optind
== 3
What I'd like to do (without copying or mallocing and without using a
VLA) is to get a pointer to a slice of argv, specifically, {"one",
"two", "three", "four"}. In Python terms argv[optind:argc].
Is this possible in C?
To answer the specific question, I would use pointer arithmetic,
provided there is no intention to modify values, ie:

char **slice = argv + config->optind;

thus slice now points at the appropriate part of argv and can be indexed
or dereferenced / incremented to access elements.
if (config->optind < config->argc) for (int i = config->optind; i <
config.argc; ++i) process(config->argv[i]);
This works fine, so really I'm just wondering if there's a nicer way.
I don't think so, other than to drop the outer check as the loop
condition provides the same boundary.

Phil.
Mark Summerfield
2024-08-29 07:52:02 UTC
Permalink
On Wed, 28 Aug 2024 09:13:39 +0100, Phil Ashby wrote:
[snip]
Post by Phil Ashby
To answer the specific question, I would use pointer arithmetic,
char **slice = argv + config->optind;
thus slice now points at the appropriate part of argv and can be indexed
or dereferenced / incremented to access elements.
[snip]

Thank you, that works great. I now do that plus store the slice's size using:
argc - optind

I tried to get the size using

#define ARRAYSIZE(a) (&(a)[1] - (a))

char **folders = argv + optind;
int num_folders = argc - optind;
int size = ARRAYSIZE(folders);
printf("%d %d\n", num_folders, size);

but size was always 1.
Ben Bacarisse
2024-08-29 10:04:01 UTC
Permalink
Post by Mark Summerfield
[snip]
Post by Phil Ashby
To answer the specific question, I would use pointer arithmetic,
char **slice = argv + config->optind;
thus slice now points at the appropriate part of argv and can be indexed
or dereferenced / incremented to access elements.
[snip]
argc - optind
I tried to get the size using
#define ARRAYSIZE(a) (&(a)[1] - (a))
The difference between a pointer to the second element of an array and a
pointer to the first will always be 1.

Another way to look at this is to expand the expression.

a[1] means *(a+1)
&a[1] means &*(a+1)
&*(a+1) is the same as a+1 (barring some details; see 6.5.3.2 p3)
&a[1] - a means a+1 - a or just 1
Post by Mark Summerfield
char **folders = argv + optind;
int num_folders = argc - optind;
int size = ARRAYSIZE(folders);
printf("%d %d\n", num_folders, size);
but size was always 1.
Another fact that might help you is that the argv array will have a null
pointer as it's last element so you may not need to remember the length
of the slice since it is the tail slice of a null-terminated array.
--
Ben.
Stefan Ram
2024-08-28 09:50:54 UTC
Permalink
Post by Mark Summerfield
argv == {"./efind", "-D", "-x", "one", "two", "three", "four"}
. . .
Post by Mark Summerfield
In Python terms argv[optind:argc].
#include <stdio.h>

int main()
{ char const * const argv[] =
{ "./efind", "-D", "-x", "one", "two", "three", "four" };

size_t const argc = sizeof( argv )/ sizeof( 0[ argv ]);
printf( "%zu\n", argc );

char const * const * rest = argv + 3; /* <<< answer to the question */

for( char const * const * entry = rest; entry < argv + argc; entry++ )
printf( "%.32s\n", *entry ); }

If that's not your jam, you can pretty much 86 all the "const"
declarations in the program above. But if you're rolling
in from Python, where "const" is ghost, it's hella tight
to be able to use "const" again. You'd be missing out on the
gnarliest feature in C!

In Python, slices are always copied. Here in the C program
above, the "slice" shares memory with the base array.
(There's no such thing as "slices" in C.)
Blue-Maned_Hawk
2024-08-28 14:25:59 UTC
Permalink
Post by Mark Summerfield
I'm using getopt_long() to process a command line.
argv == {"./efind", "-D", "-x", "one", "two", "three", "four"}
optind == 3
What I'd like to do (without copying or mallocing and without using a VLA)
is to get a pointer to a slice of argv, specifically,
{"one", "two", "three", "four"}.
In Python terms argv[optind:argc].
Is this possible in C?
Yes.

const char * argv_slice[argc - optind] = &argv[optind];
--
Blue-Maned_Hawk│shortens to Hawk│/blu.mɛin.dʰak/│he/him/his/himself/Mr.
blue-maned_hawk.srht.site
This subroutine must not be called from any thread.
Ben Bacarisse
2024-08-28 15:29:58 UTC
Permalink
Post by Blue-Maned_Hawk
Post by Mark Summerfield
I'm using getopt_long() to process a command line.
argv == {"./efind", "-D", "-x", "one", "two", "three", "four"}
optind == 3
What I'd like to do (without copying or mallocing and without using a VLA)
is to get a pointer to a slice of argv, specifically,
{"one", "two", "three", "four"}.
In Python terms argv[optind:argc].
Is this possible in C?
Yes.
const char * argv_slice[argc - optind] = &argv[optind];
First, the OP did not want to use a VLA and, second, that initialisation
is invalid. Third, if a VLA is used, the pointers would be copied and
the OP did not want that either.
--
Ben.
Tim Rentsch
2024-08-28 15:33:32 UTC
Permalink
Post by Blue-Maned_Hawk
Post by Mark Summerfield
I'm using getopt_long() to process a command line.
argv == {"./efind", "-D", "-x", "one", "two", "three", "four"}
optind == 3
What I'd like to do (without copying or mallocing and without
using a VLA)
is to get a pointer to a slice of argv, specifically,
{"one", "two", "three", "four"}.
In Python terms argv[optind:argc].
Is this possible in C?
Yes.
const char * argv_slice[argc - optind] = &argv[optind];
Presumably the declaration that was intended is something more
like

const char *(*argv_slice)[argc - optind] = (void*) &argv[optind];

It should be noted that this solution satisfies the stated
requirement of not using a VLA, but it does rely on using
a variably modified type.
Loading...