简体   繁体   中英

Confusion about static and dynamic arrays in C

typedef struct mystruct{
  int a;
  char arr[10];
  char *str;
}mystruct;


void f(void *data, int offset){
  char *s = (char *)(data + offset);
  printf("%s", s);
}

void g(void *data, int offset){
  char *s = *(char **)(data+offset);
  printf("%s", s);
}

int main(){
  mystruct test;
  test.a = 2;
  test.str = malloc(100);
  strcpy(test.arr, "Hello ");
  strcpy(test.str, "World!");
  f(&test, offsetof(mystruct,arr));
  g(&test, offsetof(mystruct,str));
  return 0;
}

I am wondering why do I need two different ways to print strings. In function f , what is (data + offset) actually pointing at? Is it not pointing to arr which is a char pointer to the first element of the string? But in function g , (data + offset) is pointing to a char pointer too. So why two different approaches must be used to do the same task?

In both cases data+offset points to a member of the struct .

But lets look at the structure of the the struct. It consists of

+-----+--------------------------------------------+
| a   | sizeof(int) probably 4 or 8 bytes          |
+-----+--------------------------------------------+
| possible padding of unknown size (probably zero) |
+-----+--------------------------------------------+
| arr | 10 bytes                                   |
+-----+--------------------------------------------+
| possible padding of unknown size (maybe 2 bytes) |
+-----+--------------------------------------------+
| str | sizeof(char*) probably 4 or 8 bytes        |
+-----+--------------------------------------------+

and elsewhere in memory is a block of 100 byte allocated with malloc .

Notice that the data for test.arr is stored in the memory allocated for test , but the thing stored in test for test.str is the address of another block of memory.

You have to give the compiler information about the type of pointer you have in functions f and g , you can't do point arithmetic on void pointers.

Cast data to a char pointer and the printf's will work

void f(void *data, int offset){
  char *s = (char *)(( char*)data + offset); 
  printf("%s", s);
}

void g(void *data, int offset){
  char *s = *(char **)((char*)data+offset);   
  printf("%s", s);
}

In function f , what is (data + offset) actually pointing at? Is it not pointing to arr which is a char pointer to the first element of the string?

This is your primary source of confusion. arr is not a pointer, it is an array. Arrays are not pointers. Meanwhile, str is a pointer. "Arrays" and "pointers" are two absolutely different things. They have virtually nothing in common. And this is exactly why you have to work with them differently.

Arrays and pointers can behave very similarly in so called value contexts (ie when used as rvalues ), but this is purely superficial similarity. They immediately reveal their major differences in so called object contexts (ie when used as lvalues ). In your specific example your member-accessing code is an example of object context , which is why you have to carefully observe the difference between arr and str .

The matter of differences between arrays and pointers has been covered many times already. I don't see any reason to repeat it here. Just do a search on "array pointer difference" on SO. Or read the essential FAQ

http://c-faq.com/aryptr/index.html

PS Also note that C language does not support pointer arithmetic on void * pointers. All of your your (data + offset) expressions are invalid. What you wanted to do is really ((char *) data + offset) . The conversion of data from void * to char * is required in order to be able to perform point arithmetic with byte-offsets.

In your struct, at (presumably) offset 4, is a list of 10 bytes one after each other, containing the letters that make 'Hello'; So (data+4) is a pointer at char and needs to be dcoded accordingly (ie char * ).

However, after those 10 bytes, come a few bytes that make the address of a buffer somewhere, ie those bytes are a 'char *' (you defined them so), so data+offset there is a pointer at a char-pointer or a char ** .

What is probably confusing is that both

strcpy(test.arr, "Hello ");
strcpy(test.str, "World!");

work.

This is a confusing (but useful feature of C/C++). The name of an array, when used in a place that requires a pointer to the type of the array element, will be treated by the compiler as if it were a pointer to the first element of the array.

So test.str is clearly a pointer to char (because you defined it that way). test.arr can be used as a pointer to the first element of test, if the situation suggests it.

When you write strcpy(test.arr, "Hello "); what the compiler assumes you mean is strcpy(&test.arr[0], "Hello ");

In function f, what is (data + offset) actually pointing at?

It is pointing at the 'arr' member of the structure object (if you assume GCC's semantics for the void pointer arithmetic).

Is it not pointing to arr which is a char pointer to the first element of the string?

arr is a char array, not a char pointer.

But in function g, (data + offset) is pointing to a char pointer too.

In this case, it is pointing to a char pointer, yes.

So why two different approaches must be used to do the same task?

The pointers are to two different things - one to a char pointer (which itself points at a char array) and the other to a char array. There is one more level of indirection in the first case.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM