Author Topic: Typecasting pointer in C  (Read 11147 times)

0 Members and 2 Guests are viewing this topic.

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: Typecasting pointer in C
« Reply #25 on: November 06, 2018, 09:05:02 am »
As helius correctly said, they are not equal.
This is a source of confusion for many C beginners (and not so much beginners!): the false equivalence between pointers and arrays.

The most important rule is "array of type decays to pointer to type when used in an expression context", supplemented by the one for function parameters: "the declaration of a array of type function parameter is adjusted to pointer to type".

This is easily demonstrated with, e.g., the sizeof operator which can take either a parenthesized type or an expression, yielding clearly different results:
Code: [Select]
newbrain@neutron:~$ cat sizeof.c
#include <stdio.h>

int a[10];

void func( int parm[10] )
{
        printf("\nIn func()\n sizeof(parm)\t\t%zu\n sizeof(parm+0)\t\t%zu\n", sizeof(parm), sizeof(parm+0));
}

int main(void)
{
        printf("In main():\n sizeof(a)\t\t%zu\n sizeof(a+0)\t\t%zu\n", sizeof(a), sizeof(a+0));
        func( a );
        return 0;
}
newbrain@neutron:~$ ./sizeof
In main():
 sizeof(a)              40
 sizeof(a+0)            8

In func()
 sizeof(parm)           8
 sizeof(parm+0)         8

If the above rules are taken into consideration, the ambiguity is minimal. I won't go into C99 VLAs and [static nnn] function parameters :-X
Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: nForce

Offline nForceTopic starter

  • Frequent Contributor
  • **
  • Posts: 393
  • Country: ee
Re: Typecasting pointer in C
« Reply #26 on: November 06, 2018, 01:14:12 pm »
Sorry but I still don't understand:

Quote

Together with the definition of strings as arrays, this has some surprising consequences. Consider the main(int argc, char *argv[]) function. You can write the second parameter as char **argv, and as char argv[][], as well: they mean the same thing in a function prototype.

The main function never knows the dimensions of argv. Instead, it relies on the fact that strings are null-terminated and the number of strings in the argv array is passed in the first argument, argc. So when it calls strlen(argv[j]), it is guaranteed to only access legal elements of the multidimensional array, as long as (j<argc) is true. The result of strlen tells it how many elements of the given string it can access.


So if I add another parameter to a function int size where size means the size of array, I can have **array (pointer to a pointer) instead of writing as an array[][]?

 

Offline sokoloff

  • Super Contributor
  • ***
  • Posts: 1799
  • Country: us
Re: Typecasting pointer in C
« Reply #27 on: November 06, 2018, 01:33:25 pm »
Well, sort of, but almost surely not the way you're hoping.

If you take your original code and modify it along the lines above, the following works, but you still have to keep track of array indices yourself and do the offset math manually rather than having the compiler do it for you:

Code: [Select]
#include <stdio.h>

void pointerArray(int *A, int size) {
  for (int i = 0; i < 2; i++)
    for (int j = 0; j < size; j++)
      printf("A[%d][%d] = %d\n", i, j, A[i][size * i + j]);
}

int main() {
  int somearray[2][3] = {{1,2,3},{4,5,6}};
   
  pointerArray(&somearray[0][0], 3);

  return 0;
}
 
The following users thanked this post: nForce

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: Typecasting pointer in C
« Reply #28 on: November 06, 2018, 02:56:38 pm »
Code: [Select]
      printf("A[%d][%d] = %d\n", i, j, A[i][size * i + j]);

You left an [i] too much, the correct line would be:
Code: [Select]
      printf("A[%d][%d] = %d\n", i, j, A[size * i + j]);

Explanation:
The y[x] postfix operator takes its two arguments (one must be a pointer, the other one a scalar), sums them and then dereference the resulting pointer (C99 standard: 6.5.2.1 Array subscripting).
It is completely equivalent to (*((x)+(y))).
A[i] will then yield an integer, so the original line will throw an error during compilation, in fact:
Code: [Select]
newbrain@muon:~$ make intarr CFLAGS+="-std=c99 -pedantic"
cc -std=c99 -pedantic    intarr.c   -o intarr
intarr.c: In function ‘pointerArray’:
intarr.c:6:44: error: subscripted value is neither array nor pointer nor vector
       printf("A[%d][%d] = %d\n", i, j, A[i][size * i + j]);
                                            ^
<builtin>: recipe for target 'intarr' failed
make: *** [intarr] Error 1

Note that the [] operator is commutative, so (size * i + j)[A] gives exactly the same result.

Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: nForce

Offline sokoloff

  • Super Contributor
  • ***
  • Posts: 1799
  • Country: us
Re: Typecasting pointer in C
« Reply #29 on: November 06, 2018, 03:16:46 pm »
You left an [i] too much, the correct line would be:
Code: [Select]
      printf("A[%d][%d] = %d\n", i, j, A[size * i + j]);
Damn it. You're right. Thanks for the fix.

I probably ought to compile the code I post as answers to questions. ;)  :-DD
 

Offline nForceTopic starter

  • Frequent Contributor
  • **
  • Posts: 393
  • Country: ee
Re: Typecasting pointer in C
« Reply #30 on: November 06, 2018, 07:33:43 pm »
Hmm, why is then argv[][]? Argv as a 2-dimensional array, if we have just parameters in a 1D array, like this: ['first', 'second']?

So we should have just 1D array like c# or java has.

Sorry but these concepts aren't easy.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Typecasting pointer in C
« Reply #31 on: November 06, 2018, 07:59:49 pm »
Hmm, why is then argv[][]? Argv as a 2-dimensional array, if we have just parameters in a 1D array, like this: ['first', 'second']?

So we should have just 1D array like c# or java has.

Sorry but these concepts aren't easy.

It is 1-dimensional - declared as "char* argv[]" - array which consists of pointers to char, same as "char** argv"

"argv[_x]" will give you the pointer. This pointer points to a string, which is an array of chars. "argv[_x][_y]" will give you a char from the char array pointed to by "argv[_y]".
« Last Edit: November 06, 2018, 08:06:29 pm by NorthGuy »
 
The following users thanked this post: nForce

Offline nForceTopic starter

  • Frequent Contributor
  • **
  • Posts: 393
  • Country: ee
Re: Typecasting pointer in C
« Reply #32 on: November 07, 2018, 09:14:03 am »
So it's stored like this: {{'f','i',r','s','t'},{'s','e','c','o','n','d'}}?
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: Typecasting pointer in C
« Reply #33 on: November 07, 2018, 09:43:55 am »
So it's stored like this: {{'f','i',r','s','t'},{'s','e','c','o','n','d'}}?
No, actually you do not know where they are stored.
There's no guarantee that the the strings will be contiguous in memory, you are only guaranteed that they can be modified and will keep the values you assign.

See the attached picture.
Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: nForce

Offline sokoloff

  • Super Contributor
  • ***
  • Posts: 1799
  • Country: us
Re: Typecasting pointer in C
« Reply #34 on: November 07, 2018, 11:26:19 am »
So it's stored like this: {{'f','i',r','s','t'},{'s','e','c','o','n','d'}}?
No, actually you do not know where they are stored.
There's no guarantee that the the strings will be contiguous in memory, you are only guaranteed that they can be modified and will keep the values you assign.
Each string will, of course, be contiguous in memory. You just have no guarantee that argv[0] will be immediately before argv[1]. (I know that newbrain knows this; this clarification isn't for them.)

Also note that C strings are zero-terminated, so in memory they are actually:
{{'f','i',r','s','t',0} <arbitrary amount of gap, perhaps even negative> {'s','e','c','o','n','d',0}}
 
The following users thanked this post: newbrain, nForce

Offline legacy

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: Typecasting pointer in C
« Reply #35 on: November 07, 2018, 12:29:23 pm »
Code: [Select]
#define my_arg_size    27
typedef struct { char_t line[my_arg_size]; }   my1_arg_t;
typedef char_t                                 my2_arg_t[my_arg_size];

#define my_arg_n    10
private void do_init
(
    my1_arg_t my1_arg[],
    my2_arg_t my2_arg[]
)
{
    uint32_t i;
    uint32_t j;

    for (j = 0; j < my_arg_n; j++)
    {
        for (i = 0; i < my_arg_size - 1; i++)
        {
            my1_arg[j].line[i] = 'a' + i;
            my2_arg[j][i]      = 'a' + i;
        }
        my1_arg[j].line[i] = '\0';
        my2_arg[j][i]      = '\0';
    }
}

private void do_show
(
    my1_arg_t my1_arg[],
    my2_arg_t my2_arg[]
)
{
    uint32_t i;
    uint32_t j;

    for (j = 0; j < my_arg_n; j++)
    {
        console_out_uint32("", j, ": ");
        console_out_string("[", my1_arg[j].line, "] ");
        console_out_string("[", my2_arg[j], "] ");
        console_out_cr();
    }
}

Code: [Select]
private void app()
{
    my1_arg_t my1_arg[my_arg_n];
    my2_arg_t my2_arg[my_arg_n];

    do_init(my1_arg, my2_arg);
    do_show(my1_arg, my2_arg);

    console_out_uint32("sizeof(my1_arg_t))=", sizeof(my1_arg_t), "\n");
    console_out_uint32("sizeof(my2_arg_t))=", sizeof(my2_arg_t), "\n");
}

Code: [Select]
OrangeCube raaa $ obj/app
0: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
1: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
2: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
3: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
4: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
5: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
6: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
7: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
8: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
9: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
sizeof(my1_arg_t))=27
sizeof(my2_arg_t))=27
 

Offline sokoloff

  • Super Contributor
  • ***
  • Posts: 1799
  • Country: us
Re: Typecasting pointer in C
« Reply #36 on: November 07, 2018, 01:04:27 pm »
More fun with typecasting using legacy's code:
Code: [Select]
#define my_arg_size    27

#include <stdint.h>
#include <stdio.h>

typedef char char_t;

typedef struct { char_t line[my_arg_size]; }   my1_arg_t;
typedef char_t                                 my2_arg_t[my_arg_size];

#define my_arg_n    10
void do_init
(
    my1_arg_t *my1_arg,
    my2_arg_t *my2_arg
)
{
    uint32_t i;
    uint32_t j;

    for (j = 0; j < my_arg_n; j++)
    {
        for (i = 0; i < my_arg_size; i++)
        {
            my1_arg[j].line[i] = 'a' + i;
            my2_arg[j][i]      = 'a' + i;
        }
        my1_arg[j].line[my_arg_size-1] = '\0';
        my2_arg[j][my_arg_size-1]      = '\0';
    }
}

void do_show
(
    my1_arg_t my1_arg[],
    my2_arg_t my2_arg[]
)
{
  uint32_t i;
  uint32_t j;
 
  for (j = 0; j < my_arg_n; j++)
    {
      printf("%d: [%s] [%s]\n", j, my1_arg[j].line, (char *)my2_arg);
    }
}

int main()
{
  my1_arg_t my1_arg[my_arg_n];
  my2_arg_t my2_arg[my_arg_n];
 
  do_init(my1_arg, my2_arg);
  do_show(my1_arg, my2_arg);
 
  printf("sizeof(my1_arg_t))=%lu\n", sizeof(my1_arg_t));
  printf("sizeof(my2_arg_t))=%lu\n", sizeof(my2_arg_t));

  // On most compilers, this will also work, though is
  // not guaranteed by the language standard
 
  // Assuming the packing is the same, a
  // my1_arg_t and a my2_arg_t are laid out the same in memory

  printf("Don't rely on this next bit, but it will work on most compilers...\n");
  do_show((my1_arg_t *)my2_arg, (my2_arg_t *)my1_arg);

  return 0;
}

I'd wager that last part (casting a pointer to my2_arg_t as a pointer to my1_arg_t and vice versa and calling do_show with them) going work on most modern platform C compilers, but is not guaranteed by the standard.
 

Offline sokoloff

  • Super Contributor
  • ***
  • Posts: 1799
  • Country: us
Re: Typecasting pointer in C
« Reply #37 on: November 07, 2018, 01:06:35 pm »
Code: [Select]
MacBook-Pro-2:eevblog jsokoloff$ gcc test-2018-11-07.cc
MacBook-Pro-2:eevblog jsokoloff$ ./a.out
0: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
1: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
2: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
3: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
4: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
5: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
6: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
7: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
8: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
9: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
sizeof(my1_arg_t))=27
sizeof(my2_arg_t))=27
Don't rely on this next bit, but it will work on most compilers...
0: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
1: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
2: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
3: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
4: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
5: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
6: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
7: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
8: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
9: [abcdefghijklmnopqrstuvwxyz] [abcdefghijklmnopqrstuvwxyz]
 

Offline nForceTopic starter

  • Frequent Contributor
  • **
  • Posts: 393
  • Country: ee
Re: Typecasting pointer in C
« Reply #38 on: November 07, 2018, 01:51:03 pm »
So it's stored like this: {{'f','i',r','s','t'},{'s','e','c','o','n','d'}}?
No, actually you do not know where they are stored.
There's no guarantee that the the strings will be contiguous in memory, you are only guaranteed that they can be modified and will keep the values you assign.

See the attached picture.

I have made my own function which takes char **arg for testing. By the diagram which you provided:

Code: [Select]
void arguments(char **arg){
   
}

int main(int argc, char **argv){
   
    char a[][4] = {{'F', 'i', 'r','0'}, {'S', 'e', 'c','0'}};
    arguments(a);
   
    return 0;
}


But I get a compilation error. What would be then a right input?
 

Offline sokoloff

  • Super Contributor
  • ***
  • Posts: 1799
  • Country: us
Re: Typecasting pointer in C
« Reply #39 on: November 07, 2018, 02:12:58 pm »
Code: [Select]
#include <stdio.h>

void arguments(char **arg){
  printf("%s\n", arg[0]);
  printf("%s\n", arg[1]);
}

int main(int argc, char **argv){
   
  char *a0 = (char *) "Fir";  // The compiler will null-terminate a string for you
  char *a1 = (char *) "Sec";  // if you use string syntax
  // Note also that '0' is an ASCII 0 (an unshifted close-paren on most US keyboards)
  // while numeric 0 is the string terminator.

  char *a[2] = {a0, a1};

  arguments(a);
 
  return 0;
}

Code: [Select]
MacBook-Pro-2:eevblog jsokoloff$ gcc test-2018-11-07-2.cc
MacBook-Pro-2:eevblog jsokoloff$ ./a.out
Fir
Sec
« Last Edit: November 07, 2018, 02:14:49 pm by sokoloff »
 
The following users thanked this post: nForce

Offline helius

  • Super Contributor
  • ***
  • Posts: 3642
  • Country: us
Re: Typecasting pointer in C
« Reply #40 on: November 07, 2018, 04:33:34 pm »
But I get a compilation error. What would be then a right input?
Code: [Select]
#include <stdio.h>
char *arguments(char *arg, int c){
    return &arg[c];
}

int main(int argc, char **argv)
{
    char a[][4] = {{'F', 'i', 'r', '\0'},
                  {'S', 'e', 'c', '\0'}};
    printf("\n%s",arguments(a,0));
    printf("\n%s",arguments(a,4));
    return 0;
}
See https://onlinegdb.com/BkJ1NcxaX

Basically, the problem is that multi-dimensional arrays are stored as a single array in row-major order, whereas arguments passed as "char **" expect to receive a pointer to another pointer. Since the variable "a" points to an array of characters (representing a multi-dimensional array) and not to a pointer, the callee function misinterprets characters as pointers and segfaults. Instead either pass the array as a pointer to char (so that the callee just sees it as a single-dimensional array) (in the above code the cast is implicit, it could be written explicitly as printf("\n%s",arguments((char *)a, 0))), or write the callee function to receive an array with the same minor dimensions (all dimensions except the first) so that it knows how to access it (this was my previous code snippet).
« Last Edit: November 07, 2018, 04:44:38 pm by helius »
 
The following users thanked this post: nForce

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: Typecasting pointer in C
« Reply #41 on: November 07, 2018, 05:58:36 pm »
I have made my own function which takes char **arg for testing. By the diagram which you provided:

Code: [Select]
void arguments(char **arg){
   
}

int main(int argc, char **argv){
   
    char a[][4] = {{'F', 'i', 'r','0'}, {'S', 'e', 'c','0'}};
    arguments(a);
   
    return 0;
}


But I get a compilation error. What would be then a right input?
Does the compilation error point to a specific problem?
Let me guess, is it's something like "incompatible pointers"? ;)

In reality, you are not following my diagram (so, it was not clear enough, my bad  :-//).

Let's see what's happening here:
  • The arguments() function expects a pointer to pointer to char
  • You have declared a bi-dimensional array, with two rows of 4 character each.
  • The array, according to the rules, decays to a pointer to its first element when used in an expression (in this case, the function parameter).
  • The first element of the bi-dimensional array is, in fact, a 4 element array of char.
  • The function is then receiving a pointer to a 4 element array of char.
  • A pointer to a 4 element array of char is not compatible with a pointer to pointer to char.
  • Sadness ensues. No segfault, though, the program will not compile.

Quite a mouthful, I hope I made it clear enough...
So, what do we need to have this code working?
Something's gotta give: either we change the array, or we change the function parameter.
Let's go with the first option:
Code: [Select]
void arguments(char **arg){

}

int main(int argc, char **argv){
    char *a[] = { "Fir", "Sec" };
    arguments(a);

    return 0;
}

And now, let's go through the same exercise of analyzing the code!
  • As before, the arguments() function expects a pointer to pointer to char
  • We have now have declared a one-dimensional array of pointer to char with two elements*.
  • The array, according to the rules, decays to a pointer to its first element when used in an expression (in this case, the function parameter).
  • The first element of the array is, in fact, a pointer to char.
  • The function is then receiving a pointer to a pointer to char.
  • So, the type of the formal parameter matches 1-to-1 the type of the actual parameter.
  • Profit!

*Note, an important one at that:
A string literal (something between double quotes, 6.4.5) is an array of char, so it makes sense that, when used as an initializer, it decays to a pointer to char. The standard makes also explicit provision for a string literal to be used as initializer for an array of characters, 6.7.8 clause 14. So you could have written your original array as:
Code: [Select]
    char a[][4] = {"Fir", "Sec" };
As helius points out, the terminating NUL character is added automatically for string literals.

And this brings us to the second way to correct the code, changing arguments() parameter type:
Code: [Select]
void arguments(char (*arg)[4]){

}

int main(int argc, char **argv){

    // char a[][4] = {{'F', 'i', 'r','0'}, {'S', 'e', 'c','0'}};
    char a[][4] = { "Fir", "Sec" }; // As good as yours, but convenient...
    arguments(a);

    return 0;
}

I won't go through the whole story again, just take note that the function now takes (using the old trick of reading complicated declarations from the inside):
  • A parameter called arg: arg
  • Which is a pointer: *arg
  • To an array of 4 elements: (*arg)[4]
  • Of type char: char (*arg)[4]

HTH!
Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: nForce

Offline nForceTopic starter

  • Frequent Contributor
  • **
  • Posts: 393
  • Country: ee
Re: Typecasting pointer in C
« Reply #42 on: November 08, 2018, 05:45:37 pm »
Yes, it helps.  :)

Let's go in depth, with this statement:
Quote
We have now have declared a one-dimensional array of pointer to char with two elements*.

You mean by this, that the pointer points to an array of char? And these elements can have an arbitrary space between them as sokoloff pointed out.
But crucial here is if it points to an array of char in memory. So where an array starts in memory, there should point the pointer.

 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: Typecasting pointer in C
« Reply #43 on: November 08, 2018, 10:29:51 pm »
Yes, it helps.  :)

Let's go in depth, with this statement:
Quote
We have now have declared a one-dimensional array of pointer to char with two elements*.

You mean by this, that the pointer points to an array of char? And these elements can have an arbitrary space between them as sokoloff pointed out.
But crucial here is if it points to an array of char in memory. So where an array starts in memory, there should point the pointer.

Let's take the declaration again
Code: [Select]
    char *a[] = { "Fir", "Sec" };
  • We are declaring an object named a: a
  • Which is an array: a[]
  • Of pointers: *a[]
  • To char: char *a[]
  • Its size is two elements, as, though not directly specified, it is set by the initializer.

Now, this is almost exactly as the "argv" data structure is defined (nitpicking details below^).
When the name a is used in any expression (i.e. more or less any place that is not the declaration itself or the single parenthesized argument of a sizeof operator) the general rule applies: it will decay to a pointer to its first element, giving, in fact, a char **.
(And this is where the second rule would apply: a function parameter, if declared as char *arg[], is adjusted to yield a pointer, so it's equivalent to char **arg.)

This pointer ( "a" ) does not point to an array of char, but to an array of pointers to char (the elements of a).

Each element, in turn, is pointing to an array of char in memory, allocated where the compiler sees fit^.

This realizes exactly what my original picture tried to show: an array of char*, and the relative pointed to array of chars.

You might say, simplifying, that in C arrays are how stuff is stored in memory, but actual access to them is always realized through pointers, possibly with the help of the [] operator.
Pointers "know" the size of the type they are pointing to, so pointer arithmetic and [] works as expected.
Pointers do not know how many elements they are pointing to (0 is a corner case, 1 or more cover the case of a single variables and arrays).
A null pointer to any type has the value 0 and does not point to anything.

^Nitpicks: argv (or whatever you might want to call it, the name is just conventional) will always contain also a final null pointer element. Furthermore, the char arrays that represent string literals ("Fir", "Sec") are not modifiable (6.4.5, clause 6), while the ones pointed by argv elements can be modified (5.1.2.2.1, end of clause 2).

Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: nForce

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: Typecasting pointer in C
« Reply #44 on: November 08, 2018, 11:06:50 pm »
Code: [Select]
#define my_arg_size    27
....
  // On most compilers, this will also work, though is
  // not guaranteed by the language standard
 
  // Assuming the packing is the same, a
  // my1_arg_t and a my2_arg_t are laid out the same in memory

  printf("Don't rely on this next bit, but it will work on most compilers...\n");
  do_show((my1_arg_t *)my2_arg, (my2_arg_t *)my1_arg);

  return 0;
}
I'd wager that last part (casting a pointer to my2_arg_t as a pointer to my1_arg_t and vice versa and calling do_show with them) going work on most modern platform C compilers, but is not guaranteed by the standard.
It is actually guaranteed by the standard.  :-+
Two sections are relevant:
  • 6.7.2.1 Structure and union specifiers, clause 13:
    "...A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning."
  • 6.3.2.3 Pointers, clause 7:
    "...When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object."
So, not only the layout is the same but since to be passed to printf they are converted to char* (permitted by 6.3.2.3), it's not even undefined behaviour to print them!
(At least, I read it that way, but I'm ready to be corrected).
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline sokoloff

  • Super Contributor
  • ***
  • Posts: 1799
  • Country: us
Re: Typecasting pointer in C
« Reply #45 on: November 08, 2018, 11:38:25 pm »
I think that the key phrase is that "there may be unnamed padding within a structure object", meaning that a my1_arg_t might be (consistently) padded out to 2, 4, or 8 bytes in size and have the implementation still be a conforming C compiler. It more commonly comes up with bitfields than with a struct of a simple char.
 
The following users thanked this post: newbrain

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: Typecasting pointer in C
« Reply #46 on: November 09, 2018, 07:14:58 am »
Cor blimey, what was I thinking? :palm:
I should not post late at night. :-[

I simply overlooked the fact that we are using an array of structures vs. an array of arrays, and only considered the first members.
You are perfectly right.

Apart from the first element that must have the same layout in both arrays, the pointers might not be correctly aligned.
The mere possibility means that we are using undefined behaviour.


I think that the key phrase is that "there may be unnamed padding within a structure object", meaning that a my1_arg_t might be (consistently) padded out to 2, 4, or 8 bytes in size and have the implementation still be a conforming C compiler. It more commonly comes up with bitfields than with a struct of a simple char.

EtA: My post holds only when going to and from pointers to a specific element and then accessing it as a char[]
« Last Edit: November 09, 2018, 07:19:12 am by newbrain »
Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: sokoloff

Offline sokoloff

  • Super Contributor
  • ***
  • Posts: 1799
  • Country: us
Re: Typecasting pointer in C
« Reply #47 on: November 16, 2018, 01:41:47 pm »
An interesting article on the peculiarities of individual machines.

https://begriffs.com/posts/2018-11-15-c-portability.html
 
The following users thanked this post: newbrain

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: Typecasting pointer in C
« Reply #48 on: November 17, 2018, 12:14:51 pm »
An interesting article on the peculiarities of individual machines.

https://begriffs.com/posts/2018-11-15-c-portability.html

Thanks! A nice memory trip...
I don't see why they seem to think big endianness is an oddity  :-//, though it's becoming less and less common.

It's probably quite difficult to provide a (reasonably efficient) standard conforming implementation for some of those architectures.

For the most part, though, the various quirks can be easily ignored by abiding to a simple rule:
"Thou shalt not use undefined behaviour".
And if one wants to write portable programs, also implementation defined behaviour should be kept as low as possible.

One's complement and sign magnitude integer representations are explicitly supported by the standard (AFAICR, those and two's complement are the only admissible ones).
Non-zero null pointers look odd, but the compiler just needs to know what to do with a '0' constant expression when it converts it to a pointer.
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline andersm

  • Super Contributor
  • ***
  • Posts: 1198
  • Country: fi
Re: Typecasting pointer in C
« Reply #49 on: November 17, 2018, 01:42:38 pm »
One's complement and sign magnitude integer representations are explicitly supported by the standard (AFAICR, those and two's complement are the only admissible ones).
C++20 will only allow two's complement, and it is expected that the next revision of the C standard will follow suit. Right-shift of signed integers will also finally be codified as sign-extending.
 
The following users thanked this post: newbrain


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf