How to determine the length of a function? - c

Consider the following code that takes the function f(), copies the function itself in its entirety to a buffer, modifies its code and runs the altered function. In practice, the original function that returns number 22 is cloned and modified to return number 42.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ENOUGH 1000
#define MAGICNUMBER 22
#define OTHERMAGICNUMBER 42
int f(void)
{
return MAGICNUMBER;
}
int main(void)
{
int i,k;
char buffer[ENOUGH];
/* Pointer to original function f */
int (*srcfptr)(void) = f;
/* Pointer to hold the manipulated function */
int (*dstfptr)(void) = (void*)buffer;
char* byte;
memcpy(dstfptr, srcfptr, ENOUGH);
/* Replace magic number inside the function with another */
for (i=0; i < ENOUGH; i++) {
byte = ((char*)dstfptr)+i;
if (*byte == MAGICNUMBER) {
*byte = OTHERMAGICNUMBER;
}
}
k = dstfptr();
/* Prints the other magic number */
printf("Hello %d!\n", k);
return 0;
}
The code now relies on just guessing that the function will fit in the 1000 byte buffer. It also violates rules by copying too much to the buffer, since the function f() will be most likely a lot shorter than 1000 bytes.
This brings us to the question: Is there a method to figure out the size of any given function in C? Some methods include looking into intermediate linker output, and guessing based on the instructions in the function, but that's just not quite enough. Is there any way to be sure?
Please note: It compiles and works on my system but doesn't quite adhere to standards because conversions between function pointers and void* aren't exactly allowed:
$ gcc -Wall -ansi -pedantic fptr.c -o fptr
fptr.c: In function 'main':
fptr.c:21: warning: ISO C forbids initialization between function pointer and 'void *'
fptr.c:23: warning: ISO C forbids passing argument 1 of 'memcpy' between function pointer and 'void *'
/usr/include/string.h:44: note: expected 'void * __restrict__' but argument is of type 'int (*)(void)'
fptr.c:23: warning: ISO C forbids passing argument 2 of 'memcpy' between function pointer and 'void *'
/usr/include/string.h:44: note: expected 'const void * __restrict__' but argument is of type 'int (*)(void)'
fptr.c:26: warning: ISO C forbids conversion of function pointer to object pointer type
$ ./fptr
Hello 42!
$
Please note: on some systems executing from writable memory is not possible and this code will crash. It has been tested with gcc 4.4.4 on Linux running on x86_64 architecture.

You cannot do this in C. Even if you knew the length, the address of a function matters, because function calls and accesses to certain types of data will use program-counter-relative addressing. Thus, a copy of the function located at a different address will not do the same thing as the original. Of course there are many other issues too.

In the C standard, there is no notion of introspection or reflection, thus you'd need to devise a method yourself, as you have done, some other safer methods exists however.
There are two ways:
Disassemble the function (at runtime) till you hit the final RETN/JMP/etc, while accounting for switch/jump tables. This of course requires some heavy analysis of the function you disassemble (using an engine like beaEngine), this is of course the most reliable, but its slow and heavy.
Abuse compilation units, this is very risky, and not fool proof, but if you know you compiler generates functions sequentially in their compilation unit, you can do something along these lines:
void MyFunc()
{
//...
}
void MyFuncSentinel()
{
}
//somewhere in code
size_t z = (uintptr_t)MyFuncSentinel - (uintptr_t)MyFunc;
uint8_t* buf = (uint8_t*)malloc(z);
memcpy(buf,(char*)MyFunc,z);
this will have some extra padding, but it will be minimal (and unreachable). although highly risky, its a lot faster that the disassemble method.
note: both methods will require that the target code has read permissions.
#R.. raises a very good point, your code won't be relocatable unless its PIC or you reassasmble it in-place to adjust the addresses etc.

Here is a standards compliant way of achieving the result you want:
int f(int magicNumber)
{
return magicNumber;
}
int main(void)
{
k = f(OTHERMAGICNUMBER);
/* Prints the other magic number */
printf("Hello %d!\n", k);
return 0;
}
Now, you may have lots of uses of f() all over the place with no arguments and not want to go through your code changing every one, so you could do this instead
int f()
{
return newf(MAGICNUMBER);
}
int newf(int magicNumber)
{
return magicNumber;
}
int main(void)
{
k = newf(OTHERMAGICNUMBER);
/* Prints the other magic number */
printf("Hello %d!\n", k);
return 0;
}
I'm not suggesting this is a direct answer to your problem but that what you are doing is so horrible, you need to rethink your design.

Well, you can obtain the length of a function at runtime using labels:
int f()
{
int length;
start:
length = &&end - &&start + 11; // 11 is the length of function prologue
// and epilogue, got with gdb
printf("Magic number: %d\n", MagicNumber);
end:
return length;
}
After executing this function we know its length, so we can malloc for the right length, copy and editing the code, then executing it.
int main()
{
int (*pointerToF)(), (*newFunc)(), length, i;
char *buffer, *byte;
length = f();
buffer = malloc(length);
if(!buffer) {
printf("can't malloc\n");
return 0;
}
pointerToF = f;
newFunc = (void*)buffer;
memcpy(newFunc, pointerToF, length);
for (i=0; i < length; i++) {
byte = ((char*)newFunc)+i;
if (*byte == MagicNumber) {
*byte = CrackedNumber;
}
}
newFunc();
}
Now there's another bigger problem though, the one #R. mentioned. Using this function once modified (correctly) results in segmentation fault when calling printf because the call instruction has to specify an offset which will be wrong. You can see this with gdb, using disassemble f to see the original code and x/15i buffer to see the edited one.
By the way, both my code and yours compile without warnings but crash on my machine (gcc 4.4.3) when calling the edited function.

Related

Different pointer value before and after return

I have strange problem, i allocated memory using malloc and returned address of this newly allocated memory. But this address is different before and after return (inside and outside function).
Here is the code (care only about 3 last lines):
char* InfoFile_getValue(char* projectName, char* key)
{
char* returnValue = NULL;
char projectInfoPath[200];
sprintf(projectInfoPath,"projects/%s/info.txt",projectName);
FILE* fp = fopen(projectInfoPath,"r");
if (fp != NULL) {
char* ptr;
size_t len = 0;
char* lineFromFile = NULL;
while(getline(&lineFromFile, &len, fp)!=-1)
{
if (strstr(lineFromFile,key))
{
ptr = lineFromFile + strlen(key);
ptr = ptr + strspn(ptr, ": ");
returnValue = malloc(strlen(ptr)+1);
strcpy(returnValue,ptr);
break;
}
}
free(lineFromFile);
fclose(fp);
}
printf("Inside size: %d\n",sizeof(returnValue));
printf("String address inside function %p\n", returnValue);
return returnValue;
}
And then i call this function using:
char* baseString = InfoFile_getValue(projectName, "base");
printf("Outside size: %d\n",sizeof(baseString));
printf("String address outside function: %p\n",baseString);
printf("%s\n", baseString);
I comipled this using CC and below flags:
pkg-config --cflags gtk+-3.0 -Wno-incompatible-pointer-types
-Wno-int-conversion -Wno-discarded-qualifiers pkg-config --libs gtk+-3.0 -lpthread -lm
And it gives following results:
Inside size: 8
String address inside function 0x5567d9ff4540
Outside size: 8
String address outside function: 0xffffffffd9ff4540
Naruszenie ochrony pamięci (zrzut pamięci)
It looks like it cuts this address to 4 bytes and prefixes it with 0xff, but i have no idea why, i have never came across problem like that. Any suggestions will be helpfull.
You seem to get a sign extension of the lower 32 bits of the address. Just a wild guess, but I think that the compiler saw InfoFile_getValue as a function returning an int while pointers are 64 bits long.
The rule is that a function has to be declared before it is used. If it is not, C assumes that it is declared as int func(), that is a function taking any parameters and returning an int. You should make sure that you have:
char* InfoFile_getValue(char* projectName, char* key);
before the function (maybe main) containing char* baseString = InfoFile_getValue(projectName, "base");
That looks like sign extension, so this very much smells like a C90 implicit int bug. That is, the compiler thinks that the function returns int with the value 0xd9ff4540, which on a 32 bit system is most likely a negative 2's complement number. Then somehow it gets converted to 64 bit because of %p and you get sign extension.
Easiest way to solve it this bug to stop using C90 already, it's bloody 30 years old, broken and dangerous. Getting rid of implicit int alone is reason enough to port your code to standard C.
In case you have to use it, make sure that the function declaration and definition are identical and that the caller can see the function declaration. Then maximize compiler warnings and pay attention to them.
Your experiencing value-truncation and sign-extension due to an ill-prepared caller. I.e. the caller isn't aware of the actual function return type due to lack of proper prototype. This can be aggravating unless you keeping a close watch in C, especially when dealing with a code base that seems to work just fine on platforms where int and void* are the same size.
The simplest case to repro this is below, and documents what must be going on.
main.c
#include <stdio.h>
int main()
{
void *p = foo();
printf("main: p = %p\n", p);
}
foo.c
#include <stdio.h>
void *foo()
{
static int x;
void *p = &x;
printf("foo: p = %p\n", p);
return p;
}
Executing the above code after build for both x86 (where int and void* are the typically the same size) and x64 (where int is typically 32bit and void is 64bit) will expose both the problem and the subtlety of how you can miss this in this former case. Both should exhibit warnings similar to this (which you should be treating as errors anyway)
1>main.c(13): warning C4013: 'foo' undefined; assuming extern returning int
1>main.c(13): warning C4047: 'initializing': 'void *' differs in levels of indirection from 'int'
The results of the run on both platforms (obviously the values here can vary on your system)
x86
foo: p = 00A38138
main: p = 00A38138
x64
foo: p = 00007FF7E7B0C160
main: p = FFFFFFFFE7B0C160
With that you can hopefully see how two things are critical:
Always ensure functions you're calling within your source are properly prototyped before their usage.
Always compile with warnings-as-errors to catch problems like this.
Probably the biggest thing to keep in the back of your head is, by not doing both items above, the code still appears to run fine on x86, and still compiles on x64. The former can lull you into a false sense of accomplishment, and the latter just further confirms that, making hunting down problems like this especially irritating. Let the compiler help you. Make sure (1) and (2) are in your routine.

I want to implement a stack but printf throws a segmentation fault

I want to implement a stack using structures in C. Unfortunately, printf throws a segmentation fault. Perhaps there is a problem with dynamic allocation. Does anyone know how to solve it?
I have been facing this issue for the last two days. Your help will be very helpful for my study.
Here is my code
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define SIZE 256
typedef int (*pf)();
typedef struct _stack{
int arr[SIZE];
int top;
pf push, pop, peek, isEmpty, size, clear, print;
} stack;
void *nstack(){
stack *pstack = (stack *)malloc(sizeof(stack));
void push(int data) {
if(pstack->top < SIZE-1) pstack->arr[++pstack->top]=data;
}
int pop() {
return (pstack->top >= 0)?pstack->arr[pstack->top--]:0;
}
int peek(){
return (pstack->top >= 0)?pstack->arr[pstack->top]:0;
}
bool isEmpty(){
return (pstack->top >= 0)?false:true;
}
int size(){
return pstack->top+1;
}
void clear(){
pstack->top = -1;
}
void print(){
if(!isEmpty()){
for(int i = 0; i <= pstack->top; i++) printf("%d", pstack->arr[i]);
printf("\n");
}
}
pstack->push=push;
pstack->pop=pop;
pstack->peek=peek;
pstack->isEmpty=isEmpty;
pstack->size=size;
pstack->clear=clear;
pstack->print=print;
pstack->top=-1;
return pstack;
}
void dstack(stack *pstack){
free(pstack);
}
void main() {
stack *A = nstack();
A->push(1);
A->push(4);
A->push(6);
printf("%d",A->pop());
printf("%d",A->pop());
dstack(A);
}
While the code compiles (with warning), it try to leverage GCC extensions for functions within functions. However, the internal functions must be called within a valid context - they try to access the local pstackof the nstack function - but it does not exists.
While this style work in many OO langauges (Java, and possibly C++), that support 'closures' or lambda, it does not work for C. Consider changing the interface for each of the function to take stack *, and change the calling sequence to pass it.
void push(stack *pstack, int data) {
if(pstack->top < SIZE-1) pstack->arr[++pstack->top]=data;
}
main() {
...
A->push(A, 1) ;
...
printf("%d", A->pop(A) ;
}
Really, you should edit your question and provide the exact errors but I've decided to do some of the legwork for you since you're probably pretty new to this.
So first thing I did was compile your code here with -Wall and look what I get:
SO_stack.c: In function ‘nstack’:
SO_stack.c:49:17: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
pstack->push=push;
^
SO_stack.c:52:20: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
pstack->isEmpty=isEmpty;
^
SO_stack.c:54:18: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
pstack->clear=clear;
^
SO_stack.c:55:18: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
pstack->print=print;
^
SO_stack.c: At top level:
SO_stack.c:66:6: warning: return type of ‘main’ is not ‘int’ [-Wmain]
void main() {
Let's look at the first error on line 49: pstack->push=push;
You've defined push function prototype as: void push(int data) but pstack->push is of type pf which is defined as int (*pf)(); See the problem here? You're trying to pass an argument to a function pointer that is not properly defined to handle the argument AND the return type is different. This is totally wrong. Your push(int data) implementation declares a return type of void and a parameter of int yet your pointer to this function declares a return type of int and a parameter of void. This is the case with push, isEmpty, clear, and print. You're going to have to decide if all of these functions need to have the same prototype or if you need to create several different function pointer types to handle them, etc...
So that's the first problem.
Second problem is that as the warning says, you have a void main() prototype for your main function. You should return int from main and specify a return code to the caller of main (likely the OS)... Commonly, successful execution returns 0 and failure returns -1 but this is specific to the platform so you can instead return EXIT_SUCCESS on success and return EXIT_FAILURE upon failure from main. For this macros to be defined, you need to have #include <stdlib.h> present, which you do.
Next issue is that as a commenter wrote, you should learn to use a debugger such as GDB, LLDB, or Windows Debugger so that you can figure out exactly where the program crashes.
I've not re-written your program because it has so many issues that I don't think it would be constructive to do so in this iteration, however, provide an exact error next time, and use the debugger to see if the crash happens inside of the actual printf() code as you implied, or it happens because you supplied corrupt memory to the printf function. My guess is that it is the latter... Meaning, it is in fact probably your code which is flawed and supplying an invalid char * to printf which is either out of bounds, nonexistent, etc... This is precisely what you will use a debugger to find out, by placing a breakpoint before the trouble code and watching the memory to see what's going on.
You need to either remove int data.
Work on this some more, and you will probably find the rest of the issues yourself, if there are any. You should compile with flag -Wall and consider compiling with flag -Werror to clear this kind of stuff up yourself in the future.

Confused about detecting the errors of this code in c programming language?

The code given below is an exercise that our teacher gave to prepare us for exams.
We are supposed to find the errors that occur in this code and fully explain them .
#define SIZE 10
int start (void a,int k) {
const int size=10;
char array[size];
char string[SIZE];
mycheck(3,4);
array[0]=string[0]='A';
printf("%c %c\n", array[0], string[0]);
myRec(7);
}
int mycheck(int a , int b) {
if (a==0 || b==0 ) {
return 0;
}
else {
return (a*b);
}
}
int myRec(int x) {
if(x==0)
return 0;
else
printf("%d,",x);
myRec(x--);
}
I have found these errors so far:
1.int start (void a,int k)
explanation: We can't have a variable of type void, because void is an incomplete type
2.const int size=10;
explanation:we can't use variable to define size of array
(problem is when I run it in dev-c++ it doesn't show an error so I'm not sure about this)
3.mycheck(3,4);
explanation: prototype of function mycheck() is not declared, so the function mycheck is not visible to the compiler while going through start() function
4.A friend told me that there is an error in function myRec because of this statement myRec(x--);
(I don't really get why is this an error and how you can I explain it?)
5.Main() function doesn't exist.
I'm not sure about this but if i run the code (in dev-c++) without main function I get a compilation error
I'm not sure if the errors that I pointed out are 100% right or if I missed an error or if I explained them correctly.
Please correct me if any of the above is wrong!
a friend told me that there is an error in function myRec cuz of this
statement myRec(x--);
It will lead to stackoverflow. Due to post-decrement, the actual argument passed to function myRec(), never decreases and therefore the condition:
if(x==0)
return 0;
will never become true. Regarding your rest of the errors, it depends on the compiler version being used:
For example C99, you are allowed to have variable size arrays like this:
const int size=10;
char array[size];
char string[SIZE];
but pre C99, you would have to use malloc or calloc. For your functions used without prototype, most compilers would generate a warning and not error and also due to no #include<stdio.h> statement, your printf would also lead to a warning.i Again, lot of these things are compiler dependent.
1.int start (void a,int k)
explanation: We can't have a variable of type void ,because void is an
incomplete type
Correct.
2.const int size=10;
explanation:we can't use variable to define size of array (problem is
when i run it in dev-c++ it doesnt show an error?so im not sure about
this!)
This is also correct, that char array[size];, where size is not a compile-time constant, is invalid in C89. However, in C99 and newer, this is actually valid and would create a variable-length array. It is possible that your Dev-C++ IDE is using GCC with the language set to C99 or newer, or has GNU C extensions enabled to enable this feature.
3.mycheck(3,4);
explanation: prototype of function mycheck() is not declared.So the
function mycheck is not visible to the compiler while going through
start() function
Correct. This can be fixed either by declaring the function's prototype before the start() function, or just moving the whole function to the top of the file. As noted by Toby Speight in the comments, in C89, this should not actually be a compiler error, since functions are implicitly declared when they are used before any actual declaration as int (), i.e. a function returning int with any arguments, which is compatible with the declarations of mycheck and myRec. It is however bad practice to rely on this, and implicit function declaration does not work in C99 or newer.
4.a friend told me that there is an error in function myRec cuz of this statement myRec(x--);
(I don't really get why is this an error and how you can explain it?)
This function is a recursive function. This means it calls itself within itself in order to achieve a kind of looping. However, this function as it is currently written would run forever and cause an infinite loop, and since it is a recursive function, and needs a new stack frame each time it is called, it will most likely end in a stack overflow.
The function is written with this statement:
if(x==0)
return 0;
This is intended to terminate the recursion as soon as x reaches 0. However, this never happens, because of this line of code here:
myRec(x--);
In C, postfix -- and ++ operators evaluate to their original value before the addition or subtraction:
int x = 5;
int y = x--;
/* x is now 4; y is now 5 */
However, using the prefix version of these operators will evaluate to their new value after adding / subtracting 1:
int x = 5;
int y = --x;
/* x is now 4; y is now 4 */
This means that on each recursion, the value of x never actually changes and so never reaches 0.
So this line of code should actually read:
myRec(--x);
Or even just this:
myRec(x - 1);
5.Main() function doesn't exist ...again im not sure about this but if i run the code (in dev-c++) without main function i get a compilation
error
This one could either be right or wrong. If the program is meant to run on its own, then yes, there should be a main function. It's possible that the function start here should actually be int main(void) or int main(int argc, char *argv[]). It is entirely valid however to compile a C file without a main, for example when making a library or one individual compilation unit in a bigger program where main is defined in another file.
Another problem with the program is that myRec is used before it is declared, just like your point 3 where mycheck is used before it is declared.
One more problem is that the functions start and mycheck are declared to return int, yet they both do not contain a return statement which returns an int value.
Other than that, assuming that this is the entire verbatim source of the program, the header stdio.h isn't included, yet the function printf is being used. Finally, there's the issue of inconsistent indentation. This may or may not be something you are being tested for, but it is good practice to indent function bodies, and indentation should be the same number of spaces / tab characters wherever it's used, e.g.:
int myRec(int x) {
if(x==0)
return 0;
else
printf("%d,",x);
myRec(x--);
}
1) Hello friend your Recursive function myRec() will go infinite because it
call itself with post detriment value as per C99 standard it will
first call it self then decrements but when it call itself again it have
to do the same task to calling self so it will never decrements and new
stack is created and none of any stack will clear that recursion so
stack will full and you will get segmentation fault because it will go
beyond stack size.
2) printf("%d,",x); it should be printf("%d",x); and you should include #include library.
I think your another mistake is you are calling your mycheck() and you
returning multiplication of two integer but you are not catch with any
value so that process got west.So while you are returning something you
must have to catch it otherwise no need to return it.
3) In this you Program main() function missing. Program execution starts
with main() so without it your code is nothing. if you want to execute
your code by your own function then you have to do some process but
here main() should be present.or instead of start() main() should
be present.
4) you can also allocate any char buffer like this int j; char array[j=20];
your code should be like this.
#include<stdio.h>
#define SIZE 10
int mycheck(int a , int b) {
if (a==0 || b==0 ) {
return 0;
}
else {
return (a*b);
}
}
int myRec(int x) {
if(x==0)
return 0;
else
printf("%d",x);
myRec(--x);
}
void main (int argc, char** argv) {
const int size=10;
char array[size];
char string[SIZE];
int catch = mycheck(3,4);
printf("return value:: %d\n",catch);
array[0]=string[0]='A';
printf("%c %c\n", array[0], string[0]);
myRec(7);
printf("\n");
}
Enjoy.............

Calling C function from Fortran with pointers

I am attempting to create a function in C that is called from Fortran. First things first. The fortran code is being compiled with f77, and the c is compiled with gcc. Both are compiled into .so libraries. The c function is going to read the memory on a device at an address, and size, specified by the fortran. I am able to see the address, and size being passed to the c, but I am having trouble filling the data in the c function and returning it to the fortran. See the relevant code below. My assumption is that there is something wrong in the memory allocation or pointer syntax for the data variable.
C
void copymemory_( uint32_t *addr, int *size, uint8_t *data )
{
int i;
printf("addr %x \n", *addr);
printf("size %i \n", *size);
for ( i = 0; i<*size; i++)
{
*data[i] = i;
printf("memory %i \n",*data[i]);
}
}
Fortran
integer memory(4)
call copymemory(z'dead', 4, memory)
DO i = 1,memsize
call printf(memory(i))
END DO
I have several points to your code.
Please, supply compilable code and describe the output of that exact code!
That includes the #include for standard headers, if you want people to debug your code, make it easy for them so that they don't have to search which lines did you omit because they seemed "obvious" to you. Just paste everything. The code should compile when copied from here!
Even the executable part of your code does not compile in my compiler. I had to change the *data to data. Are you sure you copied your actual code?
cfun.c: In function ‘copymemory_’:
cfun.c:13:9: error: invalid type argument of unary ‘*’ (have ‘int’)
*data[i] = i;
^
cfun.c:14:31: error: invalid type argument of unary ‘*’ (have ‘int’)
printf("memory %i \n",*data[i]);
^
Your Fortran code contains a call to some printf subroutine. Where is this defined? Is it present in your actual code? I doubt so. Please copy to StackOverflow always a complete and compilable code.
So after fixing the obvious your present code is:
#include <stdio.h>
#include <stdint.h>
void copymemory_( uint32_t *addr, int *size, uint8_t *data )
{
int i;
printf("addr %x \n", *addr);
printf("size %i \n", *size);
for ( i = 0; i<*size; i++)
{
data[i] = i;
printf("memory %i \n",data[i]);
}
}
implicit none
integer :: memsize = 4
integer memory(4)
integer i
call copymemory(z'dead', 4, memory)
DO i = 1,memsize
print *, (memory(i))
END DO
END
It does not crash but memory in Fortran contains garbage. It has to, because it is integer and you are treating it as int8_t in C.
So either treat it as an array of four integers in C or copy it byte by byte, but then you must pass the correct number of bytes to copy. From your description it is not clear which one is your intention so I will show just one possibility:
void copymemory_( uint32_t *addr, int *size, uint32_t *data )
The output is correct then:
> gfortran-4.10 -fsanitize=address cfun.c ffun.f90
> ./a.out
addr dead
size 4
memory 0
memory 1
memory 2
memory 3
0
1
2
3

Passing pointers of arrays in C

So I have some code that looks like this:
int a[10];
a = arrayGen(a,9);
and the arrayGen function looks like this:
int* arrayGen(int arrAddr[], int maxNum)
{
int counter=0;
while(arrAddr[counter] != '\0') {
arrAddr[counter] = gen(maxNum);
counter++;
}
return arrAddr;
}
Right now the compilier tells me "warning: passing argument 1 of ‘arrayGen’ makes integer from pointer without a cast"
My thinking is that I pass 'a', a pointer to a[0], then since the array is already created I can just fill in values for a[n] until I a[n] == '\0'. I think my error is that arrayGen is written to take in an array, not a pointer to one. If that's true I'm not sure how to proceed, do I write values to addresses until the contents of one address is '\0'?
The basic magic here is this identity in C:
*(a+i) == a[i]
Okay, now I'll make this be readable English.
Here's the issue: An array name isn't an lvalue; it can't be assigned to. So the line you have with
a = arrayGen(...)
is the problem. See this example:
int main() {
int a[10];
a = arrayGen(a,9);
return 0;
}
which gives the compilation error:
gcc -o foo foo.c
foo.c: In function 'main':
foo.c:21: error: incompatible types in assignment
Compilation exited abnormally with code 1 at Sun Feb 1 20:05:37
You need to have a pointer, which is an lvalue, to which to assign the results.
This code, for example:
int main() {
int a[10];
int * ip;
/* a = arrayGen(a,9); */
ip = a ; /* or &a[0] */
ip = arrayGen(ip,9);
return 0;
}
compiles fine:
gcc -o foo foo.c
Compilation finished at Sun Feb 1 20:09:28
Note that because of the identity at top, you can treat ip as an array if you like, as in this code:
int main() {
int a[10];
int * ip;
int ix ;
/* a = arrayGen(a,9); */
ip = a ; /* or &a[0] */
ip = arrayGen(ip,9);
for(ix=0; ix < 9; ix++)
ip[ix] = 42 ;
return 0;
}
Full example code
Just for completeness here's my full example:
int gen(int max){
return 42;
}
int* arrayGen(int arrAddr[], int maxNum)
{
int counter=0;
while(arrAddr[counter] != '\0') {
arrAddr[counter] = gen(maxNum);
counter++;
}
return arrAddr;
}
int main() {
int a[10];
int * ip;
int ix ;
/* a = arrayGen(a,9); */
ip = a ; /* or &a[0] */
ip = arrayGen(ip,9);
for(ix=0; ix < 9; ix++)
ip[ix] = 42 ;
return 0;
}
Why even return arrAddr? Your passing a[10] by reference so the contents of the array will be modified. Unless you need another reference to the array then charlies suggestion is correct.
Hmm, I know your question's been answered, but something else about the code is bugging me. Why are you using the test against '\0' to determine the end of the array? I'm pretty sure that only works with C strings. The code does indeed compile after the fix suggested, but if you loop through your array, I'm curious to see if you're getting the correct values.
I'm not sure what you are trying to do but the assignment of a pointer value to an array is what's bothering the compiler as mentioned by Charlie. I'm curious about checking against the NUL character constant '\0'. Your sample array is uninitialized memory so the comparison in arrayGen isn't going to do what you want it to do.
The parameter list that you are using ends up being identical to:
int* arrayGen(int *arrAddr, int maxNum)
for most purposes. The actual statement in the standard is:
A declaration of a parameter as "array of type" shall be adjusted to "qualified pointer to type", where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.
If you really want to force the caller to use an array, then use the following syntax:
void accepts_pointer_to_array (int (*ary)[10]) {
int i;
for (i=0; i<10; ++i) {
(*ary)[i] = 0; /* note the funky syntax is necessary */
}
}
void some_caller (void) {
int ary1[10];
int ary2[20];
int *ptr = &ary1[0];
accepts_pointer_to_array(&ary1); /* passing address is necessary */
accepts_pointer_to_array(&ary2); /* fails */
accepts_pointer_to_array(ptr); /* also fails */
}
Your compiler should complain if you call it with anything that isn't a pointer to an array of 10 integers. I can honestly say though that I have never seen this one anywhere outside of various books (The C Book, Expert C Programming)... at least not in C programming. In C++, however, I have had reason to use this syntax in exactly one case:
template <typename T, std::size_t N>
std::size_t array_size (T (&ary)[N]) {
return N;
}
Your mileage may vary though. If you really want to dig into stuff like this, I can't recommend Expert C Programming highly enough. You can also find The C Book online at gbdirect.
Try calling your parameter int* arrAddr, not int arrAddr[]. Although when I think about it, the parameters for the main method are similar yet that works. So not sure about the explanation part.
Edit: Hm all the resources I can find on the internet say it should work. I'm not sure, I've always passed arrays as pointers myself so never had this snag before, so I'm very interested in the solution.
The way your using it arrayGen() doesn't need to return a value. You also need to place '\0' in the last element, it isn't done automatically, or pass the index of the last element to fill.
#jeffD
Passing the index would be the preferred way, as there's no guarantee you won't hit other '\0's before your final one (I certainly was when I tested it).

Resources