3

Python spoiled me and trying to wrap my mind around C now is being a bloodbath of stupid errors. This is one I can't quite understand.

I wanted the C equivalent of Python's os.path.split, but there's no exact equivalent. strsep looks similar enough, but needs some massaging to be used simply.

First off, I defined my path type: a string of given length.

#define MAX_PATH_LEN 200 /* sigh */
typedef char t_path[MAX_PATH_LEN];

Then I wrote some code that does the actual massaging, attempting to avoid side effects -- just to keep things fool proof.

typedef struct {
    t_path next;
    t_path remainder;
} t_path_next

t_path_next path_walk_into(t_path path) {
    t_path_next output;

    t_path my_next, my_remainder = "/";
    strncpy(my_next, path, MAX_PATH_LEN);

    strsep(&my_next, my_remainder);

    output.remainder = my_remainder;
    output.next = my_next;
    return output;
}

gcc, however, is not impressed.

badp@delta:~/blah$ gcc path.c -Wall
path.c: In function ‘path_walk_into’:
path.c:39: warning: passing argument 1 of ‘strsep’ from incompatible pointer type
/usr/include/string.h:559: note: expected ‘char ** __restrict__’ but argument is of type ‘char (*)[200]’
path.c:41: error: incompatible types when assigning to type ‘t_path’ from type ‘char *’
path.c:42: error: incompatible types when assigning to type ‘t_path’ from type ‘char *’

I am baffled by the note -- how are char ** and char (*)[200] really different -- but the error is even weirder. I want to assign a variable I declared t_path in a field of type t_path, but I don't get to.

Why is that?


For anybody interest here's the correctly working version of the function:

t_path_next path_walk_into(t_path path) {
    t_path_next output;
    t_path my_path, delim = "/";
    char* my_path_ptr = my_path;    
    strncpy(my_path, path, MAX_PATH_LEN);

    strsep(&my_path_ptr, delim); //put a \0 on next slash and advance pointer there.

    if (my_path_ptr == NULL) //no more slashes.
        output.remainder[0] = 0;
    else
        strncpy(output.remainder, my_path_ptr, MAX_PATH_LEN);
    strncpy(output.next, my_path, MAX_PATH_LEN);

    return output;
}
badp
  • 11,409
  • 3
  • 61
  • 89
  • Part of the problem is that in C, arrays decay to pointers in various situations. If you try using `char*` instead of `char[...]`, then you may get better results. I haven't tried it, though :) – Merlyn Morgan-Graham Jan 02 '11 at 21:05
  • 5
    Using `typedef` with an array type is a sure formula or enraging everyone who ever reads your code and tries to figure out WTF it's doing. Just don't do that. – R.. GitHub STOP HELPING ICE Jan 02 '11 at 21:05
  • If the previous comment left you wondering (it did leave _me_ wondering), here's [an explanation](http://stackoverflow.com/questions/4523497/typedef-fixed-length-array/4523537#4523537). It doesn't look like a particularly strong point to me (code will still work correctly), but it's a valid one still. – badp Jan 02 '11 at 21:18
  • 3
    "how are `char **` and `char (*)[200]` really different" - the same way `char*` and `int*` are really different. They point to different things. The first points to a `char*` (and `strsep` is defined to store a pointer value into the location indicated by its first argument), the second points to a `char[200]`. Since an array is not the same thing as a pointer, it's no good passing a pointer to an array when what is needed is a pointer to a pointer. – Steve Jessop Jan 02 '11 at 22:13

2 Answers2

4

The errors: You can't directly assign to an array, such as a string, in C. You need to copy char by char, or call str(n)cpy, which does it for you.

Thomas Padron-McCarthy
  • 27,232
  • 8
  • 51
  • 75
3
  • For the warning : you are probably already aware that array may decay to pointer. That is, for example, what makes an array acceptable as an argument to a function where a pointer is expected. In your case, what you have is a pointer to an array : there is no reason for such a thing to get converted to a pointer to pointer.

    For the record, the C99 standard says (6.3.2.1/3) :

    Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.

    You're in the context of a unary & : no conversion for you.

  • For the error : it has already been answered, but array assignment is not directly possible. You might want to use strcpy or strncpy.

icecrime
  • 74,451
  • 13
  • 99
  • 111
  • Thanks for that, so would an explicit cast suffice to change to the pointer to array to a pointer to pointer? – badp Jan 02 '11 at 21:35
  • 1
    @badp: No. strsep expects a pointer to a pointer, so it can change that (second) pointer. Your variable my_next is a t_path, which is an array and not a pointer, so there _is_ no pointer for strsep to change! – Thomas Padron-McCarthy Jan 02 '11 at 21:47