3

In a recent interview, I was asked a very simple question to reverse a string (not just print) without any extra variable and any in-built function. The closest I could think of is:

#include<stdio.h>
#include<string.h>
int main()
{
    char ch[100];
    scanf("%s",&ch);
    int i=0;
    while(i<strlen(ch)/2)
    {
       ch[i]=ch[strlen(ch)-1-i]+ch[i];
       ch[strlen(ch)-1-i]=ch[i]-ch[strlen(ch)-1-i];
       ch[i]=ch[i]-ch[strlen(ch)-1-i];
       i++;
    }
    printf("%s",ch);
    return 0;
}

My solution was rejected as I used variable i. How this is even possible without using a counter variable? Is there any other method to solve this?

EDIT

These were the exact words of question(nothing more or less):

Reverse a string without using any variable or inbuilt functions in C.

wrangler
  • 3,454
  • 1
  • 19
  • 28
  • 1
    Relevant quote from the duplicate - "Best elaborate what [without any extra variable] means exactly. If there is no loophole to exploit, it's probably impossible." – Bernhard Barker Sep 22 '15 at 16:21
  • see edit :exact words of question – wrangler Sep 22 '15 at 16:27
  • 3
    The interview question, as presented, is absurd. At *minimum* you must have a variable that points to the start of the string. Without any constraints on the input string, there is no way the job can be done correctly without at least one additional variable or the `strlen()` function. – John Bollinger Sep 22 '15 at 16:31
  • Can you use `strlen`? – BLUEPIXY Sep 22 '15 at 16:31
  • This one was already solved: http://stackoverflow.com/a/198264/2747160 – m8mble Sep 22 '15 at 16:32
  • @BLUEPIXY i can as that was not the problem – wrangler Sep 22 '15 at 16:32
  • 4
    @m8mble `q` is a variable in that answer. As stated, I'd say it's impossible. The interviewer probably meant something different from what they actually asked (we could only guess as to what though - it could very well be that they were looking for that solution). – Bernhard Barker Sep 22 '15 at 16:35
  • 1
    @Phil, I don't see how any of the answers presented there avoid using additional variables. – John Bollinger Sep 22 '15 at 16:35
  • They *probably* wanted an answer involving recursion using no variables excluding the function arguments. At least this is the only solution I can think of, and I am reasonably confident that *any* solution must use at least one other identifier to refer to parts and/or a position of the string. – Konrad Rudolph Sep 22 '15 at 16:41
  • i think recursion still needs a variable to keep track of loc. – wrangler Sep 22 '15 at 16:42
  • 2
    Do function arguments count as variables? – EOF Sep 22 '15 at 16:44
  • @wrangler Yes: but it can be a parameter. – Konrad Rudolph Sep 22 '15 at 16:44
  • @Dukeling how can interviewer meant other than to reverse a string when he asked to reverse a string – wrangler Sep 22 '15 at 16:45
  • A good interview question does not necessarily have a definitive solution, but invites discussion. On the other hand, I've had interview questions, where there were several possible answers. – Weather Vane Sep 22 '15 at 17:24
  • 1
    @wrangler I was saying the interviewer probably meant something other than strictly no extra variables and no built-in functions (if `strlen` is allowed, that's already an unstated exception). – Bernhard Barker Sep 22 '15 at 17:48
  • @BoPersson at link every sol uses variable and not just one... – wrangler Sep 22 '15 at 20:15
  • The original statement is without any extra variable. The editted statement is without any variable, which would include a pointer to string. In either case, it can't be done on a variable length string without an extra variable, assuming that a parameter passed to or returned from a function counts as an extra variable. With a fixed length string, the code could use constant indexes and do xor swaps to reverse string. – rcgldr Sep 22 '15 at 22:02

4 Answers4

5

Two Three possible implementations: one only prints the string in reverse. Another reverses the string in-memory and in-place. Both assume that defining your own recursive functions is allowed and that parameters don't count as variables. In any case, the parameters themselves are constant, so arguably not variables.

void printRev(const char * const s){
    if(*s != '\0'){ // or just: if(*s){
        printRev(s + 1);
        putchar(*s);
    }
}

Does 'prefix' recursion through the string: first recurse until the end is reached, then print each character after the recursive call returns.

void revStr(char * const s, const int len){
    if(len > 0){
        if(s[0] != s[len]){
            s[0] ^= s[len];
            s[len] ^= s[0];
            s[0] ^= s[len];
        }

        revStr(s + 1, len - 2);
    }
}

Is slightly more involved: it XOR-swaps the 'first' character of the string with the 'last'. Then recurses with the next character as the start of the string, and the length decreased by two. So in the next iteration the second character becomes the first, and the second to last becomes the last character. For this the s pointer itself is still const, but obviously the characters pointed to are modified.

The second function requires the string length as an input parameter, this can also be done (recursively) without needing the built-in strlen function:

int myStrlen(const char * const s){
    if(*s != '\0'){
        return 1 + myStrlen(s + 1);
    }

    return 0;
}

Addit

Here is a version that does not use a length parameter, but requires a disjoint output string, and the input string to be modifiable. It simulates the len - 2 expression from revStr by replacing the last character in src with a NUL-character.

void copyRev(char * const restrict dst, char * const restrict src){
    if(src[0] != '\0'){
        dst[0] = src[myStrlen(src) - 1];
        dst[myStrlen(src) - 1] = src[0];
        src[myStrlen(src) - 1] = '\0';

        copyRev(dst + 1, src + 1);
    }
}
Kninnug
  • 7,992
  • 1
  • 30
  • 42
  • @KonradRudolph thanks & fixed. That's what you get when you modify your code without recompiling :). – Kninnug Sep 22 '15 at 17:14
  • OP was quite clear *"reverse a string (not just print) "*, but upvote since the other solution uses no in-built function. – Weather Vane Sep 22 '15 at 17:23
  • @WeatherVane I was taking it rather simplistically as 'not just print the string, also reverse it', but I see your point. – Kninnug Sep 22 '15 at 17:51
  • @Kninnug, your second solution is my best guess at what the question was after, possibly with an auxiliary function to compute the string length instead of requiring it to be an input parameter. – John Bollinger Sep 22 '15 at 17:57
  • Corner flaw: if `len == 1` as code attempts `revStr(s + 1, 1 - 2);`. Suggest another `if(len > 1){` -- cancel this -- , Did not see code was using `int`. In that case, suggest `size_t len` rather than `int` and use `len > 1` for complete `[]` range. – chux - Reinstate Monica Sep 22 '15 at 19:33
  • @Kninnug your second solution is much better than mine but here 'len' is still a variable.......until you call 'myStrlen' for every 'len'. – wrangler Sep 22 '15 at 19:42
  • @chux I initially used `size_t` as well and fell into that corner case, hence I switched to `int`. As this is a purely academic exercise I think it's reasonable to assume the string length won't exceed `INT_MAX`. – Kninnug Sep 22 '15 at 20:01
  • @wrangler I consider `len` to be a parameter as well, it doesn't change within the function. Calling `myStrlen` every time does not work because the 'length' needs to decrease by 2 in order to swap the second with the second-to-last character (rather than the last). I have added a third version that does call `myStrlen` instead of keeping a length parameter, but does require a disjoint output string. – Kninnug Sep 22 '15 at 20:09
  • @Kninnug i think this must be the optimal solution but this even uses a extra var `src`. – wrangler Sep 22 '15 at 20:32
  • @wrangler that's true, but I honestly can't think of another way to do it. You'll always need something to keep track of where in the string you are while iterating/recursing. – Kninnug Sep 22 '15 at 20:41
  • Thats why i mentioned in the ques _Is it even possible?_ – wrangler Sep 22 '15 at 20:52
2

we can re-alloc the string memory to double its size.
we can then memcpy the string to the 2nd half.

e.g. S T R \0 S T R \0

Now we can memcpy 1 char at a time to the original string from the 2nd half.

This is assuming an ascii string with a '\0' terminating char.

void reverseString(const char** pStr)
{
    *pStr = realloc(*pStr, strlen(*pStr)*2 + 2);
    memcpy(*pStr + strlen(*pStr), *pStr, strlen(*pStr));
    while (*pStr)
    {
        memcpy(*pStr, *pStr + strlen(*pStr + 2), 1);
        ++ (*pStr);
    }

}
  • +1, I had forgotten about `realloc` (I had thought about using a similar solution, but writing beyond the string boundary; i.e. hoping that we can abuse UB). That said, there’s a typo — it should be `while (**pStr)`, shouldn’t it? – Konrad Rudolph Sep 22 '15 at 17:01
  • You should note that this assumes the passed in string is allocated with `malloc`. Otherwise quite ingenious! – Kninnug Sep 22 '15 at 17:07
  • 2
    Nice, but it does kinda trample on the "without using any [...] inbuilt functions" bit. Of course, I don't see how allowing `strlen()`, as the OP accepted in comments, does not also trample on that provision. – John Bollinger Sep 22 '15 at 17:09
  • the first solution given by Kninnug works pretty well. The interviewer was probably looking for that answer. – Shyamal Desai Sep 22 '15 at 17:12
  • 1
    @ShyamalDesai No, the question explicitly says “not just print”. And of course that, as the other solution, uses parameters which could be construed as being variables (see commend discussion below the question). – Konrad Rudolph Sep 22 '15 at 17:13
  • ah... thanks Konrad. @John: we can write strlen that returns an int. – Shyamal Desai Sep 22 '15 at 17:19
  • @ShyamalDesai nice approach but i see you have used a _pointer var_ and if i could use pointer variable then may be i could use _count var_ so my given sol would have been accepted.. – wrangler Sep 22 '15 at 20:29
  • @wrangler: I used only the pointer passed in. However, there are plenty of temporaries in the code. – Shyamal Desai Sep 22 '15 at 21:18
  • this answer fails the question because it calls the functions `realloc()` and `strlen()` and `memcpy()` – user3629249 Sep 23 '15 at 16:37
0

Using C11's ability to create compound literals as arguments and return values:

typedef struct foo_T {
  char *s;
  char *end;
  char left, right;
} foo_T;

foo_T foo(const foo_T f) {  // No variables, just constant parameter
  if (f.s < f.end) {
    *f.s = f.right;
    *f.end = f.left;
    return (foo_T){f.s+1,f.end-1,*f.s,*f.end};
  }
  return f;
}

char *endptr(char * const s) {  // No variables, just constant parameter
  if (*s == 0) return s-1;
  return endptr(s + 1);
}

void rev_novar(char *s) {
  if (*s && s[1]) {
    foo ((foo_T){s,endptr(s),*s,*endptr(s)});
  }
}

Test

void test_rev(const char *s) {
  char buf[100];
  memset(buf, 77, sizeof buf);
  strcpy(&buf[1], s);
  printf("%d '%s' %d\n", buf[0], &buf[1], buf[strlen(buf) + 1]);
  rev_novar(&buf[1]);
  printf("%d '%s' %d\n\n", buf[0], &buf[1], buf[strlen(buf) + 1]);
}

int main(void) {
  test_rev("123");
  test_rev("1234");
  test_rev("1");
  test_rev("");
  return 0;
}

Test results. (The 77 are left and right sentinels.)

77 '123' 77
77 '321' 77

77 '1234' 77
77 '4231' 77

77 '1' 77
77 '1' 77

77 '' 77
77 '' 77
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Parameters such as f.s+1 would be considered a variable. My guess is that the interviewer got the question wrong. – rcgldr Sep 23 '15 at 03:25
  • @rcgldr "f.s+1 would be considered a variable" --> Where is a [spin doctor](https://en.wikipedia.org/wiki/Spin_(public_relations)) when I need one? – chux - Reinstate Monica Sep 23 '15 at 03:37
  • @chux nice approach but i think i mentioned only c is allowed ,so c11 is not an option – wrangler Sep 23 '15 at 06:17
  • @wrangler C11 _is_ C - just the latest version. – chux - Reinstate Monica Sep 23 '15 at 06:18
  • True but if that was the case he must have mentioned c/c11. In interview cases its better not to assume anything until we got a hint.... – wrangler Sep 23 '15 at 06:27
  • @wrangler Disagree about "better not to assume anything until we got a hint". In the classroom the problem can be well defined as the answer is often known ahead of time and assumptions are not needed. Real engineering always occurs with lots of ill defined goals - that why the big bucks - to do what has not been done before. Note, answer did not assume C11 - something out for 4 years, but stated that as the answer's prerequisite with "Using C11's ability ..." – chux - Reinstate Monica Sep 23 '15 at 06:36
0

My guess is that the interviewer got the question wrong. Without any variables, you could have a sequence of hard coded if statements to find the end of the string and then have hard coded swap sequence. It's similar in concept to a sorting network, which is used for sorting a fixed number of values (up to 16, based on wiki article). For example for strings up to length 5:

void reverse(char str[])
{
    if(str[0] == 0 || str[1] == 0)
        return;
    if(str[2] == 0){
        str[0] ^= str[1]; str[1] ^= str[0]; str[0] ^= str[1];
        return;
    }
    if(str[3] == 0){
        str[0] ^= str[2]; str[2] ^= str[0]; str[0] ^= str[2];
        return;
    }
    if(str[4] == 0){
        str[0] ^= str[3]; str[3] ^= str[0]; str[0] ^= str[3];
        str[1] ^= str[2]; str[2] ^= str[1]; str[1] ^= str[2];
        return;
    }
    if(str[5] == 0){
        str[0] ^= str[4]; str[4] ^= str[0]; str[0] ^= str[4];
        str[1] ^= str[3]; str[3] ^= str[1]; str[1] ^= str[3];
        return;
    }
}

Even with the code above, an internal variable (register) is needed for the XOR operations, unless a processor had a xor memory to memory instruction, (the compare to zero can use an immediate constant on X86/X64 processors).

Example code if constant pointers and recursion are allowed, but this essentially uses the stack as multiple instances of variables (the pointers) since they are being manipulated as pointer + 1 and/or pointer - 1 , and since reverse1() is tail recursive, an optimizing compiler will probably convert it into a loop, with beg and end as regular variables.

void reverse0(char * const beg, char * const end);
void reverse1(char * const beg, char * const end);

/* entry function */
void reverse(char str[])
{
    if(str[0] == 0 || str[1] == 0)
        return;
    reverse0(&str[0], &str[2]);
}

/* find end of str */
void reverse0(char * const beg, char * const end)
{
    if(*end != 0){
        reverse0(beg, end+1);
        return;
    }
    reverse1(beg, end-1);           
}

/* reverse str */
void reverse1(char * const beg, char * const end)
{
    *beg ^= *end;
    *end ^= *beg;
    *beg ^= *end;
    if((beg+1) < (end-1))
        reverse1(beg+1, end-1);
}
rcgldr
  • 27,407
  • 3
  • 36
  • 61
  • The [one-liner XOR-swaps](http://c-faq.com/expr/xorswapexpr.html) result in undefined behaviour. There are no sequence points between sucessive modifications of `str[0]` etc, regardless of order of evaluation. – Kninnug Sep 25 '15 at 12:14
  • Microsoft has always taken a rather liberal approach to standards. GCC gives me the following warning when compiling with `-Wall -pedantic`: `warning: operation on '*str' may be undefined [-Wsequence-point]` repeated for every XOR-swap line. The problem is not how the CPU implements the XOR-swap, but rather that it is allowed by the Standard to triple-fault or give you nasal demons. Hence: don't do it. For brevity you could replace it with a function: `void xorSwap(char * a, char * b){ *a ^= *b; *b ^= *a; *a ^= *b; }`. – Kninnug Sep 25 '15 at 22:47
  • I maintain that parameters shouldn't count as variables, because then `str` would count as a variable just as much. You could change the parameters to `char * const a` though, to show that the parameters themselves won't be changed. Or you could use a macro: `#define XSWAP(a, b) do{ a ^= b; b ^= a; a ^= b; }while(0)`. The point is that it is just syntactic sugar to avoid excessive line-duplication. As for GCC's 'maybe', that's just the compiler being a bit too modest. It is definitely undefined: see C99§6.5/2, §6.5.16/3&4 and Annex C. – Kninnug Sep 25 '15 at 23:18
  • @Kninnug - I removed the one liner xor swaps, since that shouldn't be part of the discussion for the original question. I'm not the reviewer, so I don't know what is to be counted as a variable, and the function could use a global instance of str[], instead of a parameter. Even in the case of a function using char * const ptr, the function could recurse using ptr+1, ptr+2, and those recursive parameters could be considered variables, although they would be constant increments from the original. It's my opinion that the reviewer's question is wrong or poorly worded. – rcgldr Sep 26 '15 at 01:02