11

In C, If I have:

char *reg = "[R5]";

and I want

char *reg_alt = "R5" (equal to the same thing, but without the brackets), how do I do this?

I tried

*char reg_alt = reg[1:2];

but this doesn't work.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
Austin
  • 6,921
  • 12
  • 73
  • 138

4 Answers4

15

There is no built-in syntax for dealing with substrings like that, so you need to copy the content manually:

char res[3];
memcpy(res, &reg[1], 2);
res[2] = '\0';
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • In addition to this good answer is the fact that you can also copy substrings from the original string in reverse by using a for loop. – Jake Psimos Mar 14 '16 at 06:17
  • 1
    `memmove`, unlike `memcpy`, would allow for overlapping source and destination. You don't need to re-invent it with an explicit loop. – JDługosz Mar 14 '16 at 07:47
13

I suggest you need to read a basic text on C, rather than assuming techniques from other languages will just work.

First, char *reg = "[R5]"; is not a string. It is a pointer, that is initialised to point to (i.e. its value is the address of) the first character of a string literal ("[R5]").

Second, reg_alt is also a pointer, not a string. Assigning to it will contain an address of something. Strings are not first class citizens in C, so the assignment operator doesn't work with them.

Third, 1:2 does not specify a range - it is actually more invalid syntax. Yes, I know other languages do. But not C. Hence my comment that you cannot assume C will allow things it the way that other languages do.

If you want to obtain a substring from another string, there are various ways. For example;

  char substring[3];
  const char *reg = "[R5]";    /* const since the string literal should not be modified */

  strncpy(substring, &reg[1], 2);     /* copy 2 characters, starting at reg[1], to substring */
  substring[2] = '\0';     /*  terminate substring */

  printf("%s\n", substring);

strncpy() is declared in standard header <string.h>. The termination of the substring is needed, since printf() %s format looks for a zero character to mark the end.

Peter
  • 35,646
  • 4
  • 32
  • 74
  • 1
    Between `memcopy()` and `strncopy()`, is there any reason to use one over the other? – Austin Mar 14 '16 at 03:24
  • 5
    There is no such thing as `memcopy()` or `strncopy()`. `strncpy()` will detect zero terminators on strings, and stop copying. It will also cease copying if the source string is shorter than the length specified. `memcpy()` will copy regardless - which will cause undefined behaviour if the source string is shorter than the specified length. – Peter Mar 14 '16 at 03:29
  • 2
    @Austin: On the other hand, you need to be aware that `strncpy()`... 1) fills up the remaining space with null bytes, i.e. will *always* write the full `n` bytes, and 2) will leave the destination **unterminated** if there is no null byte in the first `n` bytes of the source string. It's a tricky function that way. – DevSolar Mar 14 '16 at 10:23
3

When using null-terminated strings (the default in C), you can indeed cheaply create a substring of another string by simply changing the starting character pointer, but you cannot make the new substring have a different null-terminator.

An option is to use a Pascal-string library. Pascal-strings are length-prefixed instead of C-strings which are null-terminated, which means Pascal-strings can share contents of a larger string buffer and substring generation is cheap (O(1)-cheap). A Pascal string looks like this:

struct PString {
    size_t length;
    char*  start;
}

PString substring(const PString* source, size_t offset, size_t length) {
    // Using C99 Designated Initializer syntax:
    return PString { .length =  length, .start = source.start + offset };
}

The downside is that most of the C library and platform libraries use null-terminated strings and unless your Pascal-string ends in a null character you'll need to copy the substring to a new buffer (in O(n) time).

Of course, if you're feeling dangerous (and using mutable character buffers) then you can hack it to temporarily insert a null-terminator, like so:

struct CStr {
    char* start;
    char* end;
    char  temp;
}

CStr getCStr(PString* source) {
    char* terminator = (source.start + source.length);
    char previous = *terminator;
    *terminator = '\0';
    return CStr { .start = source.start, .end = terminator, .temp = previous };
}

void undoGetCStr(CStr cstr) {
    *cstr.end = cstr.temp;
}

Used like so:

PString somePascalString = doSomethingWithPascalStrings();
CStr temp = getCStr( somePascalString );
printf("My Pascal string: %s", temp.start ); // using a function that expects a C-string
undoGetCStr( temp );

...which then gives you O(1) PString-to-CString performance, provided you don't care about thread-safety.

Dai
  • 141,631
  • 28
  • 261
  • 374
  • 1
    The main disadvantage of this answer is that Pascal-string libraries are not generally distributed with C compilers and libraries. – Peter Mar 14 '16 at 03:23
  • @Peter I've added a simple hack that allows you to use Pascal-strings with functions that expect C-strings. – Dai Mar 14 '16 at 05:54
1

Need to be a char?

Because that only work when is a "string" So maybe you need this

char reg[] = "[R5]";

Then you can do the other thing or just split the string like this question

Community
  • 1
  • 1
Dinorah Tovar
  • 488
  • 2
  • 15
  • I need to pass it as a char* to other functions so I don't want to have to rewrite all of them to take char[] instead – Austin Mar 14 '16 at 03:11
  • Arguments of type `char []` and `char *` are the same thing in C. The problem is, within a function where an array is created, they are different things. – Peter Mar 14 '16 at 03:25