Local initialized array in riscv - c

I have a c program that comes below:
int A[size][size] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
int B[size][size] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
int C[size][size];
int multiplication()
{
//using array A, B and C here
return 0;
}
I also wrote a startup file by myself and compile them using risc-v compiler by the following command:
riscv64-unknown-elf-gcc -ffreestanding -march=rv32im -mabi=ilp32 -nostartfiles -nodefaultlibs -mno-relax crt0.s Myprogram.c -o Myprogram
Then using riscv64-unknown-elf-objdump -t Myprogram I can see that arrays A and B are in .data segment and array C is in .bss segment that makes sense.
But when I change my code such that I declared arrays A and B as local arrays to the function:
int C[size][size];
int multiplication()
{
int A[size][size] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
int B[size][size] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
//using array A, B and C here
return 0;
}
I got the compilation error: undefined reference to `memcpy'.
It seems that it uses memcpy for allocating memory to the arrays while it's not the case when I declared the arrays as global.
Does anybody know why and what's the difference?

Large constants needed by a program are stored in the program's text segment, and copied where needed. When the compiler chooses to initialize a large variable by copying the initializer from the text segment, it generates a call to memcpy. There are other ways to compile this code, sure, but calling memcpy is perfectly sensible and efficient.
This doesn't happen for a global variable because they are initialized from the program's data segment. Since they only need to be initialized when the program starts, the same memory that contains the initializer when the program starts is then used for the variable.
You might think of main as a special function and of its local variables as essentially global variables, but they aren't. main is a function, and it can be called recursively. The compiler could in theory detect that some programs doesn't call main recursively and give it special treatment, but in practice, it doesn't, and it sure can't when it's linking some assembly code which could be finding its way to main.
Normally you don't see this because the standard library provides memcpy. But since you're using -nodefaultlibs, you need to supply an alternative implementation of memcmp, memset, memcpy and memmove, per the GCC documentation:
Most of the compiler support routines used by GCC are present in libgcc, but there are a few exceptions. GCC requires the freestanding environment provide memcpy, memmove, memset and memcmp.

GCC & clang require freestanding environments to provide
memcpy, memmove, memset and memcmp. The GCC documentation says:
Most of the compiler support routines used by GCC are present in libgcc, but there are a few exceptions. GCC requires the freestanding environment provide memcpy, memmove, memset and memcmp.
You need to implement them yourself it you do not want use the standard ones
You should also remember that there are more functions which can be required - for example, integer divisions and similar.
You can also see if your compiler provides libgcc or you can take the function you require from here (or or your port libgcc repo): https://github.com/gcc-mirror/gcc/tree/master/libgcc

Related

C execute function with list of arguments

I need to evaluate function with list of arguments from array of arguments as in this example:
int compute(...) {
int n;
va_list params;
va_start(params, n);
// some custom computation with no "va" output
va_end(params);
}
And some array of int (which is dynamic array, don't rely on fixed size):
int arr[10] = {0, 1, 3, 7, 8, 1, 3, 5, 7, 9};
And I need to call compute like JS function compute.apply(this, arr)
I'm implementing some library with C that's why I need it.
In C++ this is std::apply but I want the same in C.
Thanks
In C++ this is std::apply but I want the same in C.
Since you want the same in C, you'll surely accept that you have to fulfill the same requirements, in particular, as a tuple supports std::get and std::tuple_size, their C equivalents. Now, as long as the arguments from the array are accessed in order from first to last, std::get can be implemented with va_…(), but just as the stdarg variable argument lists need some means of determining the amount of arguments (like a format string or an argument count), std::tuple_size cannot be implemented without such a means. You won't do without passing this information.

Is providing more initializers in an array initializer list than there are elements in the array a syntax error?

I was told this is true (in the pearson book How to Program), however when compiling inside the ide of NetBeans,
the following function code
int n[5] = {6, 5, 4, 3, 2, 1, 2, 3, 2};
does not result in a compile time error however does cause unexpected behavior further in the program. Accessing locations in the array that shouldn't exist also does not cause a syntax error.
printf("%d", n[7]);
Am I missing something?
Yes this code is wrong:
int n[5] = {6, 5, 4, 3, 2, 1, 2, 3, 2};
Per 6.7.9 Initialization, paragraph 2 of the C standard:
Constraints
No initializer shall attempt to provide a value for an object not
contained within the entity being initialized.
As noted in the comments, this is a constraint violation and a semantic error that must be reported by the compiler. I'd say any compiler that fails to report this error is deficient.
Yes, it is a constraint violation (aka "error") in C. Calling it a "syntax error" would be incorrect though. There's nothing wrong with the syntax in your declaration.
The only exception from this rule is permission to let the trailing zero-terminator to "fall off" the end of the character array when the initializer is expressed by a string literal
char str[5] = "Hello";
The above is valid in C even though the terminating \0 character does fit into the array. (The \0 character is discarded when it does not fit.)
If your compiler issued a warning for your declaration, then by issuing a warning it formally complied with standard requirements. Compilers like GCC will issue an error in -pedantic-errors mode.

Writing to a 2D Array via Pointer Notation

I'm having trouble understanding why incrementing the pointers in pnArryCpy below is incorrect. I figured out how to copy the array using pointer notation a different way, but I need to understand what's wrong with this (e.g., (* tgt_s)++; where int (*tgt_s)[cs]), and why tgt_s is an lvalue (e.g., tgt_s++ is valid) but *tgt_s is not (really) an lvalue.
int main(void)
{
int arr1[2][4] = { {1, 2, 3, 4}, {6, 7, 8, 9} };
int arr2[2][4];
pnArrCpy(4, arr1, arr2, arr2+2); // copies 2d array using pointer notation
// - this is where the problem is.
printarr(2, 4, arr2); // this just prints the array and works fine - not at issue
putchar('\n');
return 0;
}
void pnArrCpy(int cs, int (*src)[cs], int (*tgt_s)[cs], int (*tgt_e)[cs])
{
while (tgt_s < tgt_e)
{
**tgt_s=**src;
(* tgt_s)++; // older versions of gcc warn "target of assignment not really
// an lvalue", latest versions throw an error
(* src)++; // but no errors are runtime
}
return;
}
// trucated rest of program since it's not relevant, just the function for printing
// the array
Under the older gcc, the program compiles and displays the correct results, namely:
1 2 3 4
6 7 8 9
Mac OS 10.8.2
gcc 4.7.2 gave me the error
gcc 4.2.1 was only giving me warnings
Thank you!!
EDIT: Reason I'm using variable length arrays: this function is part of another program, and this one is just a driver I was using to troubleshoot pnArrCpy. In the actual program, the array dimensions and contents are user defined, hence use of VLA.
The thing is:
int (*tgt_s)[cs] is a pointer to an array. Take a few seconds to think about that, it's a bit of an exotic pointer
*tgt_s is therefore an array
arrays are not modifiable lvalues
What makes it hardest to understand is the way you're using the C99 feature of passing cs and then using it in the parameter list.
If you want to learn more about VLAs as function arguments, check out this excellent post.

How to correctly write declarations of extern arrays (and double arrays) in C's header files?

Suppose I want to share a global array of data across my program, for example:
int lookup_indexes[] = { -1, 1, 1, -1, 2, 1, 1, -2, 2, 2, -1, 1, 1, 2 };
What is the correct extern declaration for this array in the C header file?
Also what about an array like this:
int double_indexes[][5] = { { -1, 1, 1, -1, 1 }, { 2, -2, 2, 1, -1 } };
In my header file I tried this:
extern int lookup_indexes[];
extern int double_indexes[][5];
But this results in compiler errors:
water.h:5: error: array type has incomplete element type
I can't figure it out.
Thanks, Boda Cydo.
This link discusses the problems with arrays and sizes used as extern and how to manage them.
Declare a companion variable, containing the size of the array, defined and initialized (with sizeof) in the same source file where the array is defined
define a manifest constant for the size so that it can be used consistently in the definition and the extern declaration
Use some sentinel value (typically 0, -1, or NULL) in the array's last element, so that code can determine the end without an explicit size indication
The code you posted looks fine to me and compiles (gcc -std=c99 -pedantic and gcc -std=c90 -pedantic) on my machine. Have you copy-pasted these lines or could you have made a typo in your real header?
Example typos that could cause your error (pure guesswork):
extern int double_indexes[][]; /* forgot the 5 */
extern int double_indexes[5][]; /* [] and [5] swapped */

Predefining C Array

In C, when defining an array I can do the following:
int arr[] = {5, 2, 9, 8};
And thus I defined it and filled it up, but how do I define it in my .h file, and then fill it in my .c?
Like do something like
int arr[];
arr = {5, 2, 9, 8};
I'm pretty new to C, not sure how it would look
any suggestions?
Normally, you'd put:
extern int arr[];
In the .h file, and:
int arr[] = { 5, 2, 9, 8};
In the .c file.
Edit: Dale Hagglund and KevinDTimm raise good points: you only want to put the initialization in one .c file, and you only need to put anything in the .h file if you're going to access arr from code in more than one .c file.
You can use include guards to prevent the assignemnt from happening multiple times, but putting the assignment into headers is a very bad practice in my opinion. Put the intialization in a c file, in an init function instead and extern the array in the h file.

Resources