Концепция пустого указателя в C-программировании - PullRequest
123 голосов
/ 28 марта 2009

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

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

например ::1005 *

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-адреса.

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

Ответы [ 15 ]

92 голосов
/ 28 марта 2009

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

Нет, 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* p = ...
void *p2 = p + 1; /* what exactly is the size of void?? */
28 голосов
/ 28 марта 2009

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

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

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

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

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

15 голосов
/ 04 июня 2009

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

6 голосов
/ 19 сентября 2013

Пока что мое занижение на указателе void выглядит следующим образом.

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

main()
{
    int *p;

    void *vp;

    vp=p;
} 

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

Я попытался написать простой код на C, который принимает целое число или число с плавающей точкой в ​​качестве аргумента и пытается сделать его + 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 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 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 *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 голосов
/ 22 октября 2010

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

2 голосов
/ 30 марта 2009
void abc(void *a, int b) {
  char *format[] = {"%d", "%c", "%f"};
  printf(format[b-1], a);
}
2 голосов
/ 28 марта 2009

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

1 голос
/ 29 марта 2009

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

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

Рассматривали ли вы язык программирования C ++ для вашей программы? Или есть какие-то ограничения, которые запрещают его использование?

0 голосов
/ 03 июня 2019

Вы не можете разыменовать указатель, не указав его тип, потому что разные типы данных будут иметь разные размеры в памяти, т. Е. Int - 4 байта, char - 1 байт.

0 голосов
/ 16 мая 2019

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

struct Point {
  int x;
  int y;
};

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

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

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

00000000 00000000 00000000 00000000 00000000 00000000 00000000

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

10001010 11111001 00010010 11000101
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...