0

I want to insert a char* to a defined location in another char*. For example:

char str1[80] = "Hello world!";
char str2[] = "the ";

I want the result to be Hello the world! (insert str2 to location 6 of str1)

I have tried:

#include <stdio.h>

char str1[80] = "Hello world!";
char str2[] = "the ";
char tmp1[80];
char tmp2[80];

char *string_insert(char *scr, char *ins, int loc){
    // Get the chars from location 0 -> loc
    for(int i = 0; i <= loc; i++){
        tmp1[i] = scr[i];
    }
    
    // Get the chars from loc -> end of the string
    for(int i = 0; i < sizeof(scr) - loc; i++){
        tmp2[i] = scr[i + loc];
    }
    
    // Insert the string ins
    for(int i = 0; i < sizeof(ins); i++){
        tmp1[i + loc] = ins[i];
    }
    
    // Add the rest of the original string
    for(int i = 0; i < sizeof(scr) - loc; i++){
        tmp1[loc + 1 + i] = tmp2[i];
    }
    
    return tmp1;
}

int main(){
    printf("%s", string_insert(str1, str2, 6));
    return 0;
}

But then I got Hello two. You can execute it online at onlinegdb.com

I also wonder if there is any function from string.h that can do this?

Thanks for any help!

raspiduino
  • 601
  • 7
  • 16
  • 1
    You _can_ by directly manipulating memory, but you _shouldn't_ because you would be writing past the bounds of an allocated array and thus causing UB, aka Nasal Demons - assuming the destination is even in writable memory (depending on how you declared your string literal). (Update: I just saw that `str1` is is 80-long, so what you're trying to do _is safe_ in this particular case, but not in general). – Dai Mar 20 '21 at 15:49
  • `sizeof(scr)` does not do what you think it does. – Dai Mar 20 '21 at 15:50
  • Is `sizeof(scr)` return 80? And how to get the exact length of `char*`? – raspiduino Mar 20 '21 at 15:51
  • Your target buffer is sufficiently sized (80), so there is no reason you cannot do this, but no, there is no canned function for such an operation. You *can* do it without a temporary via some in-place operations (a concat, and a triple-reversal would do it, for example). And `sizeof(scr)` in that function evals to the size of a *pointer* not the size of what it points to. – WhozCraig Mar 20 '21 at 15:52
  • `sizeof(scr) == sizeof(void*)` because you're passing `str1` by pointer, so information about the size of the array is dropped. In C++ you could use a template function so the compiler can pass (static) array size information, but in practice you would pass the array length in a separate `size_t` parameter. – Dai Mar 20 '21 at 15:52
  • Does this answer your question? [Is the sizeof(some pointer) always equal to four?](https://stackoverflow.com/questions/399003/is-the-sizeofsome-pointer-always-equal-to-four) – Dai Mar 20 '21 at 15:57
  • Do you know [`strncat`](https://linux.die.net/man/3/strncat) and [`strncpy`](https://linux.die.net/man/3/strncpy)? You can do everything you want using those two. – rustyx Mar 20 '21 at 16:00
  • 1
    Where did you learn that using globals was a good idea? Forget they even exist - like `goto` they have no place an _good_ code, and the use of globals here has no justification whatsoever - entirely unnecessary. Unlearn that habit. Perhaps your username is a clue to where you picked up that habit - most Arduino sketches presented as examples reinforce this bad practice. – Clifford Mar 20 '21 at 17:44
  • @Dai This is not the place for this discussion, but use of globals does not reduce memory usage. There is no difference in memory usage between a static with local scope and one with global scope. Moreover that fact does not justify global in this code where there are unnecessarily three global buffers - hardly a memory saving! You might read https://www.embedded.com/a-pox-on-globals/ and learn something about cargo-cult myths like you are espousing. – Clifford Mar 22 '21 at 06:26

3 Answers3

2

There's no function in the standard library to insert a string into another.

You have to first create enough space for the characters you want to insert by moving the original characters to the right:

Hello World\0
Hello WorlWorld\0    <- move characters to the right
Hello the World\0   <- rewrite (including ' ')

In your example you have enough space (you create a buffer of 80 characters and use only 12 of them) but you should be absolutely be sure that this is the case.

However this is not what your code does. You copy those characters in another buffer and use that one as return value. In other words, your str1 is left unchanged. I don't think this is what you wanted, right?

Remo.D
  • 16,122
  • 6
  • 43
  • 74
0

fixed code with some comments to help with understanding:

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

char str1[] = "Hello world!"; // use `[]` to include trailing \0
char str2[] = "the ";

char tmp1[80]; // not very good idea to use global variables, but for algorithm learning purposes it is ok
char tmp2[80];

char *string_insert(char *src, char *str, int loc){
    // function uses global variable tmp1
    // if total length of string is > 80 - result is undefined
    
    int ti = 0; // index in tmp variable
    while (ti<loc) tmp1[ti++] = *src++; // copy first part from src
    while (*str) tmp1[ti++] = *str++; // append str
    while (*src) tmp1[ti++] = *src++; // append the rest of src

    tmp1[ti] = 0; // don't forget trailing 0
    return tmp1;
}

int main(){
    printf("%s", string_insert(str1, str2, 6));
    return 0;
}
Iłya Bursov
  • 23,342
  • 4
  • 33
  • 57
  • But if I put `tmp1` and `tmp2` I get `warning: function returns address of local variable [-Wreturn-local-addr] return tmp1;` using mingw32. Is that a problem? – raspiduino Mar 20 '21 at 16:05
  • @raspiduino, if your compiler makes that complaint about the program presented in this answer then you need to get a better compiler. But I feel pretty confident in supposing that in fact you got the message from a different, possibly similar, program. (And you should not ignore such a warning. It is a sign of a serious flaw that the program in this answer does not exhibit.) – John Bollinger Mar 20 '21 at 16:08
  • Ok and why do we need `string.h` in the code? I cannot find any function in the code use `string.h` and I can comment it without any problem. – raspiduino Mar 20 '21 at 16:10
  • @raspiduino, you don't need `string.h` for this particular program, but it is harmless to include it. – John Bollinger Mar 20 '21 at 16:12
  • Regarding the `tmp1` comment, it is _not OK_ ever - _especially_ when learning. This solution is not idiomatic. The function interface suggests insertion _in place_ i.e. modifying `str1`. – Clifford Mar 20 '21 at 17:48
  • @Clifford function interface suggests that it returns `char*` which is either global variable like strtok or new variable which should be deallocated by caller – Iłya Bursov Mar 20 '21 at 20:50
  • @IłyaBursov strtok() returns a pointer to a token in the string passed to it in the initial call - that need not be a global just because it is external to the function. The appropriate idiom here is that used strcat() modifying the string, or strcpy() modifying a caller provided buffer. That is as a string handling function it makes sense to behave in the idiomatic manner of the standard string library. My answer to the question perhaps makes my point clearer than this comment. Note _zero_ globals. – Clifford Mar 20 '21 at 21:10
  • @Clifford strtok should return ASCIIZ token, it can either put 0 into passed string, but this could fail because of readonly memory, this is why at least some implementations perform copy and return pointer to global variable – Iłya Bursov Mar 21 '21 at 00:19
  • @IłyaBursov Not any conforming implementation. But that is beside the point, it is no defence of the use of global data in this case. Even if strtok did behave as you suggest (it doesn't) it would not require global data. I suggest this discussion if off topic, perhaps you should post a question to resolve your misunderstanding. I am sure someone will explain it for you. https://www.embedded.com/a-pox-on-globals/ – Clifford Mar 21 '21 at 06:30
0

There is no string insert function. There is a strcat() function which appends. Implementing your own however is simple enough using the string primitives that are available:

char* string_insert( char* scr, const char* ins, size_t loc )
{
    size_t ins_len = strlen(ins) ;
    strcpy( &scr[loc + ins_len], &scr[loc] ) ;
    memcpy( &scr[loc], ins, ins_len ) ;
    return scr ;
}

Here the end of the string is moved to make space for ins, then ins copied to the gap.

Example usage:

int main()
{
    char str1[80] = "Hello world!" ;
    const char* str2 = "the " ;
    printf( "%s\n", string_insert( str1, str2, 6 ) ) ;

    return 0;
}

The insertion is done in place and directly modifies scr so needs no destination buffer. That would be the idiomatic behaviour given the interface the you have specified. The use of the global tmp1 and tmp2 arrays in your attempt are not good practice, and entirely unnecessary (as is always the case with globals). If you do want to modify a separate destination string (so scr is const), then you should pass that buffer as an argument:

char* string_insert( const char* scr, const char* ins, size_t loc, char* dest )
{
    size_t ins_len = strlen(ins) ;
    memmove( dest, scr, loc ) ;
    strcpy( &dest[loc + ins_len], &scr[loc] ) ;
    memcpy( &dest[loc], ins, ins_len ) ;
    return dest ;
}

int main()
{
    const char* str1 = "Hello world!";
    const char* str2 = "the " ;
    char str3[80] = "" ;
    
    printf( "%s\n", string_insert(str1, str2, 6, str3 ) ) ;

    return 0;
}

The use of const parameters allows string literals to be passed as arguments so for the first "in-place" version.:

char str[80] = "Hello world!" ;
printf( "%s\n", string_insert( str, "the ", 6 ) ) ;

And for the "destination buffer" version:

char str[80] = "" ;
printf( "%s\n", string_insert( "Hello world!", "the ", 6, str ) ) ;
Clifford
  • 88,407
  • 13
  • 85
  • 165