Konsep void pointer dalam pemrograman C.

129

Apakah mungkin untuk melakukan dereferensi pointer kosong tanpa tipe-casting dalam bahasa pemrograman C?

Juga, adakah cara menggeneralisasi suatu fungsi yang dapat menerima pointer dan menyimpannya dalam pointer kosong dan dengan menggunakan pointer kosong itu, dapatkah kita membuat fungsi umum?

untuk misalnya:

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
}

Saya ingin membuat fungsi ini generik tanpa menggunakan pernyataan if-else - apakah ini mungkin?

Juga jika ada artikel internet yang bagus yang menjelaskan konsep void pointer, maka akan bermanfaat jika Anda bisa memberikan URL.

Juga, apakah pointer aritmatika dengan pointer kosong mungkin?

AGeek
sumber

Jawaban:

97

Apakah mungkin untuk mengubah referensi void pointer tanpa mengetik dalam bahasa pemrograman C ...

Tidak, voidmenunjukkan tidak adanya tipe, itu bukan sesuatu yang dapat Anda referensi atau tetapkan.

apakah ada cara untuk menggeneralisasikan suatu fungsi yang dapat menerima pointer dan menyimpannya dalam void pointer dan dengan menggunakan void pointer kita dapat membuat fungsi umum.

Anda tidak bisa hanya melakukan dereferensi dengan cara portabel, karena mungkin tidak selaras dengan benar. Ini mungkin menjadi masalah pada beberapa arsitektur seperti ARM, di mana pointer ke tipe data harus disejajarkan pada batas ukuran tipe data (misal pointer ke integer 32-bit harus disejajarkan pada batas 4-byte yang akan direferensikan).

Misalnya, membaca uint16_tdari 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);

Juga, apakah pointer aritmatika dengan kemungkinan batal pointer ...

Aritmatika pointer tidak dimungkinkan pada pointer voidkarena kurangnya nilai konkret di bawah pointer dan karenanya ukuran.

void* p = ...
void *p2 = p + 1; /* what exactly is the size of void?? */
Alex B
sumber
2
Kecuali jika saya salah mengingat, Anda dapat dengan aman melakukan void * dalam dua cara. Casting ke char * selalu dapat diterima, dan jika Anda tahu tipe aslinya, itu menunjuk ke Anda dapat melakukan cast ke tipe itu. Void * telah kehilangan info jenis sehingga harus disimpan di tempat lain.
Dan Olson
1
Ya, Anda selalu dapat melakukan dereferensi jika Anda menggunakan tipe lain, tetapi Anda tidak dapat melakukan hal yang sama jika Anda tidak melakukan cast. Singkatnya, kompiler tidak akan tahu instruksi perakitan apa yang digunakan untuk operasi matematika sampai Anda melemparkannya.
Zachary Hamm
20
Saya pikir GCC memperlakukan aritmatika pada void *pointer sama seperti char *, tapi itu bukan standar dan Anda tidak boleh bergantung padanya.
ephemient
5
Saya tidak yakin saya mengikuti. Dalam contoh bahwa OP yang tercantum di atas jenis yang masuk dijamin benar oleh pengguna fungsi. Apakah Anda mengatakan bahwa jika kami memiliki sesuatu di mana kami menetapkan nilai tipe yang dikenal ke sebuah pointer, melemparkannya ke pointer kosong dan kemudian melemparkannya kembali ke jenis pointer asli yang bisa menjadi tidak selaras? Saya tidak mengerti mengapa kompiler akan merusak Anda seperti itu hanya hal-hal yang secara acak tidak selaras hanya karena Anda melemparkannya ke sebuah void pointer. Atau apakah Anda hanya mengatakan bahwa kami tidak dapat mempercayai pengguna untuk mengetahui jenis argumen yang mereka sampaikan ke fungsi?
doliver
2
@doliver yang saya maksud # 2 ("kami tidak bisa mempercayai pengguna untuk mengetahui jenis argumen yang mereka sampaikan ke fungsi"). Tetapi meninjau kembali jawaban saya dari 6 tahun yang lalu (ugh, apakah sudah benar-benar selama itu?) Saya bisa melihat apa yang Anda maksud, itu tidak selalu terjadi: ketika penunjuk asli menunjuk ke jenis yang tepat, itu akan sudah disejajarkan, sehingga akan aman untuk melakukan cast tanpa masalah penyelarasan.
Alex B
31

Dalam C, a void *dapat dikonversi menjadi pointer ke objek dari tipe yang berbeda tanpa gips eksplisit:

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

Ini tidak membantu menulis fungsi Anda dengan cara yang lebih umum.

Anda tidak dapat melakukan dereferensi void *dengan mengonversinya ke jenis pointer yang berbeda karena dereferencing pointer memperoleh nilai objek yang ditunjuk. Telanjang voidbukan jenis yang valid sehingga derefencing a void *tidak mungkin.

Aritmetika pointer adalah tentang mengubah nilai pointer dengan kelipatan objek sizeofyang ditunjuk-ke. Sekali lagi, karena voidbukan tipe yang benar, sizeof(void)tidak memiliki makna sehingga aritmatika pointer tidak valid void *. (Beberapa implementasi memungkinkan, menggunakan aritmatika pointer setara untuk char *.)

CB Bailey
sumber
1
@CharlesBailey Dalam kode Anda, Anda menulis void abc(void *a, int b). Tapi mengapa tidak digunakan void abc(int *a, int b), karena pada fungsi Anda, Anda akhirnya akan mendefinisikan int *test = a;, itu akan menyimpan satu variabel, dan saya tidak melihat kelebihan lainnya dengan mendefinisikan variabel lain. Saya melihat banyak kode yang ditulis dengan cara ini menggunakan void *sebagai parameter, dan casting variabel nanti dalam fungsinya. Tetapi karena fungsi ini ditulis oleh penulis, maka ia harus tahu penggunaan variabel, oleh karena itu, mengapa menggunakan void *? Terima kasih
Lion Lai
15

Anda harus menyadari bahwa di C, tidak seperti Java atau C #, sama sekali tidak ada kemungkinan untuk berhasil "menebak" jenis objek yang void*ditunjuk oleh pointer. Sesuatu yang mirip dengan getClass()tidak ada, karena informasi ini tidak dapat ditemukan. Untuk alasan itu, jenis "generik" yang Anda cari selalu datang dengan metainformation eksplisit, seperti int bpada contoh Anda atau string format dalam printfkeluarga fungsi.

Erich Kitzmueller
sumber
7

Pointer void dikenal sebagai pointer generik, yang dapat merujuk ke variabel tipe data apa pun.

Sneha
sumber
6

Sejauh ini pernyataan saya tentang void pointer adalah sebagai berikut.

Ketika variabel pointer dinyatakan menggunakan kata kunci void - itu menjadi variabel pointer tujuan umum. Alamat variabel apa pun dari tipe data apa pun (char, int, float dll.) Dapat ditetapkan ke variabel penunjuk kosong.

main()
{
    int *p;

    void *vp;

    vp=p;
} 

Karena pointer tipe data lain dapat ditugaskan ke void pointer, jadi saya menggunakannya dalam fungsi absolut_value (kode yang ditunjukkan di bawah). Untuk membuat fungsi umum.

Saya mencoba untuk menulis kode C sederhana yang mengambil integer atau float sebagai argumen dan mencoba membuatnya + ve, jika negatif. Saya menulis kode berikut,

#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;
}   

Tapi saya mendapatkan kesalahan, jadi saya tahu bahwa pemahaman saya dengan void pointer tidak benar :(. Jadi sekarang saya akan bergerak ke arah untuk mengumpulkan poin mengapa begitu.

Hal-hal yang perlu saya pahami lebih lanjut tentang void pointer adalah itu.

Kita perlu mengetikkan variabel void pointer untuk melakukan dereferensi. Ini karena void pointer tidak memiliki tipe data yang terkait dengannya. Tidak ada cara kompiler dapat mengetahui (atau menebak?) Tipe data apa yang ditunjuk oleh void pointer. Jadi untuk mengambil data yang ditunjuk oleh void pointer kita mengetikkannya dengan tipe data yang benar yang disimpan di dalam lokasi void pointer.

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 pointer bisa sangat berguna jika programmer tidak yakin tentang tipe data dari data yang dimasukkan oleh pengguna akhir. Dalam kasus seperti itu, programmer dapat menggunakan void pointer untuk menunjuk ke lokasi tipe data yang tidak dikenal. Program ini dapat diatur sedemikian rupa untuk meminta pengguna untuk menginformasikan tipe data dan tipe casting dapat dilakukan sesuai dengan informasi yang dimasukkan oleh pengguna. Cuplikan kode diberikan di bawah ini.

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
}

Poin penting lain yang harus Anda ingat tentang void pointer adalah bahwa - pointer aritmatika tidak dapat dilakukan dalam void 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.

Jadi sekarang saya mengerti apa kesalahan saya. Saya mengoreksi hal yang sama.

Referensi :

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

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

Kode baru seperti yang ditunjukkan di bawah ini.


#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;
}   

Terima kasih,

Megharaj
sumber
2

Tidak, itu tidak mungkin. Jenis apa yang harus dimiliki nilai dereferensi?

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

sumber
Apakah program ini mungkin .... Silakan periksa, apakah ini mungkin dalam pemrograman C ...
AGeek
2
Ya, ini mungkin (meskipun kode berbahaya tanpa rentang pemeriksaan untuk b). Printf mengambil sejumlah variabel argumen dan a hanya didorong pada stack sebagai pointer apa pun (tanpa informasi jenis apa pun) dan muncul di dalam fungsi printf menggunakan makro va_arg menggunakan info dari string format.
hlovdal
@ SiegeX: tolong jelaskan apa yang tidak portabel, dan apa yang mungkin tidak terdefinisi.
Gauthier
@Gauthier melewati variabel yang berisi penentu format tidak portabel dan berpotensi perilaku tidak terdefinisi. Jika Anda ingin melakukan sesuatu seperti itu maka lihatlah rasa 'vs' printf. Jawaban ini memiliki contoh yang bagus untuk itu.
SiegeX
Dalam praktiknya, saya mungkin akan mengganti int bdengan enum, tetapi membuat perubahan kemudian pada enum itu akan merusak fungsi ini.
Jack Stout
1

Saya ingin membuat fungsi ini generik, tanpa menggunakan ifs; apa itu mungkin?

Satu-satunya cara sederhana yang saya lihat adalah menggunakan overloading .. yang tidak tersedia dalam bahasa pemrograman C AFAIK.

Apakah Anda mempertimbangkan bahasa pemrograman C ++ untuk program Anda? Atau adakah kendala yang melarang penggunaannya?

Yves Baume
sumber
Ok, itu memalukan. Dari sudut pandang saya, saya tidak melihat solusi. Sebenarnya, ini sama dengan printf () & cout: 2 cara berbeda untuk mengimplementasikan pencetakan. printf () digunakan jika () pernyataan untuk memecahkan kode format string (saya kira atau yang serupa), sedangkan untuk operator kasus cout << adalah fungsi kelebihan beban
Yves Baumes
1

Berikut ini adalah pointer singkat tentang voidpointer: https://www.learncpp.com/cpp-tutorial/613-void-pointers/

6.13 - Void pointer

Karena void pointer tidak tahu jenis objek yang ditunjuknya, ia tidak dapat ditinjau secara langsung! Alih-alih, void pointer pertama-tama harus secara eksplisit dilemparkan ke tipe pointer lain sebelum dereferensi.

Jika batal pointer tidak tahu apa yang menunjuk ke sana, bagaimana kita tahu apa yang harus dilemparkan? Pada akhirnya, itu terserah Anda untuk melacak.

Batalkan penunjuk miscellany

Tidak mungkin untuk melakukan aritmatika pointer pada pointer kosong. Ini karena pointer aritmatika membutuhkan pointer untuk mengetahui ukuran objek yang ditunjuknya, sehingga dapat menambah atau mengurangi pointer dengan tepat.

Dengan asumsi memori mesin adalah byte-addressable dan tidak memerlukan akses yang selaras, cara paling umum dan atomik (paling dekat dengan representasi level mesin) menafsirkan void*adalah sebagai pointer-ke-byte uint8_t*,. Casting a void*ke a uint8_t*akan memungkinkan Anda untuk, misalnya, mencetak byte pertama 1/2/4/8 / namun-banyak-Anda-keinginan mulai dari alamat itu, tetapi Anda tidak bisa melakukan banyak hal lain.

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

Anda dapat dengan mudah mencetak printer batal

int p=15;
void *q;
q=&p;
printf("%d",*((int*)q));
Ankit Bansal
sumber
1
Siapa yang menjamin yang voidmemiliki ukuran yang sama int?
Ant_222
Ini bukan secara teknis mencetak voidpointer, itu mencetak intpada alamat memori yang berisi pointer (yang dalam hal ini akan menjadi nilai p).
Marionumber1
0

Karena C adalah bahasa yang diketik secara statis, diketik dengan sangat kuat, Anda harus memutuskan jenis variabel sebelum dikompilasi. Saat Anda mencoba meniru obat generik dalam C, Anda akan berakhir mencoba menulis ulang C ++ lagi, jadi lebih baik menggunakan C ++.

adem
sumber
0

void pointer adalah pointer umum .. Alamat dari semua tipe data dari variabel apa pun dapat ditugaskan ke sebuah void pointer.

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
N.Annamalai Raj
sumber
0

Anda tidak dapat melakukan dereferensi pointer tanpa menentukan jenisnya karena tipe data yang berbeda akan memiliki ukuran yang berbeda dalam memori yaitu int menjadi 4 byte, karakter menjadi 1 byte.

gotoat
sumber
-1

Pada dasarnya, dalam C, "jenis" adalah cara untuk menafsirkan byte dalam memori. Misalnya, apa kode berikut ini

struct Point {
  int x;
  int y;
};

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

Mengatakan "Ketika saya menjalankan main, saya ingin mengalokasikan 4 (ukuran integer) + 4 (ukuran integer) = 8 (total byte) memori. Ketika saya menulis '.x' sebagai nilai pada nilai dengan label tipe Arahkan pada waktu kompilasi, ambil data dari lokasi memori pointer plus empat byte. Berikan nilai kembali label waktu kompilasi "int." "

Di dalam komputer saat runtime, struktur "Point" Anda terlihat seperti ini:

00000000 00000000 00000000 00000000 00000000 00000000 00000000

Dan inilah yang void*tampak seperti tipe data Anda : (dengan asumsi komputer 32-bit)

10001010 11111001 00010010 11000101
noɥʇʎԀʎzɐɹƆ
sumber
Ini tidak menjawab pertanyaan
MM
-2

Ini tidak akan berfungsi, namun void * dapat banyak membantu dalam mendefinisikan pointer umum ke fungsi dan meneruskannya sebagai argumen ke fungsi lain (mirip dengan panggilan balik di Jawa) atau mendefinisikannya struktur yang mirip dengan oop.

Rami
sumber