Given the following code, my program compiles and runs successfully when I pass arguments into a function that takes a triple char pointer:
char *first[] = { "man", "1", "man", NULL };
char *second[] = { "cat", NULL };
char *third[] = { "wc", NULL };
char *fourth[] = { "cat", "-e", NULL };
char **arguments[] = { first, second, third, fourth, NULL };
I am trying to change arguments to support a variable number of string arrays. For now, I have changed arguments to a triple char pointer with a hard-coded size of 7, and each element has the same hard-coded value:
int count = 7;
int s = sizeof(char **);
char ***arguments = malloc(s * count);
for (size_t i = 0; i < count; i++) {
arguments[i] = { "cat", "file.txt", NULL };
}
However, when I attempt to pass arguments into a function that takes a triple char pointer, this time the program fails to compile. I get the error message
error: expected expression before ‘{’ token
48 | arguments[i] = { "cat", "file.txt", NULL };
With the opening curly bracket before "cat" highlighted in red. At first, I thought the error occurred because I was trying to treat a pointer like an array, but pointer arithmetic isn't working either. I get the same error when I replace the line inside the for loop with
*(arguments + i) = { "cat", "file.txt", NULL };
Could someone please tell me the right way to do this? I have successfully iterated through pointers before, but never anything this complicated. For context, this is part of a larger project to build a shell program that takes piped commands. I found a resource online for how to pipe multiple hard-coded commands, but I am trying to do what I have just explained so that I can make it take user input.
It depends what you mean by variable number of arguments.
If you want to construct an array of pointers to arrays of strings and initialize that to variable contents coming from a file, the user or some other dynamic source, you should use malloc and possibly realloc for all of these arrays and handle their lifetimes explicitly.
If you want to construct an array of command locally from a list of initializers known at compile time, but not necessarily fixed, you can use compound literals as shown below, but make sure you use the array and its elements only within the scope of the definition:
int main() {
char **arguments[] = {
(char *[]){ "man", "1", "man", NULL },
(char *[]){ "cat", NULL },
(char *[]){ "wc", NULL },
(char *[]){ "cat", "-e", NULL },
#ifdef BSD
(char *[]){ "ifconfig", NULL },
#endif
};
size_t n = sizeof(arguments) / sizeof(arguments[0]);
for (size_t i = 0; i < n; i++) {
run_command(arguments[i]);
}
return 0;
}
For a simpler and more portable approach without C99 specific constructions, you can use a 2D array:
int main() {
char *arguments[][4] = {
{ "man", "1", "man", NULL },
{ "cat", NULL },
{ "wc", NULL },
{ "cat", "-e", NULL },
#ifdef BSD
{ "ifconfig", NULL },
#endif
};
size_t n = sizeof(arguments) / sizeof(arguments[0]);
for (size_t i = 0; i < n; i++) {
run_command(arguments[i]);
}
return 0;
}
Note that string literals should be used to initialize const char * objects, the C compiler let's you use them to initialize char * objects as a sloppy compatibility trick that is disabled at recommend warning levels -Wall -Wextra or -Weverything.
You cant do it this way as it is not an initialization.
You can use compound literals.
arguments[i] = (char *[]){"cat", "file.txt", NULL};
But they will stop to exist if you go out of the current scope and you store a reference to it.
Better:
arguments[i] = malloc(sizeof((char *[]){"cat", "file.txt", NULL}));
// ---- or
//arguments[i] = malloc(sizeof((char *[3]){}));
memcpy(arguments[i], (char *[]){"cat", "file.txt", NULL}, sizeof((char *[3]){}));
Related
edit: meant 'null termination' wherever 'padding' is used.
I am using the following function to compare a string (in this case the argv[1]) to an array of accepted arguments.
const char *ARGS_HELP[] = {"-h", "--help", "-help", "help", NULL};
const char *ARGS_BUILD[] = {"-b", "--build", NULL};
int strcmp_array(char *a, const char *b[])
{
//printf("a = \"%s\" b[0] = \"%s\" b[1] = \"%s\" b[2] = \"%s\" b[3] = \"%s\" b[4] = \"%s\" b[5] = \"%s\" b[6] = \"%s\ \n",a,b[0],b[1],b[2],b[3],b[4],b[5],b[6]);
int len = 0;
while (b[len] != NULL)
{
len++;
}
for (int i = 0; i < len; i++)
{
//printf("Comparing \"%s\" to \"%s\"\n",a,b[i]);
if (strcmp(a, b[i]) == 0)
{
return 0;
}
}
return 1;
}
int main(int argc, char *argv[])
{
if (argc == 1 || strcmp_array(argv[1], ARGS_HELP) == 0)
{
printf("%s", HELP_PAGE);
return 0;
}
}
I noticed that when I ran my program with "--build" as argv[1], it would still trigger the help page. I wondered why since ARGS_HELP and ARGS_BUILD are two separate const char arrays.
I noticed strcmp_array was looping through all the possible combinations. This is because I had not inserted NULL at the ends of each array to signify its end. Who can explain why this happens, why doesn't the compiler automatically insert a NULL character at the end of the const char arrays?
With padding
const char *ARGS_HELP[] = {"-h", "--help", "-help", "help", NULL};
const char *ARGS_BUILD[] = {"-b", "--build", NULL};
a = "--build" b[0] = "-h" b[1] = "--help" b[2] = "-help" b[3] = "help" b[4] = "(null)" b[5] = "(null)" b[6] = "-b"
Without null padding
const char *ARGS_HELP[] = {"-h", "--help", "-help", "help"};
const char *ARGS_BUILD[] = {"-b", "--build"};
a = "--build" b[0] = "-h" b[1] = "--help" b[2] = "-help" b[3] = "help" b[4] = "-b" b[5] = "--build"
I should always pad my char arrays with NULL from now on?
I am not a computer scientist. I am relatively new to C.
This isn't padding, it's adding a trailing NULL so that iteration of these structures is super simple. You just move along until you hit a NULL, then stop. It's especially common with lists of char* strings, like this, but you will also see it on lists of struct pointers and in other situations.
This is not unlike now C strings are NUL (character) terminated. In other languages you need not only the character data, but a length field as well, which adds overhead and complexity.
The alternative is you'd have to know how many there are, then pass that information in as well, which is a hassle, especially if you get that number wrong.
It's not that you have to, it's that it makes it convenient if the code you're using expects things to work that way. Notice how argc and argv are provided separately, even though argv could have worked the same way. Why the difference? A design decision many decades ago, but likely one that let you quickly test argc to see if you have enough arguments before moving along. There's no strlen() equivalent for arbitrary pointer arrays.
No, you don't always need to add an additional NULL at the end of your arrays.
This, AFAIK, is just so that the functions that use them know when they've reached the end of the arrays. Instead of having to also pass the number of elements in the array.
I'm currently creating a program that captures user's keypresses and stores them in a string. I wanted the string that stores the keypresses to be dynamic, but i came across a problem.
My current code looks something like this:
#include <stdio.h>
#include <stdlib.h>
typedef struct Foo {
const char* str;
int size;
} Foo;
int main(void)
{
int i;
Foo foo;
foo.str = NULL;
foo.size = 0;
for (;;) {
for (i = 8; i <= 190; i++) {
if (GetAsyncKeyState(i) == -32767) { // if key is pressed
foo.str = (char*)realloc(foo.str, (foo.size + 1) * sizeof(char)); // Access violation reading location xxx
sprintf(foo.str, "%s%c", foo.str, (char)i);
foo.size++;
}
}
}
return 0;
}
Any help would be appreciated, as I don't have any ideas anymore. :(
Should I maybe also allocate the Foo object dynamically?
First, in order to handle things nicely, you need to define
typedef struct Foo {
char* str;
int size
} Foo;
Otherwise, Foo is really annoying to mutate properly - you invoke undefined behaviour by modifying foo->str after the realloc call in any way.
The seg fault is actually caused by sprintf(foo.str, "%s%c", foo.str, (char)i);, not the call to realloc. foo.str is, in general, not null-terminated.
In fact, you're duplicating work by calling sprintf at all. realloc already copies all the characters previously in f.str, so all you have to do is add a single character via
f.str[size] = (char) i;
Edit to respond to comment:
If we wanted to append to strings (or rather, two Foos) together, we could do that as follows:
void appendFoos(Foo* const first, const Foo* const second) {
first->str = realloc(first->str, (first->size + second->size) * (sizeof(char)));
memcpy(first->str + first->size, second->str, second->size);
first->size += second->size;
}
The appendFoos function modifies first by appending second onto it.
Throughout this code, we leave Foos as non-null terminated. However, to convert to a string, you must add a final null character after reading all other characters.
const char *str - you declare the pointer to const char. You cant write to the referenced object as it invokes UB
You use sprintf just to add the char. It makes no sense.
You do not need a pointer in the structure.
You need to set compiler options to compile **as C language" not C++
I would do it a bit different way:
typedef struct Foo {
size_t size;
char str[1];
} Foo;
Foo *addCharToFoo(Foo *f, char ch);
{
if(f)
{
f = realloc(f, sizeof(*f) + f -> size);
}
else
{
f = realloc(f, sizeof(*f) + 1);
if(f) f-> size = 0
}
if(f) //check if realloc did not fail
{
f -> str[f -> size++] = ch;
f -> str[f -> size] = 0;
}
return f;
}
and in the main
int main(void)
{
int i;
Foo *foo = NULL, *tmp;
for (;;)
{
for (i = 8; i <= 190; i++)
{
if (GetAsyncKeyState(i) == -32767) { // if key is pressed
if((tmp = addCharToFoo(f, i))
{
foo = tmp;
}
else
/* do something - realloc failed*/
}
}
}
return 0;
}
sprintf(foo.str, "%s%c", foo.str, (char)i); is ill-formed: the first argument cannot be const char *. You should see a compiler error message.
After fixing this (make str be char *), then the behaviour is undefined because the source memory read by the %s overlaps with the destination.
Instead you would need to use some other method to append the character that doesn't involve overlapping read and writes (e.g. use the [ ] operator to write the character and don't forget about null termination).
I am not familiar with a way to convert a two dimensional array from C to an array of Strings which I can use in Swift. As far as I know there is no quick and easy way to do this in Swift.
This is the header of my C function:
char **getAllFilePaths(const char path []);
I tried the following:
//Get the pointer of the String array
if var ptr = getAllFilePaths(a) {
//Check if the current String is null
while let s = ptr.pointee {
//Copy the String into an array
a_paths.append(String(cString: s)) //<- Crash: Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
//Go to the next adress
ptr += 1
}
print(a_paths)
}
(I have this code from Martin R: https://stackoverflow.com/a/38422783/10269733. Unfortunately it doesn't work anymore in Swift 4.2)
I am searching for a solution for this problem all day, therefore I am open for any kinds of creative ideas!
EDIT:
This code works perfectly
char paths [FILES_MAX][PATH_MAX];
static size_t i = 0;
char **getAllFilePaths(const char path []){
PrintFile(path);
size_t j;
for(j = 0; j < i; j++){
printf("%s\n", paths[j]);
}
return paths;
}
The problem is not in the Swift code (or related to any Swift 4.2 changes).
The real problem is that the C function returns the “2-dimensional array” variable
char paths[FILES_MAX][PATH_MAX];
as a char ** to the caller – but that are incompatible types: one is an
array of arrays (with all characters being in contiguous memory), the other is a pointer to a pointer. There should be a compiler warning like
incompatible pointer types returning 'char [...][...]' from a function with result type 'char **'
You could return paths from the function if you declare something like
typedef char PathName[PATH_MAX];
PathName *getAllFilePaths(const char path []) { ... }
But that would be imported to Swift as
typealias PathName = (Int8, Int8, ... , Int8)
public func getAllFilePaths(_ path: UnsafePointer<Int8>!) -> UnsafeMutablePointer<PathName>!
where PathName is a tuple of PATH_MAX characters – quite cumbersome
to use from Swift! Another problem is that the caller does not know
how many arrays elements have been filled.
Better define paths as an array of char pointers:
static char *paths[FILES_MAX] = { NULL };
and fill it with a NULL-terminated list of char pointers. Here is a
over-simplified example:
char **getAllFilePaths(const char path []) {
paths[0] = "foo";
paths[1] = "bar";
paths[2] = NULL;
return paths;
}
In your real application you do not add string literals, so you'll probably
have to duplicate the strings
paths[j] = strdup(someString);
which means that at some point the strings must be free()d again, to avoid
memory leaks.
credits to "https://oleb.net/blog/2016/10/swift-array-of-c-strings/"
C:
int myCFunc(char *const argv[], size_t howmany )
{
int i;
for(i=0; i < howmany; i++){
printf("\n%s", argv[i]);
}
return 1;
}
Apart from headers/bridging:
// Swift:
import Foundation
public func withArrayOfCStrings<R>(
_ args: [String],
_ body: ([UnsafeMutablePointer<CChar>?]) -> R
) -> R {
var cStrings = args.map { strdup($0) }
cStrings.append(nil)
defer {
cStrings.forEach { free($0) }
}
return body(cStrings)
}
func goLowlevel(arguments: [String]) -> Int {
let count = arguments.count
return withArrayOfCStrings(arguments) {
argv in
myCFunc(argv, count)
return 10
}
}
let arr = ["AA", "BBB", "CCCC"]
goLowlevel(arguments: arr)
My question is very simple, but I still can't manage to get things right because I am simply not used to the C language.
I have an array that looks like this:
char *itemArr[] = {
"GPGGA",
"193914.00",
"5312.983745",
"N",
"00206.32143",
"E",
"4,17",
"0.6",
"43.48",
"M",
"47.46",
"M",
"1.0",
"0000",
"GPGLL,4916.45,N,12311.12,W,225444,A,M"
};
And I would like to add itemArr to a new multidimensional array, but when I try to copy the content of itemArr to protocols array like this:
int index = 0;
char *protocols[100][TOTAL_ITEMS];
memcpy(&protocols[index++], &itemArr, sizeof(itemArr));
It does only copy the first item of the itemArr and not the rest. So I can only see the first item like the following code:
printf("itemArr copy in protocols - index 0: %s\n", protocols[0][0]);
For example, this does not work:
printf("itemArr copy in protocols - index 1: %s\n", protocols[0][1]);
Please help me with a better example of how I should use memcpy, to copy an array to a multidimensional array. Thanks!
EDIT:
I tried my best to explain the code as simple as possible, but I guess that it only helps when pasting the real one below:
void getInputProtocolFromUser(protocol) {
char input[MESSAGE_SIZE];
do {
printf("Voer hier een NMEA protocol in:");
gets(input, MESSAGE_SIZE);
} while (input[0] == '\*' || input[0] == ' ');
strncpy_s(protocol, MESSAGE_SIZE, input, MESSAGE_SIZE);
}
void splitToArray(char* protocol, char *newArray[]) {
// strdup copies message to string pointer in order to create an array of the items inside the message
char *string = _strdup(protocol),
*token;
int c = 0;
/* get the first token */
token = strtok(string, ",");
/* walk through other tokens */
while (token != NULL)
{
char *copy = malloc(sizeof(char) * strlen(token));
strcpy(copy, token);
newArray[c++] = copy;
token = strtok(NULL, ",");
}
}
void compareProtocols(char *input, char *protocols[]) {
int index = 0;
char *inputArray[TOTAL_ITEMS+1];
splitToArray(input, inputArray);
inputArray[TOTAL_ITEMS] = input;
memcpy(&protocols[index++], &inputArray, 15);
int i;
for (i = 0; i < sizeof(MESSAGES) / sizeof(MESSAGES[0]); i++) {
char *messageArray[TOTAL_ITEMS];
splitToArray(MESSAGES[i], messageArray);
memcpy(&protocols[index++], &messageArray, 15);
//processProtocol(messageArray, inputArray);
}
}
int main(void) {
char *protocols[100][TOTAL_ITEMS];
char *inputProtocol = (char *)malloc(MESSAGE_SIZE);
getInputProtocolFromUser(inputProtocol);
compareProtocols(inputProtocol, protocols);
printf("Input index 0: %s\n", protocols[0][1]);
free(inputProtocol);
return 0;
}
The original version of your post declared itemArr as
char *itemArr = {
"GPGGA",
"193914.00",
...
i.e. without the [] in the declarator. My crystal ball tells me that that's what you have in your actual code.
This is not a valid declaration in C language, but GCC (if you use GCC) accepts such declarations for some reason that's beyond my understanding. (The compiler usually issues a warning about excessive number of initializers.) The declaration is effectively translated into
char *itemArr = "GPGGA";
with the rest of the initializers discarded. The behavior of your code immediately follows from that.
If one adds the missing [] to the declaration of itemArr, the code begins to work as intended. Everything gets copied to the target array.
The following code works as expect on my Mac :
#include <stdio.h>
#include <string.h>
int main()
{
int TOTAL_ITEMS = 15;
char *itemArr[] = {"GPGGA",
"193914.00",
"5312.983745",
"N",
"00206.32143",
"E",
"4,17",
"0.6",
"43.48",
"M",
"47.46",
"M",
"1.0",
"0000",
"GPGLL,4916.45,N,12311.12,W,225444,A,M"};
int index = 0;
char *protocols[100][TOTAL_ITEMS];
memcpy(&protocols[index++], &itemArr, sizeof(itemArr));
printf("itemArr copy in protocols - index 0: %s\n", protocols[0][0]);
printf("itemArr copy in protocols - index 1: %s\n", protocols[0][1]);
printf("itemArr copy in protocols - index 0: %s\n", protocols[0][2]);
printf("itemArr copy in protocols - index 1: %s\n", protocols[0][3]);
printf("itemArr copy in protocols - index 0: %s\n", protocols[0][4]);
printf("itemArr copy in protocols - index 1: %s\n", protocols[0][5]);
}
Maybe you should fix the itemArr declaration to itemArr[]. Your compiler might be throwing a warning and you're ending up with undesired behavior.
My function is being passed a struct containing, among other things, a NULL terminated array of pointers to words making up a command with arguments.
I'm performing a glob match on the list of arguments, to expand them into a full list of files, then I want to replace the passed argument array with the new expanded one.
The globbing is working fine, that is, g.gl_pathv is populated with the list of expected files. However, I am having trouble copying this array into the struct I was given.
#include <glob.h>
struct command {
char **argv;
// other fields...
}
void myFunction( struct command * cmd )
{
char **p = cmd->argv;
char* program = *p++; // save the program name (e.g 'ls', and increment to the first argument
glob_t g;
memset(&g, 0, sizeof(g));
g.gl_offs = 1;
int res = glob(*p++, GLOB_DOOFFS, NULL, &g);
glob_handle_res(res);
while (*p)
{
res = glob(*p, GLOB_DOOFFS | GLOB_APPEND, NULL, &g);
glob_handle_res(res);
}
if( g.gl_pathc <= 0 )
{
globfree(&g);
}
cmd->argv = malloc((g.gl_pathc + g.gl_offs) * sizeof *cmd->argv);
if (cmd->argv == NULL) { sys_fatal_error("pattern_expand: malloc failed\n");}
// copy over the arguments
size_t i = g.gl_offs;
for (; i < g.gl_pathc + g.gl_offs; ++i)
cmd->argv[i] = strdup(g.gl_pathv[i]);
// insert the original program name
cmd->argv[0] = strdup(program);
** cmd->argv[g.gl_pathc + g.gl_offs] = 0; **
globfree(&g);
}
void
command_free(struct esh_command * cmd)
{
char ** p = cmd->argv;
while (*p) {
free(*p++); // Segfaults here, was it already freed?
}
free(cmd->argv);
free(cmd);
}
Edit 1: Also, I realized I need to stick program back in there as cmd->argv[0]
Edit 2: Added call to calloc
Edit 3: Edit mem management with tips from Alok
Edit 4: More tips from alok
Edit 5: Almost working.. the app segfaults when freeing the command struct
Finally: Seems like I was missing the terminating NULL, so adding the line:
cmd->argv[g.gl_pathc + g.gl_offs] = 0;
seemed to make it work.
argv is an array of pointers of char *. This means that argv has space for argc char * values. If you try to copy more than that many char * values into it, you will end up with an overflow.
Most likely your glob call results in more than argc elements in gl_pathv field (i.e, gl_pathc > argc). This is undefined behavior.
It is similar to the code below:
/* Wrong code */
#include <string.h>
int a[] = { 1, 2, 3 };
int b[] = { 1, 2, 3, 4 };
memcpy(a, b, sizeof b);
Solution: you should either work with the glob_t struct directly, or allocate new space to copy gl_pathv to a new char **:
char **paths = malloc(g.gl_pathc * sizeof *paths);
if (paths == NULL) { /* handle error */ }
for (size_t i=0; i < g.gl_pathc; ++i) {
/* The following just copies the pointer */
paths[i] = g.gl_pathv[i];
/* If you actually want to copy the string, then
you need to malloc again here.
Something like:
paths[i] = malloc(strlen(g.gl_pathv[i] + 1));
followed by strcpy.
*/
}
/* free all the allocated data when done */
Edit: after your edit:
cmd->argv = calloc(g.gl_pathc, sizeof(char *) *g.gl_pathc);
it should work, but each of argv[1] to argv[g.gl_pathc + g.gl_offs - 1] is a char * that is "owned" by the struct glob. Your memcpy call is only copying the pointers. When you later do globfree(), those pointers don't mean anything anymore. So, you need to do copy the strings for your use:
size_t i;
cmd->argv = malloc((g.gl_pathc+g.gl_offs) * sizeof *cmd->argv);
for (i=g.gl_offs; i < g.gl_pathc + g.gl_offs; ++i)
cmd->argv[i] = strdup(g.gl_pathv[i]);
This makes sure you now have your own private copies of the strings. Be sure to free them (and argv) once you are done.
There are a few other problems with your code.
You are doing *p++, you should do p++, since you're not using the value of the dereferencing.
You should really check the return value of glob.
Your paths variable needs g.gl_pathc + 1 elements, not g.gl_pathc. (Or more correctly, you need to allocate g.gl_pathc + g.gl_offs times sizeof *paths bytes.)
Your for loop to copy strings should be for (j=1; j < g.gl_pathc + g.gl_offs; ++j).
Make sure you prevent shell from expanding your glob. I.e., call ./a.out '*' instead of ./a.out *.
Don't you need to multiple g.gl_pathc by sizeof(char *)?