2

There is a pointer-to-an-Array of Arrays i.e. NameList in the code. I want the contents of each of the Arrays in the Pointer(NameList) to get printed one by one. The below code is not able do the task. Pls. help.

int Data1[] = {10,10};
int Data2[] = {20,20};
int Data3[] = {30,30};

int *NameList[] = {Data1, Data2, Data3};
main()
{  Function(NameList); }

Function(int *ArrayPointer)
  {
    int i, j, index=0;
    for (i=0; i < 3; i++)
      {
        for (j=0; j < 2; j++)
          {
             //It does not print the data
             printf("\nName: %s",  ArrayPointer[index++]);
          } 
        index=0;         //Counter reset to 0
        ArrayPointer++;  //Pointer is incremented by one to pick next array in the pointer
      }
  }
print("code sample");

Another note from the original poster of the question:

I have completed a pacman game in Turbo C. I was polishing some graphics routines so that it can be reused again easily. This is only a small sample created for the purpose of help and understanding the concept. All data in the code actually are char arrays for sprites. Now i simply want to call a function passing the pointer so that each arrays in the pointer are drawn to the screen. How can this code be modified to handle this? Im actually stuck up here.

  • Why did you roll back my commit? I added appropriate tags (C and C++) and make the source code readable by putting it under "code tags" for Markdown... – strager Jan 02 '09 at 04:39
  • I didn't roll it back, but I did re-format it. – recursive Jan 02 '09 at 04:50
  • could there maybe a bug involved? if he edits it while you retag it, maybe changes made by him automatically reverts your changes? – Johannes Schaub - litb Jan 02 '09 at 07:41
  • i got in that situation too, and got distracted by the OP and complained :) – Johannes Schaub - litb Jan 02 '09 at 08:01
  • You should be getting a warning for passing NameList to ArrayPointer: NameList is an int pointer pointer value, so ArrayPointer should be an int pointer pointer, not an int pointer. Or you should make the call Function(*NameList), not Function(NameList). – Jegschemesch Jul 01 '09 at 01:21

6 Answers6

5

Darn it litb, once again you beat me to the punch by mere minutes. (If only I didn't have kids who keep waking up...)

Ahh, what the hell. Perhaps this will still be useful to somebody.


Oh, and just to nail this thing down:

  • Arrays, such as int a[4] allocate memory space for their data.
  • Pointers, such as int * p allocate just enouch memory space for a pointer to another spot in memory.
  • That's why we can use sizeof on arrays and get the full memory footprint, but not on pointers.

Other than that little distinction, there really isn't a big difference between int[] and int*. (Consider how many folks declare *main(int argc, char **argv) vs main(int argc, char * argv[]).)


ATTENTION: All memory addresses here are fictional. I'm just making them up to illustrate a point.

Given:

int Data1[] = {10,11};
int Data2[] = {20,22};
int Data3[] = {30,33};

We now have 3 blocks of memory. Say:

0xffff0000-0xffff0003  with a value of (int)(10)
0xffff0004-0xffff0007  with a value of (int)(11)

0xffff0008-0xffff000b  with a value of (int)(20)
0xffff000c-0xffff000f  with a value of (int)(22)

0xffff0010-0xffff0013  with a value of (int)(30)
0xffff0014-0xffff0017  with a value of (int)(33)

Where:

Data1 == & Data1 [0] == 0xffff0000
Data2 == & Data2 [0] == 0xffff0008
Data3 == & Data3 [0] == 0xffff0010

NO, I'm not going to get into big-endian vs little-endian byte ordering here!

Yes, in this case, Data1[2] == Data2[0]. But you can't rely on your compiler laying things out in memory the same way I've laid them out here.

Next:

int *NameList[] = {Data1, Data2, Data3};

So we now have another block of memory. Say:

0xffff0018-0xffff001b  with a value of (int*)(0xffff0000)
0xffff001c-0xffff001f  with a value of (int*)(0xffff0008)
0xffff0020-0xffff0023  with a value of (int*)(0xffff0010)

Where:

NameList == & NameList [0] == 0xffff0018

Note that NameList is of int ** type, and NOT int* type!

We can then write:

void Function(int **ArrayPointer)
{
  for ( int i=0; i < 3;  i++ )
    for ( int j=0; j < 2; j++)
      printf("Name: %d\n",  ArrayPointer[i][j] );
}

int main() {  Function(NameList); }

ArrayPointer resolves to (int**)0xffff0018.

ArrayPointer[0] == *( (int**) 0xffff0018 ) == (int*)(0xffff0000) == Data1.

ArrayPointer[0][1] == *( ( * (int**) 0xffff0018 ) + 1 ) == (int) * ( (int*)0xffff0000 + 1 ) == (int) * (int*) 0xffff0004 == Data1[1].


You may want to review pointer arithmetic: array[N] == *( array + N )

Mr.Ree
  • 8,320
  • 27
  • 30
3

main has to return a type. You forget to put "int" as a return type (implicit int in C++ is banned).

Having said that, i'm not sure what you mean by

// It does not print the data
printf("\nName: %s",  ArrayPointer[index++]);

ArrayPointer[index++] would, as it is defined in the parameter list, return an int. How is that supposed to store a name ? It will store an integer!

Once again, that said, you can't call that Function (pun intended) with that particular argument. Let's view your types:

int Data1[] = {10,10};
int Data2[] = {20,20};
int Data3[] = {30,30};

int *NameList[] = {Data1, Data2, Data3};

Data1      Data2      Data3      NameList
int[2]     int[2]     int[2]     int*[3]

Contrary to what you said, NameList is not a pointer to an array of arrays. I feel i need to show you what that would be:

int (*NameList)[N][M] = Some3DimensionalArray;

That wouldn't make sense at all. So what do you have?

Data1 = array of 2 int
Data2 = array of 2 int
Data3 = array of 2 int
NameList = array of poiners to int

That is what you got. And you pass NameList to a Function that wants a pointer to an int. It must fail already at the time you call Function in main! I've got no idea what you mean by name in that line in Function. But if you want to print out the integers that are stored in the arrays pointed to (by pointers to their first element), you can do it like this (keeping your code as much as i can):

// don't forget the return type, mate. Also see below
void Function(int **ArrayPointer)
  {
    int i, j, index=0;
    for (i=0; i < 3; i++)
      {
        for (j=0; j < 2; j++)
          {
             // It does not print the data. It is not a string, 
             // but an int!
             printf("\nName: %d\n",  ArrayPointer[i][index++]);
          } 
        index=0;         //Counter reset to 0
        // no need to increment the pointer. that's what the indexing by i 
        // above does
        // ArrayPointer++;  
      }
  }

I keep preaching people asking questions the difference between a pointer and an array. It's crucial to write correct code. I hope i could help. At the end, just a little though about the difference between int[] and int*. The first is an incomplete array type, while the second is a complete type (pointer to int):

typedef int Single[]; // array of indeterminate size.
typedef int *Pointer; // pointer to int

Single s1 = { 1, 2, 3, 4 }; // works!
Pointer s2 = { 1, 2, 3, 4 }; // no no, doesn't work. s2 wants an address

s2's type now has a type different from int[], because you initialized the array which would have incomplete type, the array s1 become complete after defined. It has type of int[4]. In parameter lists, however, there exist a special rule, which will cause any array type (even complete ones!) to be equivalent to a pointer to their first argument. Thus:

void f(int *a) <=> void f(int a[]) <=> void f(int a[42]);
void f(int (*a)[42]) <=> void f(int a[][42]) <=> void f(int a[13][42])
// ...

That's because you can't pass arrays by value. The compiler abuses that to make array types equivalent to pointers to their first element. Same deal with functions:

void f(int a()) <=> void f(int (*a)())

Because you can't pass functions by value (huh, doesn't even make sense at all to me), the compiler abuses it to make a function type in a parameter list equivalent to a pointer to that function type.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • I get a segmentation fault with your fixed example. I think that since you are incrementing ArrayPointer in the outer loop, you need (*ArrayPointer)[index++] rather than ArrayPointer[i][index++]. Of course a better approach is ArrayPointer[i][j] and don't bother with index at all. – Paul Stephenson Jan 02 '09 at 14:56
  • ah yeah didn't notice the increment of ArrayPointer. thanks :) – Johannes Schaub - litb Jan 02 '09 at 17:52
0

I made some corrections in your program please find them and compare. I know its too late for the response. But I saw this today itself.

int Data1[] = {10,10};
int Data2[] = {20,20};
int Data3[] = {30,30};

int *NameList[] = {Data1, Data2, Data3};

main()
{  
  Function(NameList); 
}

Function(int *ArrayPointer)
{ 
    int i, j, index=0;
    for (i=0; i < 3; i++)
      {
        for (j=0; j < 5; j++)
          {
             //It does not print the data
             printf("\nName: %d\n", *((int *)ArrayPointer[i] + index));
         index++;
          } 
        index=0;         //Counter reset to 0
      }
  }

Explanation:

When you pass NameList to Function as a pointer to an integer, what gets passed is the address of the first element of the array, which happens to be Data1 (an address to an array). But since this address is held in an array of ints, it will be considered as an integer. To make it behave like an address to an array(or, for that matter pointer to an int) you need to cast it to (int *). Thats what I did in :

printf("\nName: %d\n", *((int *)ArrayPointer[i] + index));

good question
  • 17
  • 1
  • 3
0
int Data1[]

has the type of

int *

and

int *NameList[]

has the type of

int **

You have a two-dimensional array there. Most likely you meant:

Function(int **ArrayPointer)

On that note, ANSI C/C89/C99 functions have an explicit return type (or void), e.g.

int main() { ... }
void Function() { ... }

The value pointed to by ArrayPointer is an int, not a string. Thus

printf("\nName: %s",  ArrayPointer[index++]);

Should be written as something else.

A third thing: index == j in your code. Thus index can be removed in favor of j.

i and j are probably not good variable names here because ArrayPointer is not name describing a list of something.

If you need more help, please post what you're looking to do, because you code has several bugs (and oddities) in it.

strager
  • 88,763
  • 26
  • 134
  • 176
  • int* is a pointer to an int, int[] is an array (of unspecified length) of int. They are fundamentally different types. If have a function `int f(int b) { return data[b]; }` then it will mean different things - and generate different code - if data is declared `int* data;` vs. `int data[];` – CB Bailey Jan 02 '09 at 07:33
  • charles, you can have data be of incomplete array type: extern int data[]; then doing data[b] means the same as if data is int* if i'm not mistaken. (you just can't take the size of it. sizeof(data) is going to be an error, if data is not defined in between with (of course) a complete array type). – Johannes Schaub - litb Jan 02 '09 at 07:55
  • You can declare main(int argc, char * argv[]). – Mr.Ree Jan 02 '09 at 08:18
  • @litb, have a look at some generated assembler, if data is extern int[], then data[n] refers to the data location n * sizeof int beyond the address of data. If data is extern int* then data[n] loads a pointer value from data and accesses relative to the pointer value *stored* at data. – CB Bailey Jan 02 '09 at 09:16
  • Yes, you can declare main as int main(int argc, char* argv[]) because type of a function is determined by converting all parameters of type "array of T" to "pointer to T". This doesn't mean that outside of a function declaration the types int[] and int* are somehow the same. – CB Bailey Jan 02 '09 at 09:26
  • @mrree.myopenid.com, You can declare main as int main(time_t ****bar) if you really want. =] Behaviour is undefined, though. For my purposes I was merely pointing out the lack of a return type. – strager Jan 02 '09 at 09:26
  • @Bailey, Can you provide your test case? I really doubt, in my experience, the following two are different: int array[] = {data...}; int *ptr = array; array[n] VERSUS ptr[n] – strager Jan 02 '09 at 09:27
  • I'm not saying that, in your case, array[n] and ptr[n] are different, they refer to the same object. However array and ptr are different objects with different types. See here for an assembly demonstration: http://ccgi.hashpling.plus.com/blog/extern-arrays-and-pointers/ – CB Bailey Jan 02 '09 at 09:33
  • Wait a sec: Just because with array[n] g++ (internally) can access the address directly, but with pointer[n] g++ first has to load the address from memory, array[n] & pointer[n] are different objects/types? That's nothing more than a minor optimization in linker implementation. – Mr.Ree Jan 02 '09 at 15:55
  • No, it's not an optimization, it's a difference in behaviour. If you declare data as extern int *data but define it (presumably in a separate source file) as extern int data[], the the compiler will generate incorrect code because it won't perform the necessary extra indirection. – CB Bailey Jan 02 '09 at 16:29
  • Charles, oh you are totally right. when i worked on my gcc port i wondered about the definition of _end and thought i could just exchange extern char*_end; and extern char _end[]; now i understand the difference. you got clue about this :) too bad i can't vote your comment – Johannes Schaub - litb Jan 07 '09 at 17:25
0

Judging by your "answer" (you really should have edited your original post and added this information), you probably want something like this:

void draw_all_sprites(Sprite *sprites, size_t num_sprites)
{
    Sprite *cur_sprite;
    size_t i;

    for(i = 0; i < num_sprites; ++i)
    {
        draw_sprite(cur_sprite);
        ++cur_sprite;
    }
}

A fairly simple for loop to iterate through elements in an array.

Community
  • 1
  • 1
strager
  • 88,763
  • 26
  • 134
  • 176
0

Paramod,

is there a function you need to call that takes something along the lines of

void drawSprite(int *data, int rows, int cols)

?

Then you need to have all data in a single chunk of memory. In your example, the compiler allocates a separate chunk for every row, and then another chunk to hold the three pointers to rows. Thus, the array is kept in 4 different places.

What you need is a multidimensional array rather than array of arrays. Initialize your data like this:

int data[3,2] = {{10,10},{20,20},{30,30}};

Then you can call your function like this:

drawSprite(&data[0,0], 3, 2);

Using multidimensional array places all elements in one block of memory. The advantage is, you can pass the piointer to first element, and you know where all other elements are. The disadvantage - all rows are allocated the same size.