Home > Back-end >  In C, is there a way to cast a void pointer to a defined size type?
In C, is there a way to cast a void pointer to a defined size type?

Time:09-25

I need a “generic" scanf function that valids the input while also printing a message for the instruction and for the error. Scan is function that takes a void pointer, which is the direction of the variable that will store the input value, the specifier for the type, and the messages. The code looks like this:

void scan(char specifier, void *dest, string instruction, string error) {
bool success = true;
do {
    if(!success) {
        while (getchar()!='\n');
        perror(error);
    }
    puts(instruction);
    switch (specifier)
    {
    case 'd':
        success = scanf(" %d", (int *)dest);
        break;
        
    case 's’:
        // In this particular program I need the strings to be 30 bytes with spaces.
        success = scanf(" 0[^\n]s", (char *)dest);
        break;

    case 'h':
        success = scanf(" %hd", (short *)dest);
        break;

    case 'f':
        success = scanf(" %f", (float *)dest);
        break;
    }
} while (!success ); }

But is it possible to just cast the void pointer to a pointer of a given size type instead of the switch? Like this:

double myVar;
size_t varSize = sizeof(double); void * dest = &myVar;
// specifierForDest is a string
scanf(specifierForDest, (varSize *)dest);

so that dest becomes a pointer to a variable of size varSize.

Or any other known method better than writing a case for every type in C?

CodePudding user response:

The C standard does not provide a way to dynamically convert pointers to the types of pointers that scanf requires. This means that, once your function has received a void * argument, the only way for it to convert to the types that scanf is specified to receive is to handle them as individual cases.

An alternative is not to receive the argument as void * but rather to receive it in a variable argument list and forward it to vscanf, which is designed for this sort of operation. To do this, you must move the destination parameter to the end of the parameter list. Below is example code. The variable argument list handling is in the last few lines.

#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>


typedef char *string;


void scan(char specifier, string instruction, string error,...)
{
    bool success = true;
    do
    {
        if (!success)
        {
            int c;
            do
                c = getchar();
            while (c != EOF && c != '\n');
            perror(error);
        }
        puts(instruction);
        char *specifierForDest;
        switch (specifier)
        {
            case 'd': specifierForDest = " %d";       break;
            case 's': specifierForDest = " 0[^\n]"; break;
            case 'h': specifierForDest = " %hd";      break;
            case 'f': specifierForDest = " %f";       break;
            default:
                ;   // Do something here; do not neglect error cases.
        }
        va_list ap;           // Create argument list pointer.
        va_start(ap, error);  // Initialize argument list handling.
        success = 1 == vscanf(specifierForDest, ap);
                              // Forward argument to vscanf.
        va_end(ap);           // Clean up argument list handling.
    } while (!success);
}

CodePudding user response:

Thanks. With all your suggestions I came up with this:

#include <stdarg.h>
#include <stdlib.h>

typedef char string[30];
void scan(string specifier, string instruction, string error, ...) {
bool success = true;
va_list ap; va_start(ap, error);
do {
    if(!success) {
        while (getchar()!='\n');
        puts(error);
    }
    puts(instruction);
    success = vscanf(specifier, ap);
} while (!success );
va_end(ap); }

Which works fine.

  • Related