call a vararg function with an array? - c

In this example below, I would like to pass to a function that receive variable number of arguments the content of an array.
In other terms, I would like to pass to printf the content of foo by value and thus, pass these arguments on the stack.
#include <stdarg.h>
#include <stdio.h>
void main()
{
int foo[] = {1,2,3,4};
printf("%d, %d, %d, %d\n", foo);
}
I know this example looks stupid because I can use printf("%d, %d, %d, %d\n", 1,2,3,4);. Just imagine I'm calling void bar(char** a, ...) instead and the array is something I receive from RS232...
EDIT
In other words, I would like to avoid this:
#include <stdarg.h>
#include <stdio.h>
void main()
{
int foo[] = {1,2,3,4};
switch(sizeof(foo))
{
case 1: printf("%d, %d, %d, %d\n", foo[0]); break;
case 2: printf("%d, %d, %d, %d\n", foo[0], foo[1]); break;
case 3: printf("%d, %d, %d, %d\n", foo[0], foo[1], foo[2]); break;
case 4: printf("%d, %d, %d, %d\n", foo[0], foo[1], foo[2], foo[3]); break;
...
}
}

I would like to pass to printf the content of foo by value and thus, pass these arguments on the stack.
You cannot pass an array by value. Not by "normal" function call, and not by varargs either (which is, basically, just a different way of reading the stack).
Whenever you use an array as argument to a function, what the called function receives is a pointer.
The easiest example for this is the char array, a.k.a. "string".
int main()
{
char buffer1[100];
char buffer2[] = "Hello";
strcpy( buffer2, buffer1 );
}
What strcpy() "sees" is not two arrays, but two pointers:
char * strcpy( char * restrict s1, const char * restrict s2 )
{
// Yes I know this is a naive implementation in more than one way.
char * rc = s1;
while ( ( *s1++ = *s2++ ) );
return rc;
}
(This is why the size of the array is only known in the scope the array was declared in. Once you pass it around, it's just a pointer, with no place to put the size information.)
The same holds true for passing an array to a varargs function: What ends up on the stack is a pointer to the (first element of) the array, not the whole array.
You can pass an array by reference and do useful things with it in the called function if:
you pass the (pointer to the) array and a count of elements (think argc / argv), or
caller and callee agree on a fixed size, or
caller and callee agree on the array being "terminated" in some way.
Standard printf() does the last one for "%s" and strings (which are terminated by '\0'), but is not equipped to do so with, as in your example, an int[] array. So you would have to write your own custom printme().
In no case are you passing the array "by value". If you think about it, it wouldn't make much sense to copy all elements to the stack for larger arrays anyway.

As already said, you cannot pass an array by value in a va_arg directly. It is possible though if it is packed inside a struct. It is not portable but one can do some things when the implementation is known.
Here an example, that might help.
void call(size_t siz, ...);
struct xx1 { int arr[1]; };
struct xx10 { int arr[10]; };
struct xx20 { int arr[20]; };
void call(size_t siz, ...)
{
va_list va;
va_start(va, siz);
struct xx20 x = va_arg(va, struct xx20);
printf("HEXDUMP:%s\n", HEXDUMP(&x, siz));
va_end(va);
}
int main(void)
{
struct xx10 aa = { {1,2,3,4,5,[9]=-1}};
struct xx20 bb = { {[10]=1,2,3,4,5,[19]=-1}};
struct xx1 cc = { {-1}};
call(sizeof aa, aa);
call(sizeof bb, bb);
call(sizeof cc, cc);
}
Will print following (HEXDUMP() is one of my debug functions, it's obvious what it does).
HEXDUMP:
0x7fff1f154160:01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ................
0x7fff1f154170:05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x7fff1f154180:00 00 00 00 ff ff ff ff ........
HEXDUMP:
0x7fff1f154160:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x7fff1f154170:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x7fff1f154180:00 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 ................
0x7fff1f154190:03 00 00 00 04 00 00 00 05 00 00 00 00 00 00 00 ................
0x7fff1f1541a0:00 00 00 00 00 00 00 00 00 00 00 00 ff ff ff ff ................
Tested on Linux x86_64 compiled with gcc 5.1 and Solaris SPARC9 compiled with gcc 3.4
I don't know if it is helpful, but it's maybe a start. As can be seen, using the biggest struct array in the functions va_arg allows to handle smaller arrays if the size is known.
But be careful, it probably is full of undefined behaviours (example, if you call the function with a struct array size smaller than 4 int, it doesn't work on Linux x86_64 because the struct is passed by registers, not as an array on stack, but on your embedded processor it might work).

Short answer: No, you can't do it, it's impossible.
Slightly longer answer: Well, maybe you can do it, but it's super tricky. You are basically trying to call a function with an argument list that is not known until run time. There are libraries that can help you dynamically construct argument lists and call functions with them; one library is libffi: https://sourceware.org/libffi/.
See also question 15.13 in the C FAQ list: How can I call a function with an argument list built up at run time?
See also these previous Stackoverflow questions:
C late binding with unknown arguments
How to call functions by their pointers passing multiple arguments in C?
Calling a variadic function with an unknown number of parameters

Ok look at this example, from my code. This is simple one way.
void my_printf(char const * frmt, ...)
{
va_list argl;
unsigned char const * tmp;
unsigned char chr;
va_start(argl,frmt);
while ((chr = (unsigned char)*frmt) != (char)0x0) {
frmt += 1;
if (chr != '%') {
dbg_chr(chr);
continue;
}
chr = (unsigned char)*frmt;
frmt += 1;
switch (chr) {
...
case 'S':
tmp = va_arg(argl,unsigned char const *);
dbg_buf_str(tmp,(uint16_t)va_arg(argl,int));
break;
case 'H':
tmp = va_arg(argl,unsigned char const *);
dbg_buf_hex(tmp,(uint16_t)va_arg(argl,int));
break;
case '%': dbg_chr('%'); break;
}
}
va_end(argl);
}
There dbg_chr(uint8_t byte) drop byte to USART and enable transmitter.
Use example:
#define TEST_LEN 0x4
uint8_t test_buf[TEST_LEN] = {'A','B','C','D'};
my_printf("This is hex buf: \"%H\"",test_buf,TEST_LEN);

As mentioned above, variadic argument might be passed as a struct-packed array:
void logger(char * bufr, uint32_t * args, uint32_t argNum) {
memset(buf, 0, sizeof buf);
struct {
uint32_t ar[16];
} argStr;
for(uint8_t a = 0; a < argNum; a += 1)
argStr.ar[a] = args[a];
snprintf(buf, sizeof buf, bufr, argStr);
strcat(buf, '\0');
pushStr(buf, strlen(buf));
}
tested and works with gnu C compiler

Related

insufficient space for an object of type 'char' C and heap buffer overflow error

i encounter this error when trying to run the following function in leetcode. but when ran on VSC with my own main function it works perfectly fine.
Line 15: Char 28: runtime error: store to address 0x602000000070 with insufficient space for an object of type 'char' [solution.c]
0x602000000070: note: pointer points here
01 00 80 67 be 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
^
char * longestCommonPrefix(char ** strs, int strsSize)
{
int z = 0;
int i = 0;
char* outputa = malloc(sizeof *outputa * z);
for (i = 0; i < *strs[i]; i++)
{
for (z = 0; z < *strs[i*z]; z++)
{
if (strs[i+1][z] == strs[i][z] && strs[i+2][z] == strs[i][z])
{
outputa[z] = strs[i][z];
continue;
}
break;
}
break;
}
return outputa;
}
ive tried to implement a while loop to check if strs[i][z] != '0', but that just gets me a heap buffer overflow error. ive also tried rearranging how i wrote the malloc variable, with many people on stackoverflow giving different recommendations, all of which just land me on a heap buffer overflow error, or an insufficient space for type 'char' error.

Compare 2 GDB-Core Dumps

i'm in serious trouble with a heap/stack corruption. To be able to set a data breakpoint and find the root of the problem, i want to take two core dumps using gdb and then compare them.
First one when i think the heap and stack are still ok, and a second one shortly before my program crashes.
How can i compare those dumps?
Information about my project:
using gcc 5.x
Plugin for a legacy, 3rd-party-program with RT-support. No sources available for the project (for me).
Legacy Project is C, My Plugin is C++.
Other things i tried:
Using address sanitizers -> won't work because the legacy program wont start with them.
Using undefined behavior sanitizers -> same
Figuring out what memory gets corrupted for data breakpoint -> no success, because the corrupted memory does not belong to my code.
Ran Valgrind -> no errors around my code.
Thank you for your help
Independent from your underlying motivation, I'd like to get into your question. You ask how the difference between two core dumps can be identified. This is going to be lengthy, but will hopefully give you your answer.
A core dump is represented by an ELF file that contains metadata and a specific set of memory regions (on Linux, this can be controlled via /proc/[pid]/coredump_filter) that were mapped into the given process at the time of dump creation.
The obvious way to compare the dumps would be to compare a hex-representation:
$ diff -u <(hexdump -C dump1) <(hexdump -C dump2)
--- /dev/fd/63 2020-05-17 10:01:40.370524170 +0000
+++ /dev/fd/62 2020-05-17 10:01:40.370524170 +0000
## -90,8 +90,9 ##
000005e0 00 00 00 00 00 00 00 00 00 00 00 00 80 1f 00 00 |................|
000005f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
The result is rarely useful because you're missing the context. More specifically, there's no straightforward way to get from the offset of a value change in the file to the offset corresponding to the process virtual memory address space.
So, more context if needed. The optimal output would be a list of VM addresses including before and after values.
Before we can get on that, we need a test scenario that loosely resembles yours. The following application includes a use-after-free memory issue that does not lead to a segmentation fault at first (a new allocation with the same size hides the issue). The idea here is to create a core dump using gdb (generate) during each phase based on break points triggered by the code:
dump1: Correct state
dump2: Incorrect state, no segmentation fault
dump3: Segmentation fault
The code:
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
int **g_state;
int main()
{
int value = 1;
g_state = malloc(sizeof(int*));
*g_state = &value;
if (g_state && *g_state) {
printf("state: %d\n", **g_state);
}
printf("no corruption\n");
raise(SIGTRAP);
free(g_state);
char **unrelated = malloc(sizeof(int*));
*unrelated = "val";
if (g_state && *g_state) {
printf("state: %d\n", **g_state);
}
printf("use-after-free hidden by new allocation (invalid value)\n");
raise(SIGTRAP);
printf("use-after-free (segfault)\n");
free(unrelated);
int *unrelated2 = malloc(sizeof(intptr_t));
*unrelated2 = 1;
if (g_state && *g_state) {
printf("state: %d\n", **g_state);
}
return 0;
}
Now, the dumps can be generated:
Starting program: test
state: 1
no corruption
Program received signal SIGTRAP, Trace/breakpoint trap.
0x00007ffff7a488df in raise () from /lib64/libc.so.6
(gdb) generate dump1
Saved corefile dump1
(gdb) cont
Continuing.
state: 7102838
use-after-free hidden by new allocation (invalid value)
Program received signal SIGTRAP, Trace/breakpoint trap.
0x00007ffff7a488df in raise () from /lib64/libc.so.6
(gdb) generate dump2
Saved corefile dump2
(gdb) cont
Continuing.
use-after-free (segfault)
Program received signal SIGSEGV, Segmentation fault.
main () at test.c:31
31 printf("state: %d\n", **g_state);
(gdb) generate dump3
Saved corefile dump3
A quick manual inspection shows the relevant differences:
# dump1
(gdb) print g_state
$1 = (int **) 0x602260
(gdb) print *g_state
$2 = (int *) 0x7fffffffe2bc
# dump2
(gdb) print g_state
$1 = (int **) 0x602260
(gdb) print *g_state
$2 = (int *) 0x4008c1
# dump3
$2 = (int **) 0x602260
(gdb) print *g_state
$3 = (int *) 0x1
Based on that output, we can clearly see that *g_state changed but is still a valid pointer in dump2. In dump3, the pointer becomes invalid. Of course, we'd like to automate this comparison.
Knowing that a core dump is an ELF file, we can simply parse it and generate a diff ourselves. What we'll do:
Open a dump
Identify PROGBITS sections of the dump
Remember the data and address information
Repeat the process with the second dump
Compare the two data sets and print the diff
Based on elf.h, it's relatively easy to parse ELF files. I created a sample implementation that compares two dumps and prints a diff that is similar to comparing two hexdump outputs using diff. The sample makes some assumptions (x86_64, mappings either match in terms of address and size or they only exist in dump1 or dump2), omits most error handling and always chooses a simple implementation approach for the sake of brevity.
#include <elf.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#define MAX_MAPPINGS 1024
struct dump
{
char *base;
Elf64_Shdr *mappings[MAX_MAPPINGS];
};
unsigned readdump(const char *path, struct dump *dump)
{
unsigned count = 0;
int fd = open(path, O_RDONLY);
if (fd != -1) {
struct stat stat;
fstat(fd, &stat);
dump->base = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
Elf64_Ehdr *header = (Elf64_Ehdr *)dump->base;
Elf64_Shdr *secs = (Elf64_Shdr*)(dump->base+header->e_shoff);
for (unsigned secinx = 0; secinx < header->e_shnum; secinx++) {
if (secs[secinx].sh_type == SHT_PROGBITS) {
if (count == MAX_MAPPINGS) {
count = 0;
break;
}
dump->mappings[count] = &secs[secinx];
count++;
}
}
dump->mappings[count] = NULL;
}
return count;
}
#define DIFFWINDOW 16
void printsection(struct dump *dump, Elf64_Shdr *sec, const char mode,
unsigned offset, unsigned sizelimit)
{
unsigned char *data = (unsigned char *)(dump->base+sec->sh_offset);
uintptr_t addr = sec->sh_addr+offset;
unsigned size = sec->sh_size;
data += offset;
if (sizelimit) {
size = sizelimit;
}
unsigned start = 0;
for (unsigned i = 0; i < size; i++) {
if (i%DIFFWINDOW == 0) {
printf("%c%016x ", mode, addr+i);
start = i;
}
printf(" %02x", data[i]);
if ((i+1)%DIFFWINDOW == 0 || i + 1 == size) {
printf(" [");
for (unsigned j = start; j <= i; j++) {
putchar((data[j] >= 32 && data[j] < 127)?data[j]:'.');
}
printf("]\n");
}
addr++;
}
}
void printdiff(struct dump *dump1, Elf64_Shdr *sec1,
struct dump *dump2, Elf64_Shdr *sec2)
{
unsigned char *data1 = (unsigned char *)(dump1->base+sec1->sh_offset);
unsigned char *data2 = (unsigned char *)(dump2->base+sec2->sh_offset);
unsigned difffound = 0;
unsigned start = 0;
for (unsigned i = 0; i < sec1->sh_size; i++) {
if (i%DIFFWINDOW == 0) {
start = i;
difffound = 0;
}
if (!difffound && data1[i] != data2[i]) {
difffound = 1;
}
if ((i+1)%DIFFWINDOW == 0 || i + 1 == sec1->sh_size) {
if (difffound) {
printsection(dump1, sec1, '-', start, DIFFWINDOW);
printsection(dump2, sec2, '+', start, DIFFWINDOW);
}
}
}
}
int main(int argc, char **argv)
{
if (argc != 3) {
fprintf(stderr, "Usage: compare DUMP1 DUMP2\n");
return 1;
}
struct dump dump1;
struct dump dump2;
if (readdump(argv[1], &dump1) == 0 ||
readdump(argv[2], &dump2) == 0) {
fprintf(stderr, "Failed to read dumps\n");
return 1;
}
unsigned sinx1 = 0;
unsigned sinx2 = 0;
while (dump1.mappings[sinx1] || dump2.mappings[sinx2]) {
Elf64_Shdr *sec1 = dump1.mappings[sinx1];
Elf64_Shdr *sec2 = dump2.mappings[sinx2];
if (sec1 && sec2) {
if (sec1->sh_addr == sec2->sh_addr) {
// in both
printdiff(&dump1, sec1, &dump2, sec2);
sinx1++;
sinx2++;
}
else if (sec1->sh_addr < sec2->sh_addr) {
// in 1, not 2
printsection(&dump1, sec1, '-', 0, 0);
sinx1++;
}
else {
// in 2, not 1
printsection(&dump2, sec2, '+', 0, 0);
sinx2++;
}
}
else if (sec1) {
// in 1, not 2
printsection(&dump1, sec1, '-', 0, 0);
sinx1++;
}
else {
// in 2, not 1
printsection(&dump2, sec2, '+', 0, 0);
sinx2++;
}
}
return 0;
}
With the sample implementation, we can re-evaluate our scenario above. A except from the first diff:
$ ./compare dump1 dump2
-0000000000601020 86 05 40 00 00 00 00 00 50 3e a8 f7 ff 7f 00 00 [..#.....P>......]
+0000000000601020 00 6f a9 f7 ff 7f 00 00 50 3e a8 f7 ff 7f 00 00 [.o......P>......]
-0000000000602260 bc e2 ff ff ff 7f 00 00 00 00 00 00 00 00 00 00 [................]
+0000000000602260 c1 08 40 00 00 00 00 00 00 00 00 00 00 00 00 00 [..#.............]
-0000000000602280 6e 6f 20 63 6f 72 72 75 70 74 69 6f 6e 0a 00 00 [no corruption...]
+0000000000602280 75 73 65 2d 61 66 74 65 72 2d 66 72 65 65 20 68 [use-after-free h]
-0000000000602290 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
+0000000000602290 69 64 64 65 6e 20 62 79 20 6e 65 77 20 61 6c 6c [idden by new all]
The diff shows that *gstate (address 0x602260) was changed from 0x7fffffffe2bc to 0x4008c1:
-0000000000602260 bc e2 ff ff ff 7f 00 00 00 00 00 00 00 00 00 00 [................]
+0000000000602260 c1 08 40 00 00 00 00 00 00 00 00 00 00 00 00 00 [..#.............]
The second diff with only the relevant offset:
$ ./compare dump1 dump2
-0000000000602260 c1 08 40 00 00 00 00 00 00 00 00 00 00 00 00 00 [..#.............]
+0000000000602260 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
The diff shows that *gstate (address 0x602260) was changed from 0x4008c1 to 0x1.
There you have it, a core dump diff. Now, whether or not that can prove to be useful in your scenario depends on various factors, one being the timeframe between the two dumps and the activity that takes place within that window. A large diff will possibly be difficult to analyze, so the aim must be to minimize its size by choosing the diff window carefully.
The more context you have, the easier the analysis will turn out to be. For example, the relevant scope of the diff could be reduced by limiting the diff to addresses of the .data and .bss sections of the library in question if changes in there are relevant to your situation.
Another approach to reduce the scope: excluding changes to memory that is not referenced by the library. The relationship between arbitrary heap allocations and specific libraries is not immediately apparent. Based on the the addresses of changes in your initial diff, you could search for pointers in the .data and .bss sections of the library right in the diff implementation. This does not take every possible reference into account (most notably indirect references from other allocations, register and stack references of library-owned threads), but it's a start.

Segmentation fault(core dumped ) error while reading a python generated binary array in C [duplicate]

This question already has answers here:
Crash or "segmentation fault" when data is copied/scanned/read to an uninitialized pointer
(5 answers)
Closed 3 years ago.
I am trying to load a 2D array created by numpy and read the elements in C, but I get Segmentation fault(core dumped ) error while running it. The code goes by the lines of
#include <stdio.h>
#include <string.h>
int main(){
char *file;
FILE *input;
int N1, N2, ii, jj;
float element;
strcpy(file, "/home/caesar/Desktop/test.bin");
input = fopen(file, "rb");
fread(&N1, sizeof(int), 1, input);
fread(&N2, sizeof(int), 1, input);
float memoryarray[N1][N2];
for(ii= 0; ii<N1; ii++){
for(jj=0; jj<N2; jj++){
fread(&element, sizeof(float), 1, input);
memoryarray[ii][jj]= element;
}
}
printf("%f", memoryarray[2][3]);
fclose(input);
return 0;
}
This is the starting for a task where I will have to read elements from matrices of the form 400*400*400 or so. The idea is to read all elements from the file and store it in memory and then read from memory index wise when necessary, for example, here i am trying to access and print the element in the second row third column.
P.S: I am quite new to pointers.
Dear all, I tried the methods you said., here is the modified version of the code, the segmentation fault error is gone but the output is either all zeros, or is just plain garbage values.
I ran the executable three times and the outputs I got were
Output1: -0.000000
Output 2: 0.000000
Output 3 : -97341413674450944.000000
My array contains integers btw
Here is the modified version of the code
#include <stdio.h>
#include <string.h>
void main(){
const char file[] ="/home/caesar/Desktop/test.bin";
FILE *input;
int N1, N2, ii, jj;
float element;
//strcpy(file, "/home/caesar/Desktop/test.bin");
input = fopen(file, "r");
fread(&N1, sizeof(int), 1, input);
fread(&N2, sizeof(int), 1, input);
float memoryarray[N1][N2];
for(ii= 0; ii<N1; ii++){
for(jj=0; jj<N2; jj++){
fread(&element, sizeof(float), 1, input);
memoryarray[ii][jj]= element;
}
}
printf("%f", memoryarray[1][2]);
fclose(input);
Also here is the hex dump of the file that i am trying to open. Some of you asked me to verify whether fopen() is working or not, i checked, it is working.
00000000 00 00 40 40 00 00 40 40 01 00 00 00 00 00 00 00 |..##..##........|
00000010 02 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 |................|
*
00000030 04 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 |................|
00000040 05 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 |................|
00000050
So here is my problem in brief. I have multidimensional arrays of double precision floats written to a file using python. I want to take those files and access the elements whenever necessary by using the index of the elements to get the values. Any C code to do so would solve my problem.
Here is the python code i am using to write the file
with open('/home/caesar/Desktop/test.bin', 'wb') as myfile:
N= np.zeros(2, dtype= np.float32, order= "C")
N[0]= 3
N[1]= 3
a= [[1,2,3],[2,3,4], [4,5,6]]
N.astype(np.float32).tofile(myfile)
b= np.asarray(a)
b.tofile(myfile)
strcpy(file, "/home/caesar/Desktop/test.bin");
This writes to a garbage memory address.
You should either declare file as an array of suitable size, like this:
char file[100];
or
initialize the char pointer directly with the path like this (and get rid of the strcpy):
const char *file = "/home/caesar/Desktop/test.bin";
or the best, as per common consensus (refer comments):
fopen("/home/caesar/Desktop/test.bin", "rb");

Why the value of this matrix element is unknown?

This is a question on my exercise book:
If we write int a[][3]={{0},{1},{2}};, the value of the element a[1][2] will be ____.
The key says its value cannot be known.
Since the statement are not granted to be written outside a function, the matrix should not be simply seen as a global variable, which initializes all elements to 0. However, I suppose the initializer {{0},{1},{2}} is equivalent to {{0,0,0},{1,0,0},{2,0,0}}, so a[1][2] should be 0. Who is wrong, the key or me?
PS: I wrote this code:
#include <stdio.h>
int main()
{
int a[][3]={{0},{1},{2}};
printf("%d",a[1][2]);
return 0;
}
And its output is exactly 0.
You are correct, the rest of the values are initialized to default values, 0 in this case.
The relevant quote from the standard:
6.7.9 Initialization
If there are fewer initializers in a brace-enclosed list than there are elements or members
of an aggregate, or fewer characters in a string literal used to initialize an array of known
size than there are elements in the array, the remainder of the aggregate shall be
initialized implicitly the same as objects that have static storage duration.
Your answer is right and the key is wrong. Rest of the array members that you didn't initialize will be implicitly initialized to 0 and this is guaranteed by the C standard irrespective of whether the array is global or inside a function.
C11, 6.7.9
If there are fewer initializers in a brace-enclosed list than there
are elements or members of an aggregate, or fewer characters in a
string literal used to initialize an array of known size than there
are elements in the array, the remainder of the aggregate shall be
initialized implicitly the same as objects that have static storage
duration.
The problem is that C has lax rules for how the braces should be interpreted, the braces do not specify how many items there are in each array. So you will end up with an array of int [3][3]; which may or may not be what you expected.
According to the rules of array initialization, the items in each array that are not initialized explicitly, will get initialized as if they had static storage duration. That is, to zero.
So you are correct and you can easily prove it by printing the raw contents of the memory, like this:
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
void mem_dump (int a[3][3], size_t size);
int main()
{
int a[][3]={{0},{1},{2}};
printf("a is initialized like this:\n");
mem_dump(a, sizeof(a));
printf("\n");
int rubbish[3][3];
memset(rubbish, 0xAA, sizeof(rubbish)); // fill up with some nonsense character
memcpy(a, rubbish, sizeof(a)); // a guaranteed to contain junk.
printf("a is now filled with junk:\n");
mem_dump(a, sizeof(a));
printf("\n");
memcpy(a, (int[][3]){{0},{1},{2}}, sizeof(a)); // copy back the initialized values
printf("a now contains the initialized values once more:\n");
mem_dump(a, sizeof(a));
return 0;
}
void mem_dump (int a[3][3], size_t size)
{
for (size_t i=0; i<size; i++)
{
printf("%.2" PRIx8 " ", ((uint8_t*)a)[i] );
if( (i+1) % sizeof(int[3]) == 0) // if divisible by the size of a sub array
printf("\n");
}
}
Output:
a is initialized like this:
00 00 00 00 00 00 00 00 00 00 00 00
01 00 00 00 00 00 00 00 00 00 00 00
02 00 00 00 00 00 00 00 00 00 00 00
a is now filled with junk:
aa aa aa aa aa aa aa aa aa aa aa aa
aa aa aa aa aa aa aa aa aa aa aa aa
aa aa aa aa aa aa aa aa aa aa aa aa
a now contains the initialized values once more:
00 00 00 00 00 00 00 00 00 00 00 00
01 00 00 00 00 00 00 00 00 00 00 00
02 00 00 00 00 00 00 00 00 00 00 00
Both are right.
If you don't initialize a local non-static variable, it will have an indeterminate value. But you do initialize the a variable, that's what the "assignment" does, it initializes the variable. And if you initialize an array with less values than it's been declared to have, then the rest will be initialized to "zero".

How to ANSI-C cast from unsigned int * to char *?

I want these two print functions to do the same thing:
unsigned int Arraye[] = {0xffff,0xefef,65,66,67,68,69,0};
char Arrage[] = {0xffff,0xefef,65,66,67,68,69,0};
printf("%s", (char*)(2+ Arraye));
printf("%s", (char*)(2+ Arrage));
where Array is an unsigned int. Normally, I would change the type but, the problem is that most of the array is numbers, although the particular section should be printed as ASCII. Currently, the unsigned array prints as "A" and the char array prints as the desired "ABCDE".
This is how the unsigned int version will be arranged in memory, assuming 32-bit big endian integers.
00 00 ff ff 00 00 ef ef 00 00 00 41 00 00 00 42
00 00 00 43 00 00 00 44 00 00 00 45 00 00 00 00
This is how the char version will be arranged in memory, assuming 8-bit characters. Note that 0xffff does not fit in a char.
ff ef 41 42 43 44 45 00
So you can see, casting is not enough. You'll need to actually convert the data.
If you know that your system uses 32-bit wchar_t, you can use the l length modifier for printf.
printf("%ls", 2 + Arraye);
This is NOT portable. The alternative is to copy the unsigned int array into a char array by hand, something like this:
void print_istr(unsigned int const *s)
{
unsigned int const *p;
char *s2, *p2;
for (p = s; *p; p++);
s2 = xmalloc(p - s + 1);
for (p = s, p2 = s2; *p2 = *p; p2++, p++);
fputs(s2, stdout);
free(s2);
}
As Dietrich said, a simple cast will not do, but you don't need a complicated conversion either. Simply loop over your array.
uint_t Arraye[] = {0xffff,0xefef,65,66,67,68,69,0};
char Arrage[] = {0xffff,0xefef,65,66,67,68,69,0};
uint_t *p;
for(p = Arraye+2; p; p++)
printf("%c", p);
printf("%s", (char*)(2+ Arrage));

Resources