I have done a code to solve the Sum String as Numbers problem on CodeWars.
On my machine it runs and returns the correct value, it also passes all tests on CodeWars, but for some reasons it's raised the Caught unexpected signal: 6 and free(): invalid pointer.
I have checked other questions related to this and other forums but it seems that i don't anything form there. I have used only size_t my pointer don't increase like a++ or anything in order to free to not be able to dealocate memory.
My code consists on:
a function char *to_num(const char *a,size_t *size) which converts all digits from ASCII to numbers (i.e. '3' => 3) and returns a char* to a dynamic allocated array with all that numbers. Also it removes all prefixed zeros.
void no_zeros(char *a) which simply deletes all the 0 from the start of a string (i.e. "0012" => "12")
a custom len function, because i use a different nul character, as my 0 in array is tehnically 0 digit. On this example i use 10 as terminating character
char *fill(char* a,size_t size) that returns a string prefixed with as many zeros as needed to reach the length size (i.e. for a="12" and size=4 it returns 0012)
and finnaly the main function char *strsum(const char *a, const char *b) which returns the sum written as a string (i.e. "123" + "456" returns "579")
The flow of this program goes like:
convert the strings received as parameter to numbers with to_num
if one number is longer than another the fill function is called to complet the smaller one so that we can perform the addition
than we perform the addition per components and return the number as string
I have a bug that prefixes my result with zero so i call the no_zeros on resut.
My code
#include <stdio.h>
#include <malloc.h>
#include <string.h>
void display(const char *a,size_t size){
for(size_t i=0;a[i]!=0 && i<size;i++){
if(a[i]>'0'){
printf("%c",a[i]);
}
else{
printf("%d",a[i]);
}
}
printf("\n");
}
char *to_num(const char *a,size_t *size){
size_t i=0,j=0;
*size = strlen(a);
char *result = malloc((*size+1)*sizeof(char));
if(result==NULL){return 0;}
while(a[i]=='0'){i++;}
while(a[i]!=0){
result[j] = a[i];
i++;
j++;
}
result[j]=0;
*size = j;
for(size_t i=0;i<*size;i++){
result[i] = result[i]-'0';
}
result[*size] =10;
return result;
}
void no_zeros(char *a){
size_t i=0,j=0;
while(a[i]=='0'){i++;}
while(a[i]!=0){
a[j] = a[i];
i++;
j++;
}
a[j]=0;
}
size_t len(char *a){
size_t s=0;
for(size_t i=0;a[i]!=10;i++){
s++;
}
return s;
}
char *fill(char* a,size_t size){
size_t a_size = len(a);
char *copy = malloc((size+2)*sizeof(char));
if(copy==NULL){return 0;}
for(size_t i=0;i<size-a_size;i++){
copy[i] = 0;
}
for(size_t i=0;i<a_size;i++){
copy[i+size-a_size] = a[i];
}
copy[size] = 0;
for(size_t i=0;i<size;i++){
}
return copy;
}
char *strsum(const char *a, const char *b)
{
size_t size_a,size_b,bigger,smaller;
char *x,*y;
x = to_num(a,&size_a);
y = to_num(b,&size_b);
bigger = size_a>=size_b ? size_a : size_b;
smaller = size_a<=size_b ? size_a : size_b;
if(bigger != smaller){
if(bigger == size_a){
y = fill(y,bigger);
size_b = bigger;
}
else{
x = fill(x,bigger);
size_a = bigger;
}
}
char *result = malloc((bigger+2)*sizeof(char));
if(result==NULL){return 0;}
int carry=0;
size_t i;
for(i=bigger;i>=0;i--){
result[i] = (x[i-1]+y[i-1]+carry)%10+'0';
carry = (x[i-1]+y[i-1]+carry)/10;
if(i==0) break;
}
result[bigger+1]=0;
no_zeros(result);
if(result[0]==0){return "0";}
free(x);
free(y);
return result;
}
int main(){
printf("%s\n",strsum("9567","800"));
}
display(): for(size_t i=0;a[i]!=0 && i<size;i++){: swap the order of the conditions so you do the boundary check before a[i].
strsum(): for(i=bigger;i>=0;i--){ doesn't make sense as i is a size_t i.e. unsigned and in the loop body you do x[i-1] and y[i-1] which is wrong when i is 0. What about this?
for(i=bigger; i; i--){
result[i] = (x[i-1]+y[i-1]+carry)%10+'0';
carry = (x[i-1]+y[i-1]+carry)/10;
}
result[0] = carry + '0';
strsum(): x and y leak as you free them after a return:
free(x);
free(y);
if(result[0]==0){return "0";}
strsum(): to_num() allocates an array and then fill() allocates a copy (2 mallocs) and in strsum() you may leak the first malloc() for either x or y:
x = to_num(a,&size_a);
y = to_num(b,&size_b);
// ...
if(bigger != smaller){
if(bigger == size_a){
y = fill(y,bigger);
size_b = bigger;
}
else{
x = fill(x,bigger);
size_a = bigger;
}
}
Either realloc() in fill() instead of creating a copy or use a temporary variable in strsum() so you can free the original malloc'ed value:
if(bigger != smaller){
if(bigger == size_a){
char *tmp = fill(y, bigger);
free(y);
y = tmp;
size_b = bigger;
}
else{
char *tmp = fill(x, bigger);
free(x);
x = tmp;
size_a = bigger;
}
}
Btw, this is the same code in both branches so consider writing a function.
main(): leaks the returned string:
int main(){
char *s = strsum("9567","800");
printf("%s\n", s);
free(s);
}
strsum(): Once you fix above mention leak in main() you can no longer do return "0". You would need a size check and possible realloc() before doing:
return strcpy(result, "0");
or you could free(result); return strdup("0");. This is most likely the root cause (in calling test code you don't see).
I am currently unit testing the generic quicksort I made for which i will post the code right here below, along with the compare method I'm using and the code I'm running to test it.
The problem I find is that the array is not actually being sorted. The assertion fails and if I try to print the array after sorting it just prints the values in the predetermined order.
I tried unit testing the algorithm for int and float values and it did sort just fine.
I believe the problem may lie in the fact that I probably did not cast correctly the void pointer, resulting in undefined behaviour
void quicksort(void* pointer, int size, int start, int end, int (*compare)(void*,void*)){
if(end > start){
int index = partition(pointer, size, start, end,compare);
quicksort(pointer,size,start,index-1,compare);
quicksort(pointer,size,index+1,end,compare);
}
}
int partition(void * pointer, int size, int start, int end, int (*compare)(void*,void*)){
int beginning = start + 1;
int ending = end;
while(beginning <= ending){
if(compare(&pointer[beginning*size],&pointer[start*size]) >= 0){
beginning++;
}
else{
if(compare(&pointer[ending*size],&pointer[start*size]) == -1){
ending--;
}
else{
swap(&pointer[beginning*size],&pointer[ending*size],size);
beginning++;
ending--;
}
}
}
swap(&pointer[start*size],&pointer[ending*size],size);
return ending;
}
void swap(void* datablocka, void* datablockb, size_t elem_size){
char temp;
char *chara = datablocka;
char *charb = datablockb;
while(elem_size--){
temp = chara[elem_size];
chara[elem_size] = charb[elem_size];
charb[elem_size] = temp;
}
}
int compare_string(void* first, void* second){
char * a = first;
char * b = second;
int compare = strcmp(a,b);
if (compare < 0) return 1;
else if(compare == 0) return 0;
else return -1;
}
void quicksort_string_utest() {
char* a[] = {"a","aaa","abba","mime","pippo","cacciatorpediniere"};
char* b[] = {"a","aaa","abba","cacciatorpediniere","mime","pippo"};
int end = (sizeof(a)/sizeof(a[0]))-1;
quicksort(a, sizeof(a[0]), 0, end, compare_string );
for(int i = 0; i < 6; i++){
assert(strcmp(a[i],b[i]) == 0);
}
}
I would really appreciate your help with this, since as you can see I can't sort this out
In this program, what is passed to the comparision function compare_string is pointers to elements of the array.
In this case, elements of the array is char* and what is passed is pointers to char*.
Therefore, to obtain the pointers to strings, you have to dereference the passed pointers.
Try this:
int compare_string(void* first, void* second){
char ** a = first;
char ** b = second;
int compare = strcmp(*a,*b);
if (compare < 0) return 1;
else if(compare == 0) return 0;
else return -1;
}
I wrote this code snippet to sort an array of strings into an order that minimises their concatenation:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int cmpstr(const void* p1, const void* p2){
int p1l = strlen((const char*)p1);
int p2l = strlen((const char*)p2);
int r = strncmp((const char*)p1, (const char*)p2, p1l<p2l?p1l:p2l);
if(r == 0 && p1l != p2l){
if(p1l < p2l){
return cmpstr(p1, (const char*)p2 + p1l);
}
return cmpstr((const char*)p1 + p2l, p2);
}
return r;
}
int main(){
const char* arrstr[] = {"93", "936", "15", "152", "946"};
int num = sizeof(arrstr) / sizeof(char*);
qsort(arrstr, num, sizeof(char*), cmpstr);
for(int i = 0; i < num; i++){
printf("%s\n", arrstr[i]);
}
}
These strings should sort in the order 15 152 936 93 946. We want 93 to be between 936 and 946 because 936 93 < 93 936 and 93 946 < 946 93 (ignoring the spaces added for clarity).
But the code didn't work as expected. The array didn't get sorted at all, although my tests of cmpstr() worked exactly as I expected.
What did I get wrong?
I noticed that when I change the cast part of the cmpstr() from *(char* const*) to (char*), qsort() doesn't work as well. Why is that?
The comparison function passed to qsort receives the address of the two array elements to compare. Since each array element is a char *, the address of each of these elements is a char **. So you're missing one level of indirection.
You need to cast each parameter to a char * const *, then dereference to get the pointer to the string:
int cmpstr(const void* p1p, const void* p2p){
char *p1 = *(char * const *)p1p;
char *p2 = *(char * const *)p2p;
...
}
EDIT:
Because you want to call this function recursively, you need a non-recursive wrapper function around your recursive function since the parameters they take are not the same:
// internal recursive function that takes two strings
static int cmpstr_int(const char* p1, const char* p2){
int p1l = strlen(p1);
int p2l = strlen(p2);
int r = strncmp(p1, p2, p1l<p2l?p1l:p2l);
if(r == 0 && p1l != p2l){
if(p1l < p2l){
return cmpstr_int(p1, p2 + p1l);
}
return cmpstr_int(p1 + p2l, p2);
}
return r;
}
// comparison function that extracts desired datatype from void * params
// and passes them to recursive function
static int cmpstr(const void* p1p, const void* p2p){
const char *p1 = *(char * const *)p1p;
const char *p2 = *(char * const *)p2p;
return cmpstr_int(p1, p2);
}
Your comparison function receives pointers to the elements of the array. Each element is a pointer to char, so you'll get a pointer to a pointer to char.
The comparison logic is also somewhat over-complicated; here's a working version:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int cmpstr(const void* a_, const void* b_)
{
const char *const *a = a_;
const char *const *b = b_;
int la = strlen(*a);
int lb = strlen(*b);
if (la == lb) {
/* same length - sort lexicographically */
return strcmp(*a, *b);
}
if (la < lb) {
/* a is shorter */
int result = strncmp(*a, *b, la);
if (!result) {
/* a is a prefix of b */
result = strcmp(*a, *b + la);
}
return result;
}
/* else, b is shorter - re-enter with arguments swapped,
and negate the result */
return -cmpstr(b_, a_);
}
int main() {
const char* arrstr[] = {"93", "936", "15", "152", "946"};
const size_t num = sizeof arrstr / sizeof *arrstr;
qsort(arrstr, num, sizeof *arrstr, cmpstr);
for (size_t i = 0; i < num; i++) {
printf("%s\n", arrstr[i]);
}
}
Output:
15
152
936
93
946
If you think that my cmpstr() above deviates too far from the original, consider this less intrusively-modified code, which uses the recursive comparison that you intended, with a separate wrapper to adapt it to fit qsort():
int compare_strings(const char *a, const char *b)
{
int la = strlen(a);
int lb = strlen(b);
int r = strncmp(a, b, la<lb?la:lb);
if (r == 0 && la != lb) {
if (la < lb) {
return compare_strings(a, b + la);
}
return compare_strings(a + lb, b);
}
return r;
}
int compare_strings_qsort(const void* a_, const void* b_)
{
const char *const *a = a_;
const char *const *b = b_;
return compare_strings(*a, *b);
}
I still had to change your variable names, as I find p1l and the like hard to read. I could simplify a bit further, which I think is clearer than both the original function and my first attempt above (but probably needs some comments):
int compare_strings(const char *a, const char *b)
{
const int la = strlen(a);
const int lb = strlen(b);
const int r = strncmp(a, b, la<lb?la:lb);
return (la == lb || r)
? r
: (la < lb)
? compare_strings(a, b + la)
: compare_strings(a + lb, b);
}
Here is my code, it takes as input an integer s which is the number of strings I want it to process and then it takes s strings as input. For each of them it should output a greater lexicographical permutation of the letters, the smallest one. The problem is that it compiles fine, but at runtime it crashes and I really don't know why. Any suggestion?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare(const void *a, const void * b);
void swap(char* a, char* b);
int findCeil (char* str, char first, int l, int h);
void nextPermutation(char* w);
int main(void){
int s;
char* w;
scanf("%d", &s);
while(s--){
w = (char*)malloc(sizeof(101));
if(w == NULL){ printf("Malloc failed.\n"); return 1;}
scanf("%s", w);
nextPermutation(w);
free(w);
}
return 0;
}
//function for qsort()
int compare(const void *a, const void * b){
return ( *(char *)a - *(char *)b );
}
//utility function
void swap(char* a, char* b){
char t = *a;
*a = *b;
*b = t;
}
/* This function finds the index of the smallest character
which is greater than 'first' and is present in str[l..h]*/
int findCeil (char str[], char first, int l, int h){
int ceilIndex = l;
int i;
// find the smallest character greater than first
for (i = l+1; i <= h; ++i)
if (str[i] > first && str[i] < str[ceilIndex])
ceilIndex = i;
return ceilIndex;
}
void nextPermutation(char* w){
int size = strlen(w);
int i;
// Find the rightmost character which is smaller than its next
// character. Let us call it 'first char'
for(i = size - 2; i >= 0; --i){
if(w[i] < w[i+1])
break;
}
// If there is no such chracter, all are sorted in decreasing order,
//it means we are done.
if(i == -1)
printf("no answer\n");
else{
int ceilIndex = findCeil(w, w[i], i + 1, size - 1 );
// Swap first and second characters
swap( &w[i], &w[ceilIndex] );
// Sort the string on right of 'first char'
qsort( w + i + 1, size - i - 1, sizeof(w[0]), compare );
printf("%s\n", w);
}
}
What does sizeof(101) return?
Hint: it's not 101...
The problem is that you tried using malloc(sizeof(101)). When using it, it actually allocating you a memory of int size which is probably not what you meant.
I guess you meant malloc(sizeof("101")) since you convert it later to char*. Or perhaps you wanted use malloc(101) to get an exact memory size.
I'm trying to implement an all-purpose function for printing 2D data. What I've come up with is:
int mprintf(FILE* f, char* fmt, void** data, size_t cols, size_t rows)
The challenge is determining how many bits to read at once from data, based on fmt.
The format fmt is going to be the stdlib's-specific format for printf() and alike.
Do you have any knowledge of already-existing features from stdlibc (GNU GCC C's) I could use to ease this up?
I try avoiding having to do it all manually, because I know "I am stupid" (I don't want to introduce stupid bugs). Thus, reusing code would be the bug-freest way.
Thanks
Addendum
I see there's a /usr/include/printf.h. Can't I use any of those functions to do it right and ease my job at the same time?
Design proposed in question:
int mprintf(FILE *f, char *fmt, void **data, size_t cols, size_t rows);
High-level design points
If you want to print a 4x4 section of an 8x8 matrix, you need to know the row length of the matrix as well as the size to print. Or you may prefer to have that as a separate function.
Presumably, the format will define the separation between matrix entries, or will you force a space between them, or what? (If the user specifies "%d", will the numbers all be joined together?)
You're implicitly assuming that the matrix will be printed by itself, left-justified on the page. How would you adapt the interface to print the matrix elsewhere? Leading spaces on the line? Text before each line of the matrix? Text after line of the matrix?
Low-level design points
The format string should be a const char *.
Clearly, your code can do what printf() does, more or less. It looks at the format conversion specifier, and then determines what type to collect. Your code will be slightly more complex, in some respects. You'll need to treat an array of unsigned char differently from an array of short, etc. C99 provides for modifier hh for signed char or unsigned char (before the format specifiers d, i, o, u, x, or X), and the modifier h for short or unsigned short. You should probably recognize these too. Similarly, the modifiers L for long double and l for long and ll for long long should be handled. Interestingly, printf() does not have to deal with float (because any single float value is automatically promoted to double), but your code will have to do that. By analogy with h and L, you should probably use H as the modifier to indicate a float array. Note that this case means you will need to pass to the printf() function a different format from the one specified by the user. You can make a copy of the user-provided format, dropping the 'H' (or use exactly the user-provided format except when it contains the 'H'; you will not modify the user's format string - not least because the revised interface says it is a constant string).
Ultimately, your code will have to determine the size of the elements in the array. It might be that you modify the interface to include that information - by analogy with functions such as bsearch() and qsort(), or fread() and fwrite(). Or you can determine it from the format specifier.
Note that although GCC allows pointer arithmetic on void *, Standard C does not.
Are you sure you want a void ** in the interface? I think it would be easier to understand if you pass the address of the starting element of the array - a single level of pointer.
short s[3][4];
float f[2][5];
char c[20][30];
mprintf(fp, "%3hd", &s[0][0], 4, 3);
mprintf(fp, "%8.4Hf", &f[0][0], 5, 2);
mprintf(fp, "%hhu", &c[0][0], 30, 20);
This changes the data parameter to a void *. Maybe I'm too decaffeinated, but I can't see how to make a double pointer work sanely.
Outline
Determine size of elements and correct format string.
For each row
For each column
Find the data for the element
Call an appropriate function to print it
Print a separator if you need to
Print a newline
Illustration
This code assumes a '0 is success' convention. It assumes you are dealing with numbers, not matrices of pointers or strings.
typedef int (*PrintItem)(FILE *fp, const char *format, void *element);
static int printChar(FILE *fp, const char *format, void *element)
{
char c = *(char *)element;
return (fprintf(fp, format, c) <= 0) ? -1 : 0;
}
...and a whole lot more like this...
static int printLongDouble(FILE *fp, const char *format, void *element)
{
long double ld = *(long double *)element;
return (fprintf(fp, format, ld) <= 0) ? -1 : 0;
}
int mprintf(FILE *fp, const char *fmt, void *data, size_t cols, size_t rows)
{
char *format = strdup(fmt);
int rc = 0;
size_t size;
PrintItem print;
if ((rc = print_info(format, &size, &print)) == 0)
{
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
{
void *element = (char *)data + (i * cols + j) * size;
if ((rc = print(fp, format, element)) < 0)
goto exit_loop;
}
fputc('\n', fp); // Possible error ignored
}
}
exit_loop:
free(fmt);
return rc;
}
static int print_info(char *fmt, size_t *size, PrintItem *print)
{
...analyze format string...
...set *size to the correct size...
...set *print to the correct printing function...
...modify format string if need so be...
...return 0 on success, -1 on failure...
}
Working code
Left as an exercise:
Pointers
Strings
size_t
intmax_t
ptrdiff_t
Note that I would not normally use the += or *= operators on the same line as other assignments; it was convenient for generating test numbers, though.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
/* mprintf() - print a matrix of size cols x rows */
extern int mprintf(FILE *fp, const char *fmt, void *data, size_t cols, size_t rows);
typedef int (*PrintItem)(FILE *fp, const char *format, void *element);
static int printChar(FILE *fp, const char *format, void *element)
{
char value = *(char *)element;
return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}
static int printShort(FILE *fp, const char *format, void *element)
{
short value = *(short *)element;
return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}
static int printInt(FILE *fp, const char *format, void *element)
{
int value = *(int *)element;
return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}
static int printLong(FILE *fp, const char *format, void *element)
{
long value = *(long *)element;
return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}
static int printLongLong(FILE *fp, const char *format, void *element)
{
long long value = *(long long *)element;
return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}
static int printFloat(FILE *fp, const char *format, void *element)
{
float value = *(float *)element;
return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}
static int printDouble(FILE *fp, const char *format, void *element)
{
double value = *(double *)element;
return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}
static int printLongDouble(FILE *fp, const char *format, void *element)
{
long double valued = *(long double *)element;
return (fprintf(fp, format, valued) <= 0) ? -1 : 0;
}
/* analyze format string - all arguments can be modified */
static int print_info(char *format, size_t *size, PrintItem *print)
{
char *fmt = format;
char c;
bool scanning_type = false;
int hcount = 0;
int lcount = 0;
int Hcount = 0;
int Lcount = 0;
char *Hptr = 0;
while ((c = *fmt++) != '\0')
{
switch (c)
{
case '%':
if (*fmt == '%')
fmt++;
else
scanning_type = true;
break;
/* Length modifiers */
case 'h':
if (scanning_type)
hcount++;
break;
case 'l':
if (scanning_type)
lcount++;
break;
case 'L':
if (scanning_type)
Lcount++;
break;
case 'H':
if (scanning_type)
{
Hptr = fmt - 1;
Hcount++;
}
break;
/* Integer format specifiers */
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X':
if (scanning_type)
{
/* No floating point modifiers */
if (Hcount > 0 || Lcount > 0)
return -1;
/* Can't be both longer and shorter than int at the same time */
if (hcount > 0 && lcount > 0)
return -1;
/* Valid modifiers are h, hh, l, ll */
if (hcount > 2 || lcount > 2)
return -1;
if (hcount == 2)
{
*size = sizeof(char);
*print = printChar;
}
else if (hcount == 1)
{
*size = sizeof(short);
*print = printShort;
}
else if (lcount == 2)
{
*size = sizeof(long long);
*print = printLongLong;
}
else if (lcount == 1)
{
*size = sizeof(long);
*print = printLong;
}
else
{
*size = sizeof(int);
*print = printInt;
}
return 0;
}
break;
/* Floating point format specifiers */
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
case 'a':
case 'A':
if (scanning_type)
{
/* No integer modifiers */
if (lcount > 0 || hcount > 0)
return -1;
/* Can't be both float and long double at once */
if (Lcount > 0 && Hcount > 0)
return -1;
/* Cannot repeat L or H modifiers */
if (Lcount > 1 || Hcount > 1)
return -1;
if (Lcount > 0)
{
*size = sizeof(long double);
*print = printLongDouble;
}
else if (Hcount > 0)
{
/* modify format string, dropping the H */
assert(Hptr != 0 && strlen(Hptr+1) > 0);
memmove(Hptr, Hptr+1, strlen(Hptr)); // Copy '\0' too!
*size = sizeof(float);
*print = printFloat;
}
else
{
*size = sizeof(double);
*print = printDouble;
}
return 0;
}
break;
default:
break;
}
}
return -1;
}
int mprintf(FILE *fp, const char *fmt, void *data, size_t cols, size_t rows)
{
char *format = strdup(fmt); // strdup() is not standard C99
int rc = 0;
size_t size;
PrintItem print;
if ((rc = print_info(format, &size, &print)) == 0)
{
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
{
void *element = (char *)data + (i * cols + j) * size;
if ((rc = print(fp, format, element)) < 0)
{
fputc('\n', fp); // Or fputs("<<error>>\n");
goto exit_loop;
}
}
fputc('\n', fp); // Possible error ignored
}
}
exit_loop:
free(format);
return rc;
}
#ifdef TEST
int main(void)
{
short s[3][4];
float f[2][5];
char c[8][9];
FILE *fp = stdout;
int v = 0;
for (size_t i = 0; i < 3; i++)
{
for (size_t j = 0; j < 4; j++)
{
s[i][j] = (v += 13) & 0x7FFF;
printf("s[%zu,%zu] = %hd\n", i, j, s[i][j]);
}
}
v = 0;
for (size_t i = 0; i < 8; i++)
{
for (size_t j = 0; j < 9; j++)
{
c[i][j] = (v += 13) & 0x7F;
printf("c[%zu,%zu] = %hhu\n", i, j, c[i][j]);
}
}
float x = 1.234;
for (size_t i = 0; i < 2; i++)
{
for (size_t j = 0; j < 5; j++)
{
f[i][j] = x *= 13.12;
printf("f[%zu,%zu] = %g\n", i, j, f[i][j]);
}
}
mprintf(fp, " %5hd", &s[0][0], 4, 3);
mprintf(fp, "%%(%3hhu) ", &c[0][0], 8, 9);
mprintf(fp, " %11.4He", &f[0][0], 5, 2);
mprintf(fp, " %11.4He", f, 5, 2);
return 0;
}
#endif /* TEST */
Assuming I understood your requirements and assuming fmt specifies how to format only one element (it could be extended so that it is not something you can pass to (f)printf directly, but a description of how to print the whole matrix - which I think is more useful), you simply have to do some easy string manipulation to find which type the data is, thus deciding how to cast your void** pointer (look here).
The cases won't be infinite but only restricted on how many data types you wish to support.
Once you casted the pointer, a simple for loop based on cols and rows will do the trick.
There is no means to do this that is universally applicable, it all depends on HOW you want it displayed. However, the following is pretty general and you can adapt it if you need it changed slightly:
int mprintf(FILE* file, char* fmt, int** data, size_t cols, size_t rows) {
for(int r=0; r<rows; r++) {
for(int c=0; c<cols; c++) {
fprintf(file, fmt, data[r][c]); // fmt can be e.g. "%9i"
}
printf("\n");
}
}
You can't determine the argument type from the printf format string. OP's problem is that he wants to print like printf, but consume arguments by pointer like scanf. Unfortunately, the printf conversion specifiers are insufficient for the 'scanf' side of the task. Consider "%f", which printf uses for floats or doubles. The argument conversion rules for variadic functions mean that printf always sees doubles and doesn't care, but in this case scanf needs "%f" and "%lf" to distinguish them. Nor can the OP try to use scanf versions of the specifiers, as this will break printf. Neither is a subset of the other.
Your generic function needs to be told at least both the format conversion specifier and how big the elements are to do the right pointer arithmetic. (In fact, if you are going to delegate to fprintf and friends, the size is insufficient - you will need to know the type at the fprintf call site). I would probably pass in a function rather than two parameters to avoid the risk of mismatch between the two, thus:
#include <stdio.h>
typedef const void *(*Printfunc)(FILE *f, const void *datum);
/* print an integer and advance the pointer */
static const void* print_int(FILE *f, const void *datum)
{
const int* p = datum;
fprintf(f, "%d", *p);
return p + 1;
}
/* print a char and advance the pointer */
static const void* print_char(FILE *f, const void *datum)
{
const char* p = datum;
fprintf(f, "%c", *p);
return p + 1;
}
static void mprint(FILE *f, Printfunc p, const void *data, size_t cols, size_t rows)
{
const void *next = data;
int i;
for (i = 0; i < rows; ++i) {
int j;
for (j = 0; j < cols; ++j) {
next = p(f, next);
putc(' ', f);
}
putc('\n', f);
}
}
int main()
{
int imatrix[3][2] = { { 0, 1 }, { 2, 3 }, { 4, 5 } };
char cmatrix[2][2] = { { 'a', 'b' }, { 'c', 'd' } };
mprint(stdout, print_int, imatrix, 2, 3);
mprint(stdout, print_char, cmatrix, 2, 2);
return 0;
}
I think by typecasting the void **data based upon the *fmt would do the trick. I am not sure if I understand your question correctly. Because you can you can put a switch-case statement based upon *fmt for typcasting, and then use **data as 2-D array to print it. Use rows/columns as index of the 2-D array.