0

I have been asked to build the from without using the library and pointers.

I have this so far but somehow it doesn't work:

void strcatO(char a[], char b[])
{
    int i = 0;

    for(i = 0; i < strlen(b); ++i)
    {
        a[strlen(a) + i + 1] = b[i];
    }

    printf("%s", a);
}

Output:

a

  • 1
    Note that when you declare an argument like `char a[]`, what the compiler really treats it as is `char *a`. So no, you're not doing it without pointers. – Some programmer dude Jan 26 '19 at 12:34
  • Guess you are right – Lidor Cohen Jan 26 '19 at 12:35
  • 2
    `strlen()` *is* from the library. And both `a` and `b` *are*, indeed, pointers, even if their declaration looks like arrays. Actually, if your teacher tells you they aren't, ask him to explain why `sizeof( a )` in your `strcat()` will *always* be equal to `sizeof( char * )`, no matter how large the array in the *calling* function is. Bottom line, since *any* array you use as an argument to a function call *will* degenerate into a pointer, your teacher did his / her students a disservice phrasing the task like that. The answer is, "it cannot be done, all I can do is writing it without `*`." – DevSolar Jan 26 '19 at 12:36
  • I built the strlen beforehand in another function, and the pointers thing I get it now thanks!! – Lidor Cohen Jan 26 '19 at 12:43
  • @DevSolar is correct. Arrays passed as arguments to functions are *always* "adjusted", as the technical term is, to pointers to their first elements. The declarations `void f(int arr[]);` and `void f(int *arr);` are the same thing; they are interchangeable. Even `void f(int arr[5]);` does not make a difference; the index is ignored. If you find the semantic and notational idiosyncrasies of C confusing you are not alone. – Peter - Reinstate Monica Jan 26 '19 at 12:44
  • "I built the strlen beforehand in another function,..." can have a bad effect with this code. Say the `b` string is length `N`. then `for(i = 0; i < strlen(b); ++i) { ... }` calls OP's `strlen(N)` N times. Each call running down `b` to find the length or `N` operations. Being OP's code, a compiler likely will not recognize this inefficiency and then make this code `O(N*N)` - very slow. Instead call `strlen(b)` before the loop or simply use `for(i = 0; b[i]; ++i)`. – chux - Reinstate Monica Jan 26 '19 at 14:54
  • 1
    @PeterA.Schneider: In `void f(int arr[5]);`, it is not clear that the array length is ignored. Some compilers evaluate the array size expression (after which the value is discarded). For example, with Apple LLVM 10.0.0 clang-1000.11.45.5, `void f(int arr[printf("Hello, world.\n")]) {} int main(void) { f(0); }` prints “Hello, world.” – Eric Postpischil Jan 26 '19 at 15:05

4 Answers4

2

out of your problems you continuously compute strlen for nothing hoping the compiler will optimize, you can do that :

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

void strcatO(char a[], char b[])
{
   size_t i = strlen(a);
   size_t j;

    for (j = 0; b[j] != 0; ++j)
    {
        a[i++] = b[j];
    }

    a[i] = 0;

    printf("%s\n", a);
}

int main()
{
  char a[20] = "aze";
  char b[] = "rtyu";
  strcatO(a,b);
  return 0;
}

Execution :

azertyu

Note that char a[] for a parameter is exactly char *, without pointers is false ;-)


and to point to the problems in your code as requested by Eric Postpischil :

  • a[strlen(a) + i + 1] writes 1 character after the right position, must be a[strlen(a) + 1] = 0; a[strlen(a)] = b[j];. In a way it is a chance else you will write more far after the end because strlen will not returns the initial length of a but an undefined value because of the probable missing null character in the rest of a
  • after the copy you miss to add the null character
bruno
  • 31,755
  • 7
  • 21
  • 36
  • Your answer is awesome thanks!! haven't learned about size_t yet so I don't quite understand but I did get what you did thank you – Lidor Cohen Jan 26 '19 at 12:42
  • @LidorCohen `size_t` is the type of the value return by _strlen_ and other functions, look at https://stackoverflow.com/questions/2550774/what-is-size-t-in-c – bruno Jan 26 '19 at 12:45
  • 1
    This answer fails to state what was wrong with the original code or why the new code fixes it. – Eric Postpischil Jan 26 '19 at 15:01
  • @EricPostpischil I did, but the initial code was so bad I supposed useless ^^ – bruno Jan 26 '19 at 15:14
  • @EricPostpischil you are right, I tend to think "hide this code that I can not see" from a certain level of problem, giving an other code. – bruno Jan 26 '19 at 15:18
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/187365/discussion-between-chux-and-bruno). – chux - Reinstate Monica Jan 26 '19 at 16:08
2

somehow it doesn't work

a[strlen(a) + i + 1] = b[i]; appends characters after a's null character.

void strcatO(char a[], char b[]) {
    int i = 0;
    for(i = 0; i < strlen(b); ++i) {
      a[strlen(a) + i + 1] = b[i];  // Oops: appending position is off-by-one
    }
    printf("%s", a);
}

strcatO("ab", "cd") will populate a as 'a', 'b', '\0', 'c', 'd'.

Printing that with printf("%s", a); only prints 'a', 'b'.


To fix, code needs to append in the right position, yet this overwrites the original a null character. Thus calls to strlen(a) are bad.

Instead, and to improve efficiency, do not call strlen() repeatedly.

void strcatO(char a[], const char b[]) {
  size_t ai = 0;
  while (a[ai]) {       // go to end of a
    ai++;
  }

  size_t bi = 0;
  while (b[bi]) {        // while not at the end of b ...
    a[ai++] = b[bi++];
  }

  a[ai] = '\0';
  printf("<%s>", a);
}

Details of subtle improvements:

const in const char b[] implies b references data that this function should not attempt to change. This 1) allows this function to concatenate b should it be a const char [] 2) Allows optimizations a weak compiler may not see.

size_t is better than int for long strings which could be longer than INT_MAX. size_t is the "right size" type for string lengths and array sizing. OP (Original Poster) did have "without using the library" and size_t is from the library, so code could use unsigned or better unsigned long as alternatives.

chux - Reinstate Monica
  • 113,725
  • 11
  • 107
  • 213
1

This line:

a[strlen(a) + i + 1] = b[i];

writes characters one position further than you want.

When called in your example, your routine is passed a and b with these contents:

a[0] = 'e'
a[1] = 'g'
a[2] = 'g'
a[3] = 0

b[0] = 's'
b[1] = 'a'
b[2] = 'm'
b[3] = 'p'
b[4] = 'l'
b[5] = 'e'
b[6] = 0

You want to produce this result:

a[0] = 'e'
a[1] = 'g'
a[2] = 'g'
a[3] = 's'
a[4] = 'a'
a[5] = 'm'
a[6] = 'p'
a[7] = 'l'
a[8] = 'e'
a[9] = 0

However, since your code writes to a[strlen(a) + i + 1], it writes the first character to a[strlen(a) + 0 + 1], which is a[4]. You want it in a[3]. You could change strlen(a) + i + 1 to strlen(a) + i, but then, when you have written the first character, you will have overwritten the null terminating character, and strlen will not work to find the length anymore. To fix this, you can remember the length of a before entering the loop. Consider this code:

int i = 0;
int LengthOfA = strlen(a);
for (i = 0; i < strlen(b); ++i)
{
    a[LengthOfA + i] = b[i];
}

That will write the characters to the correct place.

However, it does not put a null terminating character at the end of a. To do that, we can put another statement after the loop:

a[LengthOfA + i] = 0;

At that point, your routine will work for normal situations. However, there are two more improvements we can make.

First, instead of using int for lengths and indices, we can use size_t. In C, the width of int is flexible, and size_t is provided as a good type to use when dealing with sizes of objects. To use it, first use #include <stddef.h> to get its definition. Then your code can be:

size_t i = 0;
size_t LengthOfA = strlen(a);
for (i = 0; i < strlen(b); ++i)
{
    a[LengthOfA + i] = b[i];
}
a[LengthOfA + i] = 0;

Second, your code nominally calculates strlen(b) in every iteration. This is wasteful. It is preferable to calculate the length once and remember it:

size_t i = 0;
size_t LengthOfA = strlen(a);
size_t LengthOfB = strlen(b);
for (i = 0; i < LengthOfB; ++i)
{
    a[LengthOfA + i] = b[i];
}
a[LengthOfA + i] = 0;
Eric Postpischil
  • 141,624
  • 10
  • 138
  • 247
0

You are not overwriting the first string null(\0) terminator

    a[strlen(a) + i + 1] = b[i];

should be

int len = strlen(a);

for(i = 0; i < strlen(b); ++i)
{
    a[len + i] = b[i];
}
a[len+i] = '\0'; //Finally null terminate the new string.
kiran Biradar
  • 12,116
  • 3
  • 14
  • 35
  • ![a](https://i.gyazo.com/358a49e1bbbc7f7f0b8aacfe6022758e.png) This is the new output, the overall size of the array 'a' is 25 – Lidor Cohen Jan 26 '19 at 12:36
  • Given OP wrote their own `strlen()`, this code suffers the same [O(N*N) inefficiency](https://stackoverflow.com/questions/54378372/c-building-strcat-without-the-library-and-without-pointers#comment95572497_54378372). No need to repeatedly call `strlen(b)`. – chux - Reinstate Monica Jan 26 '19 at 14:59
  • This answer fails to state what was wrong with the original code or why the new code fixes it. – Eric Postpischil Jan 26 '19 at 15:01
  • 1
    @EricPostpischil This does does state a key problem with "You are not overwriting the first string null", which at least is a partial explanation. – chux - Reinstate Monica Jan 26 '19 at 15:04