web-dev-qa-db-ja.com

Cプログラミングのvoidポインターの概念

Cプログラミング言語で型キャストせずにvoidポインターを逆参照することは可能ですか?

また、ポインターを受け取ってvoidポインターに保存できる関数を一般化する方法はありますか?そのvoidポインターを使用して、一般化された関数を作成できますか?

例えば:

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を提供できれば有益です。

また、voidポインターを使用したポインター演算は可能ですか?

125
AGeek

Cプログラミング言語で型キャストせずにvoidポインターを逆参照することは可能ですか?

いいえ、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?? */
95
Alex B

Cでは、void *は、明示的なキャストなしで、異なるタイプのオブジェクトへのポインターに変換できます。

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

ただし、これは関数をより一般的な方法で記述するのには役立ちません。

ポインターの逆参照は、指示先オブジェクトの値を取得しているため、異なるポインター型に変換してvoid *を逆参照することはできません。裸のvoidは有効なタイプではないため、void *を逆参照することはできません。

ポインター演算は、sizeofのポイント先オブジェクトの倍数によってポインター値を変更することです。繰り返しますが、voidは真の型ではないため、sizeof(void)には意味がないため、void *ではポインター演算は無効です。 (一部の実装では、char *の同等のポインター演算を使用して許可しています。)

29
CB Bailey

Cでは、JavaやC#とは異なり、void *ポインターが指すオブジェクトのタイプを正常に「推測」する可能性はまったくありません。 「getClass()」に似たものは、単に存在しません。この情報はどこにも見つからないからです。そのため、探している種類の「ジェネリック」には、例の「int b」や関数のprintfファミリーのフォーマット文字列のような明示的なメタ情報が常に付属しています。

15

Voidポインターは汎用ポインターと呼ばれ、任意のデータ型の変数を参照できます。

6
Sneha

これまでのところ、voidポインターに関する私の過小評価は次のとおりです。

キーワードvoidを使用してポインター変数を宣言すると、汎用ポインター変数になります。任意のデータ型の変数(char、int、floatなど)のアドレスをvoidポインター変数に割り当てることができます。

main()
{
    int *p;

    void *vp;

    vp=p;
} 

他のデータ型ポインターをvoidポインターに割り当てることができるため、absolut_value(以下に示すコード)関数で使用しました。一般的な機能を作成します。

整数または浮動小数点数を引数として取り、負の場合は+ veにしようとする単純なCコードを作成しようとしました。私は次のコードを書きました、

#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ポインターについて留意すべきもう1つの重要な点は、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
Megharaj

いいえ、できません。間接参照された値にはどの型が必要ですか?

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

Ifsを使用せずに、この関数をジェネリックにします。出来ますか?

私が見る唯一の簡単な方法は、オーバーロードを使用することです..これは、Cプログラミング言語AFAIKでは利用できません。

あなたのプログラムのC++プログラミング言語を考慮しましたか?または、その使用を禁止する制約はありますか?

1
yves Baumes

Cは静的に型付けされ、厳密に型付けされた言語であるため、コンパイルの前に変数の型を決定する必要があります。 Cでジェネリックをエミュレートしようとすると、最終的にC++を再度書き直そうとするため、代わりにC++を使用することをお勧めします。

0
adem

空のプリンターを簡単に印刷できます

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

voidポインターの簡単なポインターを次に示します。 https://www.learncpp.com/cpp-tutorial/613-void-pointers/

6.13 — Voidポインター

Voidポインターはそれが指しているオブジェクトのタイプを知らないため、直接参照解除することはできません!むしろ、voidポインターは、間接参照される前に、まず別のポインター型に明示的にキャストする必要があります。

Voidポインターが何を指しているのかわからない場合、どのようにキャストするのかをどうやって知るのでしょうか?最終的に、それを追跡するのはあなた次第です。

無効なポインターのその他

Voidポインターでポインター演算を行うことはできません。これは、ポインター演算ではポインターがどのサイズのオブジェクトを指しているかを知る必要があるため、ポインターを適切に増減できるためです。

マシンのメモリがバイトアドレス可能で、アラインされたアクセスを必要としないと仮定すると、void*を解釈する最も一般的でアトミックな(マシンレベルの表現に最も近い)方法は、バイトへのポインタuint8_t*void*uint8_t*にキャストすると、たとえば、そのアドレスで始まる最初の1/2/4/8 /ただし多くのバイトを印刷できますが、他に多くはしません。

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

異なるデータ型はメモリ内で異なるサイズ、つまりintが4バイト、charが1バイトであるため、ポインタをその型を指定せずに間接参照することはできません。

0
gotoat

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