To understand the problem you're facing, you've to understand that two types, conceptually, are never the same (unless it's just a typedef). int and long are 2 different types according to the language. Although your implementation may have them equally sized, they needn't be the same across all implementations. If you're aiming for portable code, making an assumption that they will be equally sized will lead to issues. How?
Say we've an implementation where sizeof(int) is 4 and sizeof(long) is 8.
int i = 0;
long *l = &i;   // good that the compiler rejects this
If this was allowed, then you'd be getting into undefined behaviour. Say i lives at memory location 2000, since it's 4-byte sized, accessing till 2003 is allowed, since this memory is owned by the program. What lies beyond that is unkown to the program(mer) and is thus inaccessible.
|<------------- i ------------->|
+-------+-------+-------+-------+-------+-------+-------+-------+
| 0x00  | 0x00  | 0x00  | 0x00  |unknown|unknown|unknown|unknown|
+-------+-------+-------+-------+-------+-------+-------+-------+
  2000    2001    2002    2003    2004    2005    2006    2007
|<---------------------------- *l ----------------------------->|  // oops!
Since long is of size 8, making l point to i's address (2000) would mean it'll point to 2000 and if you dereference the pointer i.e. do *l to read what's in there, it'll try to read a long of size 8, there by trying to access 2004 to 2007 additional to reading 2000 to 2003. Thus leading you to undefined behaviour.
Avoid hacks like explicit conversion (type casting), do it the clean way. Use a temporary.
void need_long(long *data) { /* some code */ }
int i = 0;
long l = i;     // temporary long to pass it to need_long
need_long(&l);
If you are OK with not being portable and are confident that int and long are of the same size, on your implementation, you may additionally pass -fpermissive to allow this conversion which GCC prohibits by default.