1

Before starting, I should say I have seen several posts around this topic (like this one, but I am still missing something. I am quite new to C, so please bear with me. I am attempting to construct a function that copies a string from one pointer location to another.

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

void astrncpy(char **base, char *copyme){

    printf("Copying '%s' into a location currently featuring the following string: '%s'\n", copyme,*base);
    printf("The location of our string to be copied (%s) is %d.\n", copyme, &copyme);
    printf("The location of our string to be replaced (%s) is %d.\n", *base, base);

    //Declare string length variable
    int new_len=strlen(copyme);
    printf("Calculating new length for replaced memory allocation (%d).\n",new_len);

    //Reallocate pointer array
    *base=realloc(*base,sizeof(char)*new_len+1);
    printf("Reallocating memory allocation.\n");

    //Copy copyme content to base string location
    strncpy(*base,copyme,new_len);

    printf("The string at location %d is now %s\n", base, *base);

}

void main(){

    //Declare iterator
    int i;

    //Generate strings
    char first_lit[]="Fortran?";
    char second[]="Now that's a name I've not heard in a long time.";

    //Convert first string to array (so we can get at the pointer to the strings pointer)
    char **first; //Declare pointer to pointer array that represents the first string
    first=malloc(strlen(first_lit)*sizeof(char)); //Allocate space for the pointer array
    *first=first_lit; //Assign values to the pointer locations

    //Copy copyme into base
    astrncpy(first,second);
}

When I attempt to reallocate within astrncpy(), the core dumps. From what I understand, this occurs if the pointer array is either not NULL, or is not the product of malloc(). I feel like I am not failing that test. Any guidance on what is happening would be appreciated.

Bonus points for insight on my construction of the input *base. I spent a good deal of time experimenting to determine what an acceptable input would be for the **base argument (note that both arguments to astrncpy() are given in the text I am working through). I am not clear, however, on why I can't get there with first_lit instead of having to construct first. The string is still an array, is it not? Again, help is appreciated.

UPDATE: I put this down for a while to do work I actually get paid to do, but coming back to it, I couldn't shake the idea that the declaration modifications in the response below weren't necessary. (This is because they had not yet been covered in the text.) In any event, I will still give a check to the solution because it works and was helpful on multiple fronts. It should be noted, however, that the following also works:

/*Program demonstrates string manipulation operations*/

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

void astrncpy(char **base, char *copyme){

    printf("\nWe are inside the function, astrncpy.\n");
    printf("\nCopying '%s' (*copyme) into a location currently featuring the following string: '%s' (**base)\n", copyme,*base);
    printf("The location of our string to be copied (%s) is %p.\n", copyme, &copyme);
    printf("The location of our string to be replaced (%s) is %p.\n", *base, base);

    //Declare string length variable
    size_t new_len=strlen(copyme);
    printf("Calculating new length for replaced memory allocation (%d).\n",new_len);

    //Reallocate pointer array
    printf("Reallocating memory block associated with base string to be replaced.\n");
    *base=realloc(*base,sizeof(char)*(new_len+1));


    //Copy copyme content to base string location
    strncpy(*base,copyme,new_len);

    printf("The string at location %p is now %s\n", base, *base);

}

void main(){

    //Declare iterator
    int i;

    //Generate strings
    char first_lit[]="Fortran?";
    char second[]="Now that's a name I've not heard in a long time.";
    //int testint=5;

    //Capture elements of first_lit in pointer array (so we can get at the pointer to the strings pointer)
    char *first; //Declare pointer to pointer array that represents the first string
    first=malloc((strlen(first_lit)+1)*sizeof(char)); //Allocate space for the pointer array
    strncpy(first,first_lit,strlen(first_lit)); //Assign values to the pointer locations

    //Copy copyme into base
    printf("Initiating copy operation...\n");
    astrncpy(&first,second);
}
Community
  • 1
  • 1
Marvin Ward Jr
  • 1,019
  • 1
  • 11
  • 30
  • Your linked question has an answer 'The only pointers that can be passed to realloc are null pointers and those that have been returned by calloc, malloc or realloc previously!' – KevinDTimm Aug 24 '15 at 20:43
  • NB: It's `int main(void)` -- C is not Java. – Jens Aug 24 '15 at 20:45
  • `first=malloc(strlen(first_lit)*sizeof(char))` - this is completely the wrong size calculation – M.M Aug 24 '15 at 21:09
  • `strncpy(*base,copyme,new_len);` - you forgot to null terminate the string. To fix that, use `strcpy(*base, copyme);` . The `strncpy` function is an unsafe version of strcpy and should be avoided – M.M Aug 24 '15 at 21:10

4 Answers4

4

As per the C11 standard, chapter §7.22.3.5, realloc() specification, (emphasis mine)

void *realloc(void *ptr, size_t size);

If ptr is a null pointer, the realloc() function behaves like the malloc() function for the specified size. Otherwise, if ptr does not match a pointer earlier returned by a memory management function, or if the space has been deallocated by a call to the free() or realloc() function, the behavior is undefined. [...]

In your case, *base is not a pointer returned by malloc() and family. Hence undefined behavior. And the segmentation fault follows.

That said, inside main(), the allocation for first also looks faulty. first being a char ** the memory allocation should look like

first = malloc(sizeof(*first) * num_of_pointers_needed);

instead of

first=malloc(strlen(first_lit)*sizeof(char));

Also, you should always check the return value of malloc() and family against NULL to ensure success before using the returned pointer.

Finally, a syntax like

 p = realloc(p, newsize);

is very dangerous, as, in case realloc() fails, you'll end up losing the actual pointer by assigning the return value of realloc() to the same pointer. Quoting the standard again,

[...] If memory for the new object cannot be allocated, the old object is not deallocated and its value is unchanged.

and

The realloc() function returns a pointer to the new object (which may have the same value as a pointer to the old object), or a null pointer if the new object could not be allocated.

You should always collect the return value of realloc() in some temporary pointer, perform the success check and assign it back to the actual pointer in case of success.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
4

From what I understand, this occurs if the pointer array is either not NULL, or is not the product of malloc().

The first part is not right. The second part is right. The argument to realloc can be NULL. If it is not NULL, it must be a value that was returned by the malloc family of functions.

The problem with your code is that *first in main is not a value that is returned by malloc or realloc. It is a simply a pointer to an array in main.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
1

You are passing pointer to the memory allocated in the stack, trying to realloc it. This is wrong. You should first malloc() string and then try to realloc() it.

Nickolay Olshevsky
  • 13,706
  • 1
  • 34
  • 48
1

Looking at these statements

first=malloc(strlen(first_lit)*sizeof(char)); //Allocate space for the pointer array
*first=first_lit; //Assign values to the pointer locations

it seems that you think that you may assign arrays with other arrays.

It is a wrong supposition. You may only copy elements of one array into another array.

Also take into account that you named the function like

astrncpy
    ^^

but usually such functions with n in the middle has a parameter of type size_t. Your function does not have such a parameter. So it would be better to name it like

astrcpy

that is without n in the middle of the function name.

Also usually string functions return pointer to the target strings.

What you mean is the following

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

char * astrcpy( char **base, const char *copyme )
{
    printf( "Copying '%s' into a location currently featuring the following string: '%s'\n", copyme,*base );
    printf( "The location of our string to be copied (%s) is %p.\n", copyme, ( const void * )copyme );
    printf( "The location of our string to be replaced (%s) is %p.\n", *base,  ( void * )*base );

    //Declare string length variable
    size_t new_len = strlen( copyme );
    printf("Calculating new length for replaced memory allocation (%zu).\n", new_len );

    //Reallocate pointer array
    printf("Reallocating memory allocation.\n");

    char *tmp;
    if ( ( tmp = realloc( *base, sizeof( char ) * ( new_len + 1 ) ) ) != NULL )
    {
        *base = tmp;

        //Copy copyme content to base string location
        strcpy( *base, copyme );

        printf( "The string at location %p is now %s\n", ( void * )*base, *base );
    }

    return tmp;
}

int main( void )
{
    //Generate strings
    char first_lit[] = "Fortran?";
    char second[] = "Now that's a name I've not heard in a long time.";

    //Convert first string to array (so we can get at the pointer to the strings pointer)
    char *first; //Declare pointer to pointer array that represents the first string

    first = malloc( ( strlen( first_lit ) + 1 ) * sizeof( char ) ); //Allocate space for the pointer array
    strcpy( first, first_lit ); //Assign values to the pointer locations

    //Copy copyme into base
    if ( astrcpy( &first, second ) ) puts( first );

    free( first );
}

The program output is

Copying 'Now that's a name I've not heard in a long time.' into a location currently featuring the following string: 'Fortran?'
The location of our string to be copied (Now that's a name I've not heard in a long time.) is 0x7ffe4b3d23d0.
The location of our string to be replaced (Fortran?) is 0x6da010.
Calculating new length for replaced memory allocation (48).
Reallocating memory allocation.
The string at location 0x6da010 is now Now that's a name I've not heard in a long time.
Now that's a name I've not heard in a long time.

It is interesting to note that function realloc did not changed the initial address of the memory. It simply enlarged it.:)

Before the realloc

The location of our string to be replaced (Fortran?) is 0x6da010.

and after the realloc

The string at location 0x6da010 is now Now that's a name I've not heard in a long time.

the address is the same 0x6da010

As for this question

I am not clear, however, on why I can't get there with first_lit instead of having to construct first

then you can not move arrays with static or automatic storage duration. You can only reallocate memory that was dynamically allocated and copy there an array.

Thus you can not reallocte memory for first_lit . You can dynamically allocate memory and copy there elements of the array first_lit and then you can reallocate this dynamically allocated memory.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • There is a lot of helpful info. Thanks to all (seriously), but this response was the most comprehensive. Before I check it off, though, I have a few questions. First, in the assignment of a statically stored array to a dynamic array, can the target dynamic array exceed the source array in size? Second, I didn't know about the `size_t` intrinsic, but why is `int` insufficient? Also, thanks for the info on using `n` in function names. It came from the text, but good to know. Finally, could have sworn I tried the `char *first`/`astrncpy(&first...)` combo, but I will give it another go. – Marvin Ward Jr Aug 25 '15 at 00:45
  • @Marvin Ward Jr You may copy a string stored in one array with less size to an array with greater size. Function strlen allow to determine how many elements are usind by the string in the array. size_t is an implementation defined unsigned integer type. This type is returned by the operator sizeof and by function strlen. So it is better to use that size. As for the last question then I did not understand it.. – Vlad from Moscow Aug 25 '15 at 07:16
  • No worries, last one wasn't a question, but rather a reference to my attempts to get the `**base` input into `atrscpy()` to work. In any event, why do you use `(void *)` instead of `&`? It seems to have the same effect, and in a couple places it seems unnecessary. (The program runs with either `(void *)*base` and `base`.) The only place it seems to matter is when a `const` call comes into play (`const void *) copyme`. Without `const` I had a seg fault. Is it correct to assume some effort to modify the `copyme` pointer occurred? Thanks again for all your help. – Marvin Ward Jr Aug 25 '15 at 11:00