Double pointer to char[] - c

Alright, so I have the following code:
char** args = (char**)malloc(10*sizeof(char*));
memset(args, 0, sizeof(char*)*10);
char* curToken = strtok(string, ";");
for (int z = 0; curToken != NULL; z++) {
args[z] = strdup(curToken);
curToken = strtok(NULL, ";")
}
I want every arg[z] casted into an array of chars -- char string[100] -- and then processed in the algorithms I have following. Every arg[z] needs to be casted to the variable string at some point. I am confused by pointers, but I am slowly getting better at them.
EDIT:
char string[100] = "ls ; date ; ls";
arg[0] will be ls, arg[1] will be date, and arg[2] will be ls after the above code.
I want to put each argument back into char string[100] and process it through algorithms.

one easiest way is to keep a backup of the original string in some temporary variable.
char string[100] = "ls ; date ; ls";
char temp_str[100] = {0};
strcpy (temp_str, string);
Another way is to do it by strcat. z has the number of agruments.
memset(string, '\0', 100);
for (i = 0; i < z; i++)
{
strcat(string, args[i]);
if (i != (z - 1))
{
//if it is last string dont append semicolon
strcat(string, ";");
}
}
Note : Take care of the boundary condition check

If you want the parts of string copied into a fixed length string[100] then you need to malloc 100 chars for each args[] inside the loop and strncpy() the result of strtok into it. strdup will only allocate enough memory for the actual length of the supplied string (plus \0)

This:
char** args = (char**)malloc(10*sizeof(char*));
memset(args, 0, sizeof(char*)*10);
is broken code. First, you shouldn't cast malloc()'s return value. Second, args is a pointer to ten pointers to char. You can't set them to NULL using memset(), there's no guarantee that "all bytes zero" is the same as NULL. You need to use a loop.

Related

Splitting a string into two and assigning to two char array

I have a string like:
my age is 22\r\n\r\n and I live in Rostock
and some other strings like:
I like to swim\r\n\r\n .Yesterday I competed with my 2 friends
Now I want to split a string by \r\n\r\n and have it associated with buffer. Here is what I am trying to do:
char buffer[500];
strcpy(buffer, "my age is 22\r\n\r\n and I live in Rostock");
char *p = buffer;
if((p = strchr(p,"\r\n\r\n"))) {
p[strcspn(p,"tock")] = 0; // trying to slice until the end
}
printf("%s", p);
This gives me a warning as I try to compile saying warning: passing argument 2 of ‘strchr’ makes integer from pointer without a cast I could not understand what this meant.
Also what is good way to split this into 2 char buffers?
strchr expects a mere char and not a string for its second parameter. So you can only pass it a single character.
In C a char is a single character and strings are null terminated char arrays.
What you want to do is probably:
char buffer[500] = "my age is 22\r\n\r\n and I live in Rostock"; // directly initialize the char array
const char sep[] = "\r\n\r\n";
char * p = strstr(buffer, sep); // search the separator
if (p) {
*p = '\0'; // null terminate the first part
p += strlen(sep); // make p point to the start of the second part
printf("%s - %s\n", buffer, p);
}
If you really need to purge the initial part from buffer and have it start at the second part, you could do:
for (char *dest = buffer; p<buffer+sizeof(buffer)-1;) { // do not go past end of array
*dest++ = *p++;
if (*p == '\0') break; // stop on first null
}

Problems passing in a char[][] to execvp

I am trying to iterate over some input (which are commands and arguments), split the input into individual strings, then pass that input into execvp().
I am having trouble as execvp() wants a (char*, char*[]) as its arguments. I am passing in (char*, char[][]) which I thought was the same thing but it isn't liking it.
I would use a char*[] but I don't know how big the strings are prior to it running, so that is the reason I didn't use it. So obviously if I use char*[], I get a seg fault when I try and access elements of the char*'s.
Here's a snippet of the code
//input
char *line = "echo hello there what is";
int count = 0;
//store arguments
char args[6][10];
int argCount = 0;
//store temp string from input
char temp[100];
int tempCount = 0;
//word(string) size
int wordSize = 0;
/*
Here I iterate over the input, storing each string I find in args
all the above variables help me do that.
*/
execvp(args[0], args);
printf("Error: It didnt work\n");
Hopefully that is clear and a valid question, let me know if you want me to add the code of me turning the input into args.
An array of arrays of char is not the same as an array of pointers to arrays of char. execvp() expects the latter, with a null pointer as its last element, passing the former has undefined behavior.
You must construct an array of pointers, either allocated from the heap or defined with automatic storage (on the stack), initialize it with pointers to argument strings and pass this array to execvp().
Note also that echo is both a shell internal command and an executable file in the path.
Here is your code fragment modified accordingly (without the parse code, which is still yours to write):
//input "echo hello there what is";
//arguments array
char *args[6];
/*
* Here you should iterate over the input, storing each string you find
* on the command line into `args` and terminate with a null pointer...
*/
args[0] = "echo";
args[1] = "hello";
args[2] = "there";
args[3] = "what";
args[4] = "is";
args[5] = NULL;
execvp(args[0], args);
You can use two arrays:
char real_args[6][10]; // Six strings of up to nine characters each
...
char *args[] = {
real_args[0],
real_args[1],
real_args[2],
real_args[3],
real_args[4],
real_args[5],
NULL // Must be terminated by a null pointer
};
Use real_args for the actual arguments, and then pass args to execvp.
Ok I worked out how to use a char*[] instead of a char[][] and successfully put it into execvp().
Notes for the code below: I keep track of how long the current string is that I am iterating over in wordSize.
//input
char* line = "echo hello there what is";
int count = 0;
//store arguments
char* args[10];
int argCount = 0;
//store temp string from input
char temp[100];
int tempCount = 0;
//word size
int wordSize = 0;
while(line[count] != '\0')
{
if (line[count] == ' ' || line[count + 1] == '\0')
{
/*
As I don't know how big each string will be, I can
allocate it here as I know how big the string is through wordSize
*/
args[argCount] = malloc(sizeof(char)*(wordSize +1));
.
.
.
}
}
//terminate args with 0
args[argCount] = 0;
execvp(args[0], args);
printf("Error: It didnt work\n");

Appending a char to a char*

I'm writing a program and I need to append a char to a char*. I have a char** that represents lines of text. I'm trying to take a single char at a time from char**, and add it to a char*.
So basically, I tried strcat(char*, char[i][j]), but strcat rejects char[i][j] since it's not a pointer. I think I need to use sprintf(char*, "%c", char[i][j]), but I'm having trouble understanding how to append with sprintf. I don't want it to overwrite what's already in my char*.
Any tips?
You almost got it!
strncat (char *, & char[i][j], 1);
Calling strncat like that will copy exactly one char from your char** at position [i][j]. Just remember that it will also append the null character after that.
given that you know that buf is defined as
char buf [MAXSIZE];
Let us assume that is has been initialized to an initial string and you want to add mychar to it (a single character)
i = strlen(buf);
if (i < MAXSIZE-1)
{
buf[i] = mychar; // Make the new character the last element of buf
buf[i+1] = '\0' // end the new string with the null character
}
This will append the new character and push the ending null character to ensure that it is a valid string. This is what strcat does when the appended entry is also a string or what strncat does when the count is 1. Note that the new strlen(buf) will now be i+1
Assuming buf is a 0 terminated c-string, you can use snprintf():
#include <stdio.h>
#include <string.h>
int main(void)
{
size_t b_len;
char buf[256];
char ch = '!';
strcpy(buf, "Hello world");
b_len = strlen(buf);
snprintf(buf + b_len, sizeof buf - b_len, "%c", ch);
printf("%s", buf);
char *p = malloc(256); /* check if malloc failed */
strcpy(p, "Hello world");
size_t len = strlen(p);
snprintf(p + len, 256 - len, "%c", ch);
printf("%s", p);
return 0;
}
If buf happens to be pointer (such as an malloced pointer) then you can't use sizeof (it can only be used on an array to get the size).
I have a very hard time understanding your question, instead of posting an example of what you want to achieve you explained something that seems to be based on your way of solving a problem with very little training and/or experience in the c language. So it's hard to write a good answer but definitely I will try to show you a way of doing something similar to what apparently you want to do.
The thing is, I highly doubt that strcat() is appropriate for this or for anything except, concatenating two strings. More than two, require something better than strcat(). And for a single char it's definitely less appropriate.
The following code appends all the characters in the string literal array into a single string, check it out
int
main(int argc, char **argv)
{
char string[100];
char lines[3][14] = {
"First line...",
"Another line.",
"One more line"
};
size_t i;
i = 0;
for (size_t j = 0 ; ((j < sizeof(lines) / sizeof(lines[0])) && (i < sizeof(string) - 1)) ; ++j)
{
for (size_t k = 0 ; ((k < 13) && (i < sizeof(string) - 1)) ; ++k)
string[i++] = lines[j][k];
}
string[i] = '\0';
puts(string);
return 0;
}

append a character from an array to a char pointer

Ok, so I'm a person who usually writes Java/C++, and I've just started getting into writing C. I'm currently writing a lexical analyser, and I can't stand how strings work in C, since I can't perform string arithmetic. So here's my question:
char* buffer = "";
char* line = "hello, world";
int i;
for (i = 0; i < strlen(line); i++) {
buffer += line[i];
}
How can I do that in C? Since the code above isn't valid C, how can I do something like that?
Basically I'm looping though a string line, and I'm trying to append each character to the buffer string.
string literals are immutable in C. Modifying one causes Undefined Behavior.
If you use a char array (your buffer) big enough to hold your characters, you can still modify its content :
#include <stdio.h>
int main(void) {
char * line = "hello, world";
char buffer[32]; // ok, this array is big enough for our operation
int i;
for (i = 0; i < strlen(line) + 1; i++)
{
buffer[i] = line[i];
}
printf("buffer : %s", buffer);
return 0;
}
First off the buffer needs to have or exceed the length of the data being copied to it.
char a[length], b[] = "string";
Then the characters are copied to the buffer.
int i = 0;
while (i < length && b[i] != '\0') { a[i] = b[i]; i++; }
a[i] = '\0';
You can reverse the order if you need to, just start i at the smallest length value among the two strings, and decrement the value instead of increment. You can also use the heap, as others have suggested, ordinate towards an arbitrary or changing value of length. Furthermore, you can change up the snippet with pointers (and to give you a better idea of what is happening):
int i = 0;
char *j = a, *k = b;
while (j - a < length && *k) { *(j++) = *(k++); }
*j = '\0';
Make sure to look up memcpy; and don't forget null terminators (oops).
#include <string.h>
//...
char *line = "hello, world";
char *buffer = ( char * ) malloc( strlen( line ) + 1 );
strcpy( buffer, line );
Though in C string literals have types of non-const arrays it is better to declare pointers initialized by string literals with qualifier const:
const char *line = "hello, world";
String literals in C/C++ are immutable.
If you want to append characters then the code can look the following way (each character of line is appended to buffer in a loop)
#include <string.h>
//...
char *line = "hello, world";
char *buffer = ( char * ) malloc( strlen( line ) + 1 );
buffer[0] = '\0';
char *p = Buffer;
for ( size_t i = 0; i < strlen( line ); i++ )
{
*p++ = line[i];
*p = '\0';
}
The general approach is that you find the pointer to the terminating zero substitute it for the target character advance the pointer and appenf the new terminating zero. The source buffer shall be large enough to accomodate one more character.
If you want to append a single character to a string allocated on the heap, here's one way to do it:
size_t length = strlen(buffer);
char *newbuffer = realloc(buffer, length + 2);
if (newbuffer) { // realloc succeeded
buffer = newbuffer;
buffer[length] = newcharacter;
buffer[length + 1] = '\0';
}
else { // realloc failed
// TODO handle error...
free(buffer); // for example
}
However, this is inefficient to do repeatedly in a loop, because you'll be repeatedly calling strlen() on (essentially) the same string, and reallocating the buffer to fit one more character each time.
If you want to be smarter about your reallocations, keep track of the buffer's current allocated capacity separately from the length of the string within it — if you know C++, think of the difference between a std::string object's "size" and its "capacity" — and when it's necessary to reallocate, multiply the buffer's size by a scaling factor (e.g. double it) instead of adding 1, so that the number of reallocations is O(log n) instead of O(n).
This is the sort of thing that a good string class would do in C++. In C, you'll probably want to move this buffer-management stuff into its own module.
The simplest solution, lacking any context, is to do:
char buffer[ strlen(line) + 1 ];
strcpy(buffer, line);
You may be used to using pointers for everything in Java (since non-primitive types in Java are actually more like shared pointers than anything else). However you don't necessarily have to do this in C and it can be a pain if you do.
Maybe a good idea given your background would be to use a counted string object in C, where the string object owns its data. Write struct my_string { char *data; size_t length; } . Write functions for creating, destroying, duplicating, and any other operation you need such as appending a character, or checking the length. (Separate interface from implementation!) A useful addition to this would be to make it allocate 1 more byte than length, so that you can have a function which null-terminates and allows it to be passed to a function that expects a read-only C-style string.
The only real pitfall here is to remember to call a function when you are doing a copy operation, instead of allowing structure assignment to happen. (You can use structure assignment for a move operation of course!)
The asprintf function is very useful for building strings, and is available on GNU-based systems (Linux), or most *BSD based systems. You can do things like:
char *buffer;
if (asprintf(&buffer, "%s: adding some stuff %d - %s", str1, number, str2) < 0) {
fprintf(stderr, "Oops -- out of memory\n");
exit(1); }
printf("created the string \"%s\"\n", buffer);
free(buffer); /* done with it */
Appending is best done with snprintf
Include the stdio.h header
#include <stdio.h>
then
char* buffer;
char line[] = "hello, world";
// Initialise the pointer to an empty string
snprintf(buffer, 1, "%s", "");
for (i = 0; i < strlen(line); ++i) {
snprintf(buffer, sizeof line[i], "%s%s", buffer, line[i]);
}
As you have started the code you have there is different from the question you are asking.
You could have split the line with strtok though.
But I hope my answer clarifies it.

Fill char*[] from strtok

I have problems getting following Code to work. It parses a users input into a char*[] and returns it. However the char* command[] does not accept any values and stays filled with NULL... whats going on here?
void* setCommands(int length){
char copy[strlen(commandline)]; //commandline is a char* read with gets();
strcpy(copy, commandline);
char* commands[length];
for (int x=0; x<length; x++)
commands[x] = "\0";
int i = 0;
char* temp;
temp = strtok (copy, " \t");
while (temp != NULL){
commands[i] = temp; //doesnt work here.. commands still filled with NULL afterwards
i++;
printf("word:%s\n", temp);
temp = strtok (NULL, " \t");
}
commands[i] = NULL;
for (int u=0; u<length; u++)
printf("%s ", commands[i]);
printf("\n");
return *commands;
}
You may assume, that commandline != NULL, length != 0
commands[i] = NULL;
for (int u=0; u<length; u++)
printf("%s ", commands[i]);
Take a very good look at that code. It uses u as the loop control variable but prints out the element based on i.
Hence, due to the fact you've set commands[i] to NULL in the line before the loop, you'll just get a series of NULLs.
Use commands[u] in the loop rather than commands[i].
In addition to that:
void* setCommands(int length){
char* commands[length];
:
return *commands;
}
will only return one pointer, the one to the first token, not the one to the array of token pointers. You cannot return addresses of local variables that are going out of scope (well, you can, but it may not work).
And, in any case, since that one pointer most likely points to yet another local variable (somewhere inside copy), it's also invalid.
If you want to pass back blocks of memory from functions, you'll need to look into using malloc, in this case both for the array of pointers and the strings themselves.
You have a number of issues... Your program will be exhibiting undefined behaviour currently, so until you address the issues you cannot hope to predict what's going on. Let's begin.
The following string is one character too short. You forgot to include a character for the string terminator ('\0'). This will lead to a buffer overrun during tokenising, which might be partly responsible for the behaviour you are seeing.
char copy[strlen(commandline)]; // You need to add 1
strcpy(copy, commandline);
The next part is your return value, but it's a temporary (local array). You are not allowed to return this. You should allocate it instead.
// Don't do this:
char* commands[length];
for (int x=0; x<length; x++)
commands[x] = "\0"; // And this is not the right way to zero a pointer
// Do this instead (calloc will zero your elements):
char ** commands = calloc( length, sizeof(char*) );
It's possible for the tokenising loop to overrun your buffer because you never check for length, so you should add in a test:
while( temp != NULL && i < length )
And because of the above, you can't just blindly set commands[i] to NULL after the loop. Either test i < length or just don't set it (you zeroed the array beforehand anyway).
Now let's deal with the return value. Currently you have this:
return *commands;
That returns a pointer to the first token in your temporary string (copy). Firstly, it looks like you actually intended to return an array of tokens, not just the first token. Secondly, you can't return a temporary string. So, I think you meant this:
return commands;
Now, to deal with those strings... There's an easy way, and a clever way. The easy way has already been suggested: you call strdup on each token before shoving them in memory. The annoying part of this is that when you clean up that memory, you have to go through the array and free each individual token.
Instead, let's do it all in one hit, by allocating the array AND the string storage in one call:
char **commands = malloc( length * sizeof(char*) + strlen(commandline) + 1 );
char *copy = (char*)(commands + length);
strcpy( copy, commandline );
The only thing I didn't do above is zero the array. You can do this after the tokenising loop, by just zeroing the remaining values:
while( i < length ) commands[i++] = NULL;
Now, when you return commands, you return an array of tokens which also contains its own token storage. To free the array and all strings it contains, you just do this:
free( commands );
Putting it all together:
void* setCommands(int length)
{
// Create array and string storage in one memory block.
char **commands = malloc( length * sizeof(char*) + strlen(commandline) + 1 );
if( commands == NULL ) return NULL;
char *copy = (char*)(commands + length);
strcpy( copy, commandline );
// Tokenise commands
int i = 0;
char *temp = strtok(copy, " \t");
while( temp != NULL && i < length )
{
commands[i++] = temp;
temp = strtok(NULL, " \t");
}
// Zero any unused tokens
while( i < length ) commands[i++] = NULL;
return commands;
}

Resources