0

In the following code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
    char** tab;
    int n;
}slist;

void print(slist* p);
void add(slist* p, const char* s);
void add(slist* p, const char* s)
{
    if(p->n==0)
    {
        p->tab=(char**)malloc(sizeof(char**));
    }
    strcpy(p->tab[p->n],s);
    p->n=p->n+1;
}
void print(slist* p)
{
    int i;
    printf("[");
    for(i=0;i<p->n;i++)
        printf(" %s",p->tab[i]);
    printf(" ]");
}
int main()
{
    char s1[25] = "Picsou";
    char s2[25] = "Flairsou";
    slist* p = (slist*)malloc(sizeof(slist));
    p->n=0;
    p->tab=NULL;
    add(p,s1);
    add(p,s2);
    print(p);
    return 0;
}

the function add() doesn't work, but if I change it to:

void add(slist* p, const char* s)
{
    if(p->n==0)
    {
        p->tab=(char**)malloc(sizeof(char**));
    }
    p->tab[p->n]=s;
    p->n=p->n+1;
}

it seems to work perfectly well. In the first case the output is only " ["; in the second case it is what is should be: " [ Picsou Flairsou ] ". I cannot understand why. I also tried this :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
    char** tab;
    int n;
}slist;

void print(slist* p);
void add(slist* p, const char* s);

void print(slist* p)
{
    int i;
    printf("[");
    for(i=0;i<p->n;i++)
        printf(" %s",p->tab[i]);
    printf(" ]");
}
void add(slist* p, const char* s)
{
    slist* tmp = (slist*)malloc(sizeof(slist));
    tmp->tab=(char**)malloc(sizeof(char*)*(p->n+1));
    int i;
    for(i=0;i<p->n;i++)
         tmp->tab[i]=(char*)malloc(sizeof(char));
    strcpy(tmp->tab[p->n],s);
    tmp->n=p->n+1;
    p = tmp;
}
int main()
{
    char* s1 = "Picsou";
    char* s2 = "Flairsou";
    slist* p = (slist*)malloc(sizeof(slist));
    p->n=0;
    p->tab=NULL;
    add(p,s1);
    add(p,s2);
    print(p);
    return 0;
}
  • 2
    You allocate space for the collection of pointers, but never allocate anything for those pointers to point to. – John3136 Feb 10 '17 at 02:43
  • I edited the post, I tried what you said, but it still doesn't work, i don't know what to do... But thank you for your answer ^^ – Maxime Fleury Feb 10 '17 at 03:01
  • @Maxime Fleury: You allocate memory for 1 char and then attempt to copy the entire string into that memory. No wonder it doesn't work. And why are you casting the result of `malloc`? – AnT stands with Russia Feb 10 '17 at 03:10
  • `slist* tmp = (slist*)malloc(sizeof(slist));`? You already allocated this in `main()`. You also don't need all those casts for `malloc()`. – RoadRunner Feb 10 '17 at 03:11
  • Also, why are you suddenly allocating a new list in `add`? You new version makes modifications to that new list and then throws it away. What is the point of that? – AnT stands with Russia Feb 10 '17 at 03:15
  • I'm learning c and i'm trying to do a list, in order to do that, i tried to do that : `p->tab[p->n+1]=malloc(sizeof(char*)); strcpy(p->tab[p->n],s); p->n=p->n+1; ` – Maxime Fleury Feb 10 '17 at 03:20
  • @MaximeFleury Stop and go back to the text books. Try something smaller and get that going first. You aren't a 1 star programmer yet but you are trying to write 2 star code (not having a go, just counting the `*`s in your pointer code). – John3136 Feb 10 '17 at 03:29
  • @MaximeFleury Why not try writing it all in `main()` first. Make sure you get that working first. I feel like your juggling too much at once. Get pointers down first, then start abstracting your code more. – RoadRunner Feb 10 '17 at 03:31
  • I cannot not do it that's a TP ... I'm a student, I'm just not good with pointers, I can code games, and graphical programs, but not in C or C++... I need to get better, and by doing in advence TPs I usaly get top grades but this time with thoses pointers my mind seg' fault... – Maxime Fleury Feb 10 '17 at 03:35
  • for ease of readability and understanding, 1) separate code blocks (for, if, else, while, do...while, switch, case, default) via one blank line. 2) separate functions by 2 or 3 blank lines (be consistent) – user3629249 Feb 10 '17 at 22:02
  • when calling any of the heap memory allocation functions (malloc, calloc, realloc) 1) do not cast the result. The type of the result is `void*` so can be assigned to any pointer. Casting just clutters the code, making it more difficult to understand, debug, maintain. 2) always check (!=NULL) the returned value to assure the operation was successful. – user3629249 Feb 10 '17 at 22:04
  • this line: `p->tab=malloc(sizeof(char**));` is allocating (on 32 bit architectures) 4 bytes. You really want to allocate `strlen(s)+1` bytes otherwise, if `s` is more than 3 bytes long, the call to `strcpy()` will overrun the receiving buffer, which is undefined behavior and can/will lead to a seg fault event. Suggest: `p->tab=malloc(strlen(s));` – user3629249 Feb 10 '17 at 22:08
  • the field `p->tab` is defined as a point to a list of pointers. the `add()` function only (ever) allocates room on the list of pointers for a single pointer (it should on each execution of the `add()` function use 'realloc()` to increase the size of that list of pointers by `p->n +1` Then allocate the memory needed for the string `s` and assign that resulting pointer to `p->tab[n]` Then finally call `strcpy()` with the destination being: `p->tab[n]` and the source being: `s` – user3629249 Feb 10 '17 at 22:18

1 Answers1

2

Lots of errors here, which is common with people who are new to dealing with pointers. Where to begin... I'll just go in the order things appear in code.

  1. This is not a "list".

    It's an array... sort of. If you say "list" to a C programmer, they will think you mean a linked-list.

  2. Incorrect allocation of array.

    if(p->n==0)
    {
        p->tab=(char**)malloc(sizeof(char**));
    }
    

    Here you have allocated enough to store a single pointer. The second time you call add, you're going to access memory off the end. You have also incorrectly cast the result (in C, you don't cast the return value from malloc). Additionally, you have given confusing information to the reader, because you intend to allocate an array that will hold elements of type char*, NOT char**.

    You must either allow the array to expand dynamically when required (not appropriate for your abilities right now - maybe try that in a few days), or set a maximum size. Let's do that.

    const int MAX_SIZE = 100;
    if( p->n==0 )
    {
        p->tab = malloc( MAX_SIZE * sizeof(char*) );
    }
    else if( p->n == MAX_SIZE )
    {
        printf( "Maximum size exceeded!\n" );
        return;
    }
    

    You could use calloc instead of malloc if you like. It will zero-initialise the block after allocating it: calloc( MAX_SIZE, sizeof(char*) )

  3. Copying to an uninitialized pointer.

    strcpy(p->tab[p->n],s);
    

    You allocated memory for tab, but you did not allocate memory that is pointed to by its elements, and here you have undefined behaviour (most likely resulting in a segmentation fault, but could do anything).

    Make sure you have a valid pointer, and the location it points has enough storage reserved for the data you are copying into it:

    p->tab[p->n] = malloc( strlen(s) + 1 );
    strcpy( p->tab[p->n], s );
    
  4. Storing potentially invalid pointer.

    Your alternative that "works perfectly well" uses:

    p->tab[p->n]=s;
    

    However, the only reason this works is because those pointers remain valid for the whole time that you use the "list" (but actually the program does not "work" because of reasons I highlighted in number 2.

    Sometimes we desire this behaviour in a program, and design our data structures to index pointers that they do not own. But more often, and especially for a beginner, you are better off copying the data (instead of simply copying the pointer). And so you'll instead use the approach I've suggested in number 3 above.

  5. No comment!!

    There are so many things wrong with the following code, that I'm not going to pull it apart or explain them.

    void add(slist* p, const char* s)
    {
        slist* tmp = (slist*)malloc(sizeof(slist));
        tmp->tab=(char**)malloc(sizeof(char*)*(p->n+1));
        int i;
        for(i=0;i<p->n;i++)
             tmp->tab[i]=(char*)malloc(sizeof(char));
        strcpy(tmp->tab[p->n],s);
        tmp->n=p->n+1;
        p = tmp;
    }
    

    However, it appears like you were attempting to do something similar to realloc. This is the option I mentioned in number 2 that I said you're maybe not ready for. But read up on it anyway: realloc

paddy
  • 60,864
  • 6
  • 61
  • 103
  • Nice answer :) +1 from me. – RoadRunner Feb 10 '17 at 07:02
  • this answer is good, however; Suggest also checking that each call to a heap allocation function (malloc, calloc, realloc) be immediately followed by a check (!=NULL) to assure the operation was successful. And the posted code fails to pass each of the allocated memory pointers to `free()` before exiting. While the OS will probably cleanup, that should never be depended upon – user3629249 Feb 10 '17 at 22:26
  • Yeah thanks for that. I intentionally left out the checks for clarity, but since I was listing points, I should have taken the time to cover that. – paddy Feb 13 '17 at 01:22