Need help understanding pointers, functions and 2D arrays - c

The following is a snippet of code that parses tokens and stores them. The code compiles and works.
Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int storeTokens(char *src, char *delim, char **storage, size_t len) {
int idx = 0;
char str[100] = {0};
char *token = NULL;
char *saveptr = NULL;
// copy string so we don't modify the source
if (snprintf(str, sizeof(str), "%s", src) < 0) {
return 0;
}
// Loop through string and parse out tokens
token = strtok_r(str, delim, &saveptr);
while ((token != NULL) && (idx < len)) {
snprintf(storage[idx], sizeof(storage[idx]), "%s", token);
strcpy(storage[idx++], token);
token = strtok_r(NULL, delim, &saveptr);
}
// Print the tokens
int i = 0;
for (i = 0; i < idx; i++) {
printf("fun: %s\n", storage[i]);
}
return idx; // number of tokens found
}
int main() {
int i = 0;
char teststr[] = "This*is*my*string*.";
char *storageptr[72];
char storage[72][255];
// Assign storage space to the pointers
for (i = 0; i < 72; i++) {
storageptr[i] = storage[i];
}
// Parse the tokens
int numtokens = storeTokens(teststr, "*", storageptr, 72);
// Print the tokens
for (i = 0; i < numtokens; i++) {
printf("storage: %s\n", storage[i]);
}
return EXIT_SUCCESS;
}
Output
fun: This
fun: is
fun: my
fun: string
fun: .
storage: This
storage: is
storage: my
storage: string
storage: .
The function stores the string with the char **storage variable. The reason for this being a double pointer is so the function can be used on any storage regardless of the length.
My problem is passing the storage into the function. As you can see in main, I created storageptr to point to storage before passing it to the function.
This seems convoluted and unnecessary. Not to mention the time wasted looping and linking each string. However I can't figure out how to pass storage directly into the function without creating storageptr.
I've done reading such as Passing an array of strings to a C function by reference, but nothing really clicks. I can't seem to figure out how to properly pass storage into the function.

Using a C99 or C11, having optional VLA support, compatible compiler you can pass directly the 2D array specifying dynamically the size of your array:
int storeTokens(char *src, char *delim, size_t len, size_t size, char storage[len][size]);
Call from main:
int numtokens = storeTokens(teststr, "*", 72, 255, storage);
If you have a C11 compiler to be sure that it supports VLA's (that are optional for C11) check the symbol STDC_NO_VLA, if it is defined the compiler does not support VLA's.

Unless you need to keep the argument as a pointer to pointer (to char), then to be able to pass the array of arrays (of char) directly you need to change the function signature:
int storeTokens(char *src, char *delim, char (*storage)[255], size_t len)
// ^^^^^^^^^^^^^^^
Now you can pass storage from the main function:
storeTokens(teststr, "*", storage, 72)
The problem is that while arrays can decay to a pointer to its first element, it's not "recursive".
If you have an array like yours
char storage[72][255];
then when it decays to a pointer to its first element it is &storage[0], which is a pointer to an array. This secondary array will not decay, so passing storage will pass a pointer to an array (of type char (*)[255]).
If you must pass a char **, then the intermediate storageptr array is needed.

Related

C: Splitting a string into two strings, and returning a 2 - element array

I am trying to write a method that takes a string and splits it into two strings based on a delimiter string, similar to .split in Java:
char * split(char *tosplit, char *culprit) {
char *couple[2] = {"", ""};
int i = 0;
// Returns first token
char *token = strtok(tosplit, culprit);
while (token != NULL && i < 2) {
couple[i++] = token;
token = strtok(NULL, culprit);
}
return couple;
}
But I keep getting the Warnings:
In function ‘split’:
warning: return from incompatible pointer type [-Wincompatible-pointer-types]
return couple;
^~~~~~
warning: function returns address of local variable [-Wreturn-local-addr]
... and of course the method doesn't work as I hoped.
What am I doing wrong?
EDIT: I am also open to other ways of doing this besides using strtok().
A view things:
First, you are returning a pointer to a (sequence of) character(s), i.e. a char
* rather than a pointer to a (sequence of) pointer(s) to char. Hence, the return type should be char **.
Second, you return the address of a local variable, which - once the function has finished - goes out of scope and must not be accessed afterwards.
Third, you define an array of 2 pointers, whereas your while-loop may write beyond these bounds.
If you really want to split into two strings, the following method should work:
char ** split(char *tosplit, char *culprit) {
static char *couple[2];
if ((couple[0] = strtok(tosplit, culprit)) != NULL) {
couple[1] = strtok(NULL, culprit);
}
return couple;
}
I'd caution your use of strtok, it probably does not do what you want it to. If you think it does anything like a Java split, read the man page and then re-read it again seven times. It is literally tokenizing the string based on any of the values in delim.
I think you are looking for something like this:
#include <stdio.h>
#include <string.h>
char* split( char* s, char* delim ) {
char* needle = strstr(s, delim);
if (!needle)
return NULL;
needle[0] = 0;
return needle + strlen(delim);
}
int main() {
char s[] = "Fluffy furry Bunnies!";
char* res = split(s, "furry ");
printf("%s%s\n", s, res );
}
Which prints out "Fluffy Bunnies!".
First of all strtok modifies the memory of tosplit so be certain that, that's what you wish to do. If so then consider this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* NOTE: unsafe (and leaky) implementation using strtok
*
* *into must point to a memory space where tokens can be stored
* or if *into is NULL then it allocates enough space.
* Returns:
* allocated array of items that you must free yourself
*
*/
char **__split(char *src, const char *delim)
{
size_t idx = 0;
char *next;
char **dest = NULL;
do {
dest = realloc(dest, (idx + 1)* sizeof(char *));
next = strtok(idx > 0 ? NULL:strdup(src), delim);
dest[idx++] = next;
} while(next);
return dest;
}
int main() {
int x = 0;
char **here = NULL;
here = __split("hello,there,how,,are,you?", ",");
while(here[x]) {
printf("here: %s\n", here[x]);
x++;
}
}
You can implement a much safer and non leaky version (note the strdup) of this but hopefully this is a good start.
The type of couple is char** but you have defined the function return type as char*. Furthermore you are returning the pointer to a local variable. You need to pass the pointer array into the function from the caller. For example:
#include <stdio.h>
#include <string.h>
char** split( char** couple, char* tosplit, char* culprit )
{
int i = 0;
// Returns first token
char *token = strtok( tosplit, culprit);
for( int i = 0; token != NULL && i < 2; i++ )
{
couple[i] = token;
token = strtok(NULL, culprit);
}
return couple;
}
int main()
{
char* couple[2] = {"", ""};
char tosplit[] = "Hello World" ;
char** strings = split( couple, tosplit, " " ) ;
printf( "%s, %s", strings[0], strings[1] ) ;
return 0;
}

assign the returned pointer to a new array

I am new c-programmer. I defined a function, which parse a line and generates token. Each of these token will be saved in an array of tokens. So I created an array of Strings for that purpose. My defined function returns this Array. Now I want to assign the returned array to a new array I get an error:
incompatible types when assigning to type ‘char *[5]’ from type ‘char **’.
So I do not have any idea how to deal with this problem. here is the whole code;
#include <string.h>
#include <stdio.h>
char ** parseLine(char * str){
char del[2]=",";
char * token;
//Array of strings
char * tokenArr[5];
token=strtok(str,del);
int i=0;
while(token!=NULL && i<5){
//save each new token in the array of tokens
*(tokenArr+i)=*token;
token=strtok(NULL,del);
i++;
}
return tokenArr;
}
int main(int argc, char * argv []){
char str[120]="Achon, Adkins, 3459345,cs,usa";
char * tokenArray [5];
tokenArray=parseLine(str);
}
You are trying to assing a char to a char pointer here:
*(tokenArr+i)=*token;
token is a pointer, so assign it, not the value it points to.
In the function, you are trying to return a local array tokenArr. Returning an array and/or returning a local variable is not possible. Return a pointer to an array that is allocated on the heap instead:
char** tokenArray;
tokenArray=parseLine(str);
char** parseLine(char* str){
...
char** tokenArr = malloc(sizeof(*tokenArr)*5);
...
return tokenArr ;
}
As you fix this, think about checking return values of the functions you call, for invalid values. And then how are you going to know how much elements did you actually process and how are you going to obtain that information.
In C language it is not possible to "return" a value of array type from a function. It is not possible to "assign" or in any other way copy an array using core language features.
In your situation a common practice is to pass the destination array to the function as a parameter and fill it out inside the function. The return value of your function might be used to return the actual number of tokens to the user
unsigned parseLine(char * str, char *tokens[], unsigned max_tokens)
{
char del[2] = " ,";
char *token = strtok(str, del);
unsigned i = 0;
while (token != NULL && i < max_tokens){
tokens[i++] = token;
token = strtok(NULL, del);
}
return i;
}
You will call your function as
int main(int argc, char *argv[])
{
char str[120] = "Achon, Adkins, 3459345,cs,usa";
char *tokenArray[5];
unsigned n_tokens = parseLine(str, tokenArray, 5);
for (unsigned i = 0; i < n_tokens; ++i)
printf("%s\n", tokenArray[i]);
}

Return for type void function

I'm trying to program a function that allows me to locate a substring "from" in a string "src", and replace the "from" substring with the "to" substring in all cases, and output the new string through "dest"; however I think my code looks a bit iffy, and I do not understand (conceptually) how I would return an output with dest, given that the output is of type void. I was wondering if someone could offer some assistance?
for example:
find_replace("pooppoop poop", "poo", "hel", dest) should return
"helphelp help"
thank you!
void find_replace(char* src, char* from, char* to, char* dest)
{
dest = (char * ) malloc(sizeof(src)+sizeof(from));
int i;
int j;
int check = 1;
for (i = 0; i <= strlen(src) - 1; i++) {
if (src[i] == from[0]) {
for (j = 0; j <= strlen(from) - 1; i++) {
if (src[i+j] != from[j]) {
check = 0;}
else {
continue;
}}}
if (check == 1) {
char * str3 = (char *) malloc(1 + strlen(&src[i]) + strlen(to));
strcpy(str3, &src[i]);
strcat(str3, to);
}
else { continue; }
}
return ;
You allocate memory for a new string in your example, but the calling code cannot acces this variable.
Basically, there are three methods to pass a string. Each has advantages and drawbacks.
Pass a fixed-size buffer
int repl1(char *dest, int n, const char *src, const char *find, const char *repl)
Here, the calling function provides a buffer to which the function can write. It is a good idea to provide a maximum buffer length, so that the function does not overflow that buffer. The arguments, whose contents you don't intend to change should be const, i.e. pointers to unmodifiable data.
Such a function can be void, but it could also return an integer that indicates how long the string in dest is.
The advantage is that you can easily pass automatic buffers. The disadvantage ist that these buffers might be too small for the task.
Call the function like this:
char buf[80];
int n = repl1(buf, sizeof(buf), str, "this", "that");
Return allocated memory
char *repl2(const char *src, const char *find, const char *repl)
Here, the function should allocate new memory to hold the buffer. The function returns the pointer to the new memory. That memory "belongs" to the calling function, which then is responsible for freeing the memory.
The advantage is that the function can allocate enough memory for the task. The disadvantage is that the calling function must take care of managing the new memory.
Call the function like this:
char *dest = repl2(str, "this", "that");
// Do stuff whith 'dest' ...
free(dest);
Pass a pointer to a poiner to char
int repl3(char **dest, const char *src, const char *find, const char *repl)
This is a variant of returning the pointer, where the pointer is passed by reference and can therefore be changed. The function also has access to the old contents to the dest char buffer. That is not useful in your case. I have only mentioned this possibility for completeness.
Call the function like this:
char *buf;
int n = repl3(&buf, str, "this", "that");
This answer addresses the ways of passing data. Your code uses the second method, so you should return dest, but not pass it in as parameter. I have not looked at your function logic.
Void type method won't return anything, what you can do is change the type of your function to string and return a string.
Void means nothing, so you can't return a value with a void function.
I assume you want to use call-by-reference instead of call-by-value, if you want to use a void function.
This means, that you give a pointer to the function, to tell where your array is located. Then you work with your 'real' array, instead of a copy.
[Apart from analyzing the logic of your function] A function with a return type void won't [and can't] return any value using the return statement. Also, worthy to mention, you cannot return more than one value [as you need] using a return statement, either.
To get the return value(s) in your case, you're supposed to call your function and pass pointer(s) to char as argument. Then, inside your function, when you assign/alter values of the locations pointed by those pointers, they will get modified and after returning from your function, in the caller function, you'll have the modified value.
This is another way to have more than one return value at a time from a called function.
printf("%s\n",find_replace("pooppoop poop", "poo", "hel", dest));
char * find_replace(char* src, char* from, char* to, char* dest)
{
dest = (char * ) malloc(sizeof(src)+sizeof(from));
int i;
int j;
int check = 1;
for (i = 0; i <= strlen(src) - 1; i++) {
if (src[i] == from[0]) {
for (j = 0; j <= strlen(from) - 1; i++) {
if (src[i+j] != from[j]) {
check = 0;}
else {
continue;
}}}
if (check == 1) {
char * str3 = (char *) malloc(1 + strlen(&src[i]) + strlen(to));
strcpy(str3, &src[i]);
strcat(str3, to);
return str3 ;
}
else { continue; }
}
return "";
}
The line:
dest = (char * ) malloc(sizeof(src)+sizeof(from));
overrides the address passed in
void find_replace(char* src, char* from, char* to, char* dest)
If you want to allocate memory inside the function (which I think you have to, because the caller cannot know how much to reserve), you have to tell the caller where the result data ends up in. Either you opt for an out-parameter:
void find_replace(char* src, char* from, char* to, char** dest)
{
*dest = malloc(...);
or, what I would prefer, you return the pointer:
char* find_replace(char* src, char* from, char* to)
{
char* dest = malloc(...);
// ...
return dest;
}

Reversing a string in C using pointers?

Language: C
I am trying to program a C function which uses the header char *strrev2(const char *string) as part of interview preparation, the closest (working) solution is below, however I would like an implementation which does not include malloc... Is this possible? As it returns a character meaning if I use malloc, a free would have to be used within another function.
char *strrev2(const char *string){
int l=strlen(string);
char *r=malloc(l+1);
for(int j=0;j<l;j++){
r[j] = string[l-j-1];
}
r[l] = '\0';
return r;
}
[EDIT] I have already written implementations using a buffer and without the char. Thanks tho!
No - you need a malloc.
Other options are:
Modify the string in-place, but since you have a const char * and you aren't allowed to change the function signature, this is not possible here.
Add a parameter so that the user provides a buffer into which the result is written, but again this is not possible without changing the signature (or using globals, which is a really bad idea).
You may do it this way and let the caller responsible for freeing the memory. Or you can allow the caller to pass in an allocated char buffer, thus the allocation and the free are all done by caller:
void strrev2(const char *string, char* output)
{
// place the reversed string onto 'output' here
}
For caller:
char buffer[100];
char *input = "Hello World";
strrev2(input, buffer);
// the reversed string now in buffer
You could use a static char[1024]; (1024 is an example size), store all strings used in this buffer and return the memory address which contains each string. The following code snippet may contain bugs but will probably give you the idea.
#include <stdio.h>
#include <string.h>
char* strrev2(const char* str)
{
static char buffer[1024];
static int last_access; //Points to leftmost available byte;
//Check if buffer has enough place to store the new string
if( strlen(str) <= (1024 - last_access) )
{
char* return_address = &(buffer[last_access]);
int i;
//FixMe - Make me faster
for( i = 0; i < strlen(str) ; ++i )
{
buffer[last_access++] = str[strlen(str) - 1 - i];
}
buffer[last_access] = 0;
++last_access;
return return_address;
}else
{
return 0;
}
}
int main()
{
char* test1 = "This is a test String";
char* test2 = "George!";
puts(strrev2(test1));
puts(strrev2(test2));
return 0 ;
}
reverse string in place
char *reverse (char *str)
{
register char c, *begin, *end;
begin = end = str;
while (*end != '\0') end ++;
while (begin < --end)
{
c = *begin;
*begin++ = *end;
*end = c;
}
return str;
}

strtok - how avoid new line to and put to array of strings?

if i dupe topic i really sorry, i searched for it with no result here.
I have code
void split(char* str, char* splitstr)
{
char* p;
char splitbuf[32];
int i=0;
p = strtok(str,",");
while(p!= NULL)
{
printf("%s", p);
sprintf(&splitstr[i],"%s",p);
i++;
p = strtok (NULL, ",");
}
}
How can i use proper sprintf to put the splited words by strtok to string array?
Can i somehow avoid breaklines created by strtok?
I am programming in ANSI C. I declared array splitstr and str the same way.
char* splitstr;//in main char splitstr[32];
Thanks for help.
edit:
i would like do something like this:
INPUT (it is a string) > "aa,bbb,ccc,ddd"
I declare: char tab[33];
OUTPUT (save all items to array of strings if it is even possible) >
tab[0] is "aa"
tab[1] is "bbb"
...
tab[3] is "ddd" but not "ddd(newline)"
edit2 [18:16]
I forgot add that the data string is from reading line of file. That's why i wrote about "ddd(newline)". I found after that the new line was also shown by strtok but as another item. By the way all answers are good to think over the problem. Few seconds ago my laptop has Broken (i dont know why the screen gone black) As soon as i take control over my pc i will check codes. :-)
Give this a shot:
#include <stdlib.h>
#include <string.h>
...
void split(char *str, char **splitstr)
{
char *p;
int i=0;
p = strtok(str,",");
while(p!= NULL)
{
printf("%s", p);
splitsr[i] = malloc(strlen(p) + 1);
if (splitstr[i])
strcpy(splitstr[i], p);
i++;
p = strtok (NULL, ",");
}
}
And then, in main:
#define MAX_LEN ... // max allowed length of input string, including 0 terminator
#define MAX_STR ... // max allowed number of substrings in input string
int main(void)
{
char input[MAX_LEN];
char *strs[MAX_STR] = {NULL};
...
split(input, strs);
...
}
Some explanations.
strs is defined in main as an array of pointer to char. Each array element will point to a string extracted from the input string. In main, all that's allocated is the array of pointers, with each element initially NULL; the memory for each element will be allocated within the split function using malloc, based on the length of the substring. Somewhere after you are finished with strs you will need to deallocate those pointers using free:
for (i = 0; i < MAX_STR; i++)
free(strs[i]);
Now, why is splitstr declared as char ** instead of char *[MAX_STR]? Except when it is the operand of the sizeof or & operators, or is a string literal being used to initialize another array in a declaration, an array expression will have its type implicitly converted from N-element array of T to pointer to T, and the value of the expression will be the location of the first element in the array.
When we call split:
split(input, strs);
the array expression input is implicitly converted from type char [MAX_LENGTH] to char * (T == char), and the array expression strs is implicitly converted from type char *[MAX_STRS] to char ** (T == char *). So splitstr receives pointer values for the two parameters, as opposed to array values.
If I understand correctly, you want to save the strings obtained by strtok. In that case you'll want to declare splitstr as char[MAX_LINES][32] and use strcpy, something like this:
strcpy(splitstr[i], p);
where i is the ith string read using strtok.
Note that I'm no ansi C expert, more C++ expert, but here's working code. It uses a fixed size array, which may or may not be an issue. To do anything else would require more complicated memory management:
/* Not sure about the declaration of splitstr here, and whether there's a better way.
char** isn't sufficient. */
int split(char* str, char splitstr[256][32])
{
char* p;
int i=0;
p = strtok(str,",");
while(p) {
strcpy(splitstr[i++], p);
p = strtok (NULL, ",");
}
return i;
}
int main(int argc, char* argv[])
{
char input[256];
char result[256][32];
strcpy(input, "aa,bbb,ccc,ddd");
int count = split(input, result);
for (int i=0; i<count; i++) {
printf("%s\n", result[i]);
}
printf("the end\n");
}
Note that I supply "aa,bbb,ccc,ddd" in and I get {"aa", "bbb", "ccc", "ddd" } out. No newlines on the result.

Resources