1
\$\begingroup\$

I have a char tempBuffer which stores say my!string!hello!. After every ! is encountered that token is moved to a char codeBuffer and the rest of the characters are shifted to the begiinging of tempBuffer. So after my is moved to codeBuffer, tempBuffer contains string!hello!.

I have currently implemented it as follows:

while (tempBuffer[0] != '\0') {
    /* Read input contents into tempBuffer */
    bytesReadTotal = read(fd, tempBuffer, BUFFER_SIZE);

    /* Copy contents of tempBuffer into codeBuffer till delimiter is found*/
    for (startOfBlock = 0; startOfBlock < strlen(tempBuffer); startOfBlock++){
        if (tempBuffer[startOfBlock] != opt.separator[0])
            codeBuffer[startOfBlock] = tempBuffer[startOfBlock];
        else
            break;
    }

    if (tempBuffer[startOfBlock] == '\0')
        break;

    /* Copy contents of tempBuffer into temp2Buffer from new block position*/
    for (int i = 0; i < strlen((char*) tempBuffer); i++) {
        temp2Buffer[i] = tempBuffer[startOfBlock + 1];
        startOfBlock++;
    }

    /* Copy contents of temp2Buffer to tempBuffer */
    for (startOfTB = 0; startOfTB < strlen((char*) temp2Buffer); startOfTB++) {
        tempBuffer[startOfTB] = temp2Buffer[startOfTB];
    }

    /* Reset all contents of tempBuffer after last copied position
       to '\0' to avoid overlap */
    tempBuffer[startOfTB] = '\0';
}

I would like to avoid multiple copy operations.How can I improve this code?

\$\endgroup\$
1
\$\begingroup\$

C's string implementation is null-terminated. Thus, as long as,

  1. one doesn't care about the original string, (this may not be possible if you have a const char *,) and
  2. one has at least one char that one is willing to discard per block, (usually the block delimiter,) to turn into '\0',

one can do this in place without copying. This tokenizing is very common, and string.h from the standard library has functions that will help. For example, strtok, (and thread-safe extension to the ISO C standard, strtok_r,) or non-standard, but strsep is an improvement, (possible implementation.) In this code, this takes advantage that argv is writable and uses it to come up with some strings which are separated by strchr.

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

/** One could probably store this, but printing it is easier. */
static void print(const char *p) { printf("codeBuffer <- %s\n", p); }

/** `str0` is a string that get separated by '!'. */
static void str_sep_bang(char *str0, void (*const callback)(const char *)) {
    char *str1;
    assert(str0 && callback);
    for( ; ; ) {
        if((str1 = strchr(str0, '!'))) *str1 = '\0';
        if(*str0 != '\0') callback(str0);
        if(!str1) break;
        str0 = str1 + 1;
    }
}

int main(int argc, char **argv) {
    int i;
    for(i = 1; i < argc; i++) {
        printf("You passed: %s.\n", argv[i]);
        str_sep_bang(argv[i], &print);
    }
    return 0;
}

This prints out,

bin/stringthing foo\!bar\!baz\!qux \!\!
You passed: foo!bar!baz!qux.
codeBuffer <- foo
codeBuffer <- bar
codeBuffer <- baz
codeBuffer <- qux
You passed: !!.

However, if one needs to store these values, you need space, (an array of char *, same as is passed in argv.) Usually that involves dynamic allocation or static allocation of the maximum size. As well, accessing this list is only valid while the data from the underlying string doesn't change or go out-of-scope; this is why crossing different modules typically involves duplication of the underlying data.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.