Концепция указателя void в программировании на языке C

Можно ли разыменовать пустой указатель без приведения типов в языке программирования C?

Кроме того, есть ли способ обобщить функцию, которая может получить указатель и сохранить его в пустом указателе, и с помощью этого пустого указателя мы можем сделать обобщенную функцию?

например:

void abc(void *a, int b)
{
   if(b==1)
      printf("%d",*(int*)a);     // If integer pointer is received
   else if(b==2)
      printf("%c",*(char*)a);     // If character pointer is received
   else if(b==3)
      printf("%f",*(float*)a);     // If float pointer is received
}

Я хочу сделать эту функцию общей без использования операторов if-else - возможно ли это?

Также, если есть хорошие статьи в Интернете, которые объясняют концепцию пустого указателя, было бы полезно, если бы вы могли предоставить URL-адреса.

Также возможна ли арифметика указателей с пустыми указателями?

Ответ 1

Можно ли разыменовать указатель void без ввода типов на языке программирования C...

Нет, void указывает на отсутствие типа, это не то, что вы можете разыменовать или назначить.

существует ли какой-либо способ обобщения функции, которая может получать указатель и хранить его в указателе void, и с помощью этого указателя void мы можем сделать обобщенную функцию.

Вы не можете просто разыменовать его переносимым способом, поскольку он может быть неправильно выровнен. Это может быть проблемой для некоторых архитектур, таких как ARM, где указатель на тип данных должен быть выровнен на границе размера типа данных (например, указатель на 32-разрядное целое должен быть выровнен на 4-байтовой границе, подлежащей разыменованию).

Например, чтение uint16_t из void*:

/* may receive wrong value if ptr is not 2-byte aligned */
uint16_t value = *(uint16_t*)ptr;
/* portable way of reading a little-endian value */
uint16_t value = *(uint8_t*)ptr
                | ((*((uint8_t*)ptr+1))<<8);

Кроме того, возможна арифметика указателя с указателями void...

Арифметика указателя невозможна при указателях void из-за отсутствия конкретного значения под указателем и, следовательно, размера.

void* p = ...
void *p2 = p + 1; /* what exactly is the size of void?? */

Ответ 2

В C a void * может быть преобразован в указатель на объект другого типа без явного приведения:

void abc(void *a, int b)
{
    int *test = a;
    /* ... */

Это не помогает при написании вашей функции более общим образом.

Вы не можете разыгрывать void * с преобразованием его в другой тип указателя, поскольку разыменование указателя - это получение значения объекта с указателем. Голый void не является допустимым типом, поэтому невозможна дефрагментация void *.

Арифметика указателя - это изменение значений указателя кратными значениям sizeof объектов с указателем. Опять же, поскольку void не является истинным типом, sizeof(void) не имеет значения, поэтому арифметика указателя недействительна на void *. (Некоторые реализации позволяют это, используя эквивалентную арифметику указателя для char *.)

Ответ 3

Вы должны знать, что в C, в отличие от Java или С#, нет абсолютно никакой возможности успешно "угадать" тип объекта, на который указывает указатель void *. Нечто похожее на "getClass()" просто не существует, поскольку эту информацию нигде не найти. По этой причине тип "общего", который вы ищете, всегда имеет явную метаинформацию, такую ​​как "int b" в вашем примере или строку формата в семействе функций printf.

Ответ 4

Указатель void известен как общий указатель, который может ссылаться на переменные любого типа данных.

Ответ 5

До сих пор мое занижение указателя void было следующим.

Когда переменная указателя объявляется с использованием ключевого слова void - она ​​становится переменной указателя общего назначения. Адрес любой переменной любого типа данных (char, int, float и т.д.) Может быть назначен переменной указателя void.

main()
{
    int *p;

    void *vp;

    vp=p;
} 

Поскольку другой указатель типа данных может быть назначен указателю на void, поэтому я использовал его в функции absolut_value (код, показанный ниже). Сделать общую функцию.

Я попытался написать простой C-код, который принимает целочисленный или float как аргумент и пытается сделать его + ve, если он отрицательный. Я написал следующий код,

#include<stdio.h>

void absolute_value ( void *j) // works if used float, obviously it must work but thats not my interest here.
{
    if ( *j < 0 )
        *j = *j * (-1);

}

int main()
{
    int i = 40;
    float f = -40;
    printf("print intiger i = %d \n",i);
    printf("print float f = %f \n",f);
    absolute_value(&i);
    absolute_value(&f);
    printf("print intiger i = %d \n",i);
    printf("print float f = %f \n",f);
    return 0;
}   

Но я получал ошибку, поэтому я понял, что мое понимание с указателем void неверно:( Итак, теперь я перейду к сбору очков, почему это так.

То, что мне нужно больше понять по указателям void, это то, что.

Нам нужно вывести указатель на указатель void, чтобы разыменовать его. Это связано с тем, что указатель void не имеет связанного с ним типа данных. Компилятор не знает (или угадывает?), На какой тип данных указывает указатель на пустоту. Поэтому, чтобы взять данные, на которые указывает указатель на void, мы выводим его с правильным типом данных, хранящихся внутри местоположения указателей void.

void main()

{

    int a=10;

    float b=35.75;

    void *ptr; // Declaring a void pointer

    ptr=&a; // Assigning address of integer to void pointer.

    printf("The value of integer variable is= %d",*( (int*) ptr) );// (int*)ptr - is used for type casting. Where as *((int*)ptr) dereferences the typecasted void pointer variable.

    ptr=&b; // Assigning address of float to void pointer.

    printf("The value of float variable is= %f",*( (float*) ptr) );

}

Указатель void может быть действительно полезен, если программист не уверен в типе данных, введенных конечным пользователем. В таком случае программист может использовать указатель void для указания местоположения неизвестного типа данных. Программа может быть настроена таким образом, чтобы попросить пользователя сообщить тип данных и тип литья может выполняться в соответствии с информацией, введенной пользователем. Ниже приведен фрагмент кода.

void funct(void *a, int z)
{
    if(z==1)
    printf("%d",*(int*)a); // If user inputs 1, then he means the data is an integer and type casting is done accordingly.
    else if(z==2)
    printf("%c",*(char*)a); // Typecasting for character pointer.
    else if(z==3)
    printf("%f",*(float*)a); // Typecasting for float pointer
}

Еще один важный момент, который вы должны иметь в виду о указателях void, состоит в том, что - арифметика указателя не может выполняться указателем void.

void *ptr;

int a;

ptr=&a;

ptr++; // This statement is invalid and will result in an error because 'ptr' is a void pointer variable.

Итак, теперь я понял, в чем была моя ошибка. Я исправляю то же самое.

Ссылки:

http://www.antoarts.com/void-pointers-in-c/

http://www.circuitstoday.com/void-pointers-in-c.

Новый код показан ниже.


#include<stdio.h>
#define INT 1
#define FLOAT 2

void absolute_value ( void *j, int *n)
{
    if ( *n == INT) {
        if ( *((int*)j) < 0 )
            *((int*)j) = *((int*)j) * (-1);
    }
    if ( *n == FLOAT ) {
        if ( *((float*)j) < 0 )
            *((float*)j) = *((float*)j) * (-1);
    }
}


int main()
{
    int i = 0,n=0;
    float f = 0;
    printf("Press 1 to enter integer or 2 got float then enter the value to get absolute value\n");
    scanf("%d",&n);
    printf("\n");
    if( n == 1) {
        scanf("%d",&i);
        printf("value entered before absolute function exec = %d \n",i);
        absolute_value(&i,&n);
        printf("value entered after absolute function exec = %d \n",i);
    }
    if( n == 2) {
        scanf("%f",&f);
        printf("value entered before absolute function exec = %f \n",f);
        absolute_value(&f,&n);
        printf("value entered after absolute function exec = %f \n",f);
    }
    else
    printf("unknown entry try again\n");
    return 0;
}   

Спасибо,

Ответ 6

A указатель void в c известен как общий указатель. Буквальное значение общего указателя - это указатель, который может указывать любой тип данных.

void *ptr;

  • Мы не можем разыменовать общий указатель на любой другой указатель.

  • Мы можем найти размер общего указателя с помощью оператора sizeof.

  • Универсальный указатель может содержать любые указатели, такие как указатель char, указатель структуры, массив указателя и т.д. без какого-либо приведения типов.

  • Любой тип указателя может содержать общий указатель без какого-либо приведения типов.

  • Общие указатели используются, когда мы хотим вернуть такой указатель, который применим ко всем типам указателей. Например, возвращаемый тип функции malloc является общим указателем, поскольку он может динамически распределять пространство памяти для хранения целого числа, float, структуры и т.д. Поэтому мы вводим тип возвращаемого типа в соответствующий тип указателя.

Ответ 7

void abc(void *a, int b) {
  char *format[] = {"%d", "%c", "%f"};
  printf(format[b-1], a);
}

Ответ 8

Нет, это невозможно. Какой тип должен иметь разыменованное значение?

Ответ 9

Я хочу сделать эту функцию общей, без использования ifs; возможно ли это?

Единственный простой способ, который я вижу, - использовать перегрузку, которая недоступна в программировании программирования на языке программирования AFAIK.

Вы рассматривали язык программирования С++ для своей программы? Или существует ли ограничение, запрещающее его использование?

Ответ 10

Поскольку C является статически типизированным, строго типизированным языком, вы должны решить тип переменной перед компиляцией. Когда вы пытаетесь эмулировать generics в C, вы в конечном итоге попытаетесь перезаписать С++ снова, поэтому было бы лучше использовать С++ вместо этого.

Ответ 11

Вы можете легко распечатать недействительный принтер

int p=15;
void *q;
q=&p;
printf("%d",*((int*)q));

Ответ 12

Указатель void является универсальным указателем. Адрес любого типа данных любой переменной может быть назначен указателю void.

int a = 10;
float b = 3.14;
void *ptr;
ptr = &a;
printf( "data is %d " , *((int *)ptr)); 
//(int *)ptr used for typecasting dereferencing as int
ptr = &b;
printf( "data is %f " , *((float *)ptr));
//(float *)ptr used for typecasting dereferencing as float

Ответ 13

Вот краткий указатель на void указатели: https://www.learncpp.com/cpp-tutorial/613-void-pointers/

6.13 - Пустые указатели

Поскольку указатель void не знает, на какой тип объекта он указывает, он не может быть разыменован напрямую! Скорее, указатель void должен быть явно приведен к другому типу указателя, прежде чем он будет разыменован.

Если пустой указатель не знает, на что он указывает, как мы узнаем, к чему его привести? В конечном счете, это зависит от вас, чтобы отслеживать.

Сборник пустых указателей

Невозможно сделать арифметику указателя на пустом указателе. Это связано с тем, что арифметика указателя требует, чтобы указатель знал, на какой объект размера он указывает, поэтому он может соответственно увеличивать или уменьшать указатель.

Предполагая, что машинная память является адресуемой по байту и не требует выравниваемого доступа, наиболее универсальный и атомарный (наиболее близкий к представлению машинного уровня) способ интерпретации void* - это указатель на байт, uint8_t*. uint8_t* void* к uint8_t* позволит вам, например, распечатать первые uint8_t*/однако сколько угодно желаемых байтов, начиная с этого адреса, но вы больше ничего не сможете сделать.

uint8_t* byte_p = (uint8_t*)p;
for (uint8_t* i = byte_p; i < byte_p + 8; i++) {
  printf("%x ",*i);
}

Ответ 14

По сути, в Си "типы" - это способ интерпретации байтов в памяти. Например, какой следующий код

struct Point {
  int x;
  int y;
};

int main() {
  struct Point p;
  p.x = 0;
  p.y = 0;
}

Говорит "Когда я запускаю main, я хочу выделить 4 (размер целого числа) + 4 (размер целого числа) = 8 (общее количество байт) памяти. Когда я пишу".x "как lvalue для значения с меткой типа Точка во время компиляции, извлечение данных из ячейки памяти указателя плюс четыре байта. Присвойте возвращаемому значению метку времени компиляции "int."

Внутри компьютера во время выполнения ваша структура "Point" выглядит следующим образом:

00000000 00000000 00000000 00000000 00000000 00000000 00000000

А вот как может выглядеть ваш тип данных void*: (предполагается, что 32-битный компьютер)

10001010 11111001 00010010 11000101

Ответ 15

Это не сработает, но void * может помочь в определении общего указателя на функции и передачи его в качестве аргумента другой функции (аналогично обратному вызову в Java) или определить ее структуру, похожую на oop.