8

I saw following piece of code in a legacy project.

/* token.c */
struct token id_tokens[MAX_TOKENS];

/* analyse.c (v1) */
extern struct token *id_tokens; /* Raised my eyebrow, id_token declares a pointer */

I insisted on changing analyse.c to contain the declaration as below:

/* analyse.c (v2) */
extern struct token id_tokens[]; /* I am happy with this. id_tokens declares array of unspecified size. */

I want v2 because pointer to T is not same as array of T. My friend's counter argumented that behaviour of both are same, so it doesn't matter whether I use v1 and v2.

Question 1: Does array of incomplete type deplete to a pointer?

Question 2: Is my friend right that both versions are behaviorally guaranteed to be equivalent?

Gyapti Jain
  • 4,056
  • 20
  • 40
  • 1
    See [this](http://c-faq.com/aryptr/aryptr1.html) **Array are not pointers** Many duplicates, idk what's confusion, in a way, you and your friend both are wrong – P0W Nov 14 '14 at 06:38
  • Arrays and pointers can be used interchangeably, because an array is just a group of variables of the same type in a line in memory, and a pointer simply points to the first element of a group of variables of the same type in a line in memory. That is why `char *foo` is the same as `char foo[]` – Gophyr Nov 14 '14 at 12:37
  • @Gophyr You are talking my friend's language. Check the answers below. They are indeed different. – Gyapti Jain Nov 14 '14 at 12:44
  • @GyaptiJain Technically an array is just a pointer to the beginning of the memory space as well. The memory is the same in both cases, it is only the representation in the code that looks different. – Gophyr Nov 14 '14 at 14:00
  • However, the array has a definite size associated with it, but that can be overrun, i.e. buffer overflows, etc. – Gophyr Nov 14 '14 at 14:01
  • @Gophyr No no no... array and pointers are not same. Only some of their usage may appear same, nothing more than that. – Gyapti Jain Nov 14 '14 at 14:05
  • @GyaptiJain How can they be completely interchangeable then? The compiler represents the array as a pointer to the beginning of a section of memory. The only difference is that when you declare an array, you give the compiler an expected size of the array. – Gophyr Nov 14 '14 at 14:13
  • @GyaptiJain If analyse.c v1 works, then this proves my point. – Gophyr Nov 14 '14 at 14:14
  • @Gophyr No `analyse.c v1` should not work. It appears in the legacy code mentioned by OP, this id_tokens is not used or worked well for some legacy (may be non-compliant) compiler. – Mohit Jain Nov 18 '14 at 08:23
  • @GyaptiJain I am not asking if it should work, I am asking if it does work. – Gophyr Nov 18 '14 at 12:32

3 Answers3

8

The first version is wrong. Arrays are NOT pointers, the declaration extern struct token *id_tokens; doesn't match the definition type struct token id_tokens[MAX_TOKENS];.

Reference: C FAQ: I had the definition char a[6] in one source file, and in another I declared extern char *a. Why didn't it work?. Also, see this.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
2

lets understand same stuff by program

test.c

#include<stdio.h>
#include"head.h"
struct token id_tokens[10];
int main()
{
printf("In original file: %p",id_tokens);
testing();
}

head.h

struct token {
int temp;
};

test1.c with v1

#include<stdio.h>
#include"head.h"
extern struct token* id_tokens;
void testing () {
printf("In other file %p",id_tokens);
}

Output : In original file: 0x601040In other file (nil)


test1.c with v2

#include<stdio.h>
#include"head.h"
extern struct token id_tokens[];
void testing () {
printf("In other file %p",id_tokens);
}

Output : In original file: 0x601040In other file 0x601040


This clearly shows that v1 is not correct and v2 is correct.

Jeegar Patel
  • 26,264
  • 51
  • 149
  • 222
  • Not casting id_tokens to `void *` for `%p` is UB. Why is it nil? – Gyapti Jain Nov 14 '14 at 07:19
  • In v1, You have declared id_tokens as extern pointer to struct token but there is not any global struct token in both files so it is null. – Jeegar Patel Nov 14 '14 at 07:22
  • +1 Very pragmatic way. Is there a spec reference suggesting the same> – Gyapti Jain Nov 14 '14 at 07:27
  • 1
    this things should be in spec. I do not read spec much. I usually clear my doubt by making such small program and clear the concept. – Jeegar Patel Nov 14 '14 at 07:30
  • @Mr.32 You can check my answer to understand why does it happen. Keypoint, when you write `struct token id_tokens[10];` in global scope, memory is initialized with zeros. – Mohit Jain May 13 '15 at 09:40
2
/* token.c */
struct token id_tokens[MAX_TOKENS];

/*
 id_tokens
  +-----+-----+-----+-----+...+-----+
  |     |     |     |     |   |     |
  +-----+-----+-----+-----+...+-----+
    [0]   [1]   [2]   [3]  ...  [MAX_TOKEN-1]

  To access id_tokens[i], add offset of ith element
  i.e. i * sizeof(struct token) to the **address**
  of array token
 */

So in your analyse.c, following instructions would be generated with this declaration.

  1. extern struct token id_tokens[];
    id_tokens[i]
    a. Address of id_tokens that might be linked from other compilation unit is taken
    b. offset of i is added
    c. Value is referenced
/* analyse.c (v1) */
extern struct token *id_tokens;

/*
 id_tokens
  +------+           +-----+...
  | addr |---------->|     |
  +------+           +-----+...


  To access id_tokens[i], fetch **contetnts** of pointer
  token, add offset of ith element i.e. i * sizeof(struct token)
  is added to this.
 */

So in your analyse.c, following instructions would be generated with this declaration:

  1. extern struct token *id_tokens;
    id_tokens[i]
    a. Contents from address of id_tokens that is linked from other compilation unit is taken.
    (Will result in compilation error if present in same compilation unit because of type mismatch)
    b. offset of i is added
    c. Value is referenced

Let's assume sizeof id_token[0] is 2 byte and sizeof pointer to id_token[0] is 4 byte.

Your later declaration may (mis)interprete the id_tokens[0] & id_tokens[1] as an address and add some offset to it (which may be an existing or non-existing address, aligned or non-aligned address who knows).

If it is your good day, program may crash or segfault immediately and you get a chance to fix the bug. If it is your bad day, program may just mess up with some other memory or communicate a wrong state to some module which can result in difficult to track bug and cause a nightmare.


Now I guess you understand why you got (nil) as output in Mr. 32's answer.

Community
  • 1
  • 1
Mohit Jain
  • 30,259
  • 8
  • 73
  • 100