質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ポインタ

ポインタはアドレスを用いてメモリに格納された値を"参照する"変数です。

Q&A

解決済

4回答

1908閲覧

void *型の引数は必ず関数内でキャストする必要があるのか

退会済みユーザー

退会済みユーザー

総合スコア0

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ポインタ

ポインタはアドレスを用いてメモリに格納された値を"参照する"変数です。

0グッド

0クリップ

投稿2020/11/08 06:36

編集2020/11/08 06:47

質問者のレベル

プログラミングを初めて2ヶ月程度。
学習しているのは、C, Python, html, css, jsです。

質問内容

void *型に関してですが、
関数の引数として利用する場合はvoid *型にすることで汎用的なポインタ値を受け取れると思うのですが、
関数内で利用する場合は必ずキャストする必要があるのでしょうか?
(渡されたポインタ値の型がわからず、メモリのサイズが不明だから?ですかね?)

またその場合、どのような型が渡されるか分からないため、
キャストする際の型指定など何か方法や通例があるのでしょうか?

例えば下記のページにあるmem系の関数はvoid *型で受け取り、
関数内でunsigned charもしくはconst unsigned charにキャストしていますが、
これはchar型の場合メモリサイズが1byteだからでしょうか・・・?

https://qiita.com/mizcii/items/38aa405122542771a23f

沼にハマりそうな気がしており、質問させていただければと思います。
(知識がなさすぎて質問文すらうまく書けているか不安です・・・)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yumetodo

2020/11/08 06:43

下記のページとは・・・?
maisumakun

2020/11/08 06:56

void *を使って、「どのような関数を作りたい」のですか?
退会済みユーザー

退会済みユーザー

2020/11/08 07:03

こちらご質問ありがとうございます! 自分が今目下取り組んでいることはmem系の関数を再現することだったのですが、 その学習途中でなぜ関数の引数をunsigned char *型に最初からせずに受け取ったものをキャストするのだろうと思っての質問でした・・・言葉足らずですみません!
guest

回答4

0

ベストアンサー

void* 型の値は

  • 元の型にキャストしなおす
  • char* にキャストする

という二点の使い方以外はほとんど保証がありません。 あくまで「ほとんど」であって細かいルールはたくさんあるのですが全部説明するのは無理なので、 mem 系関数での意味をここでは説明します。

仮引数が void* で定義されているのは「暗黙の型変換」を活用するためです。 基本的には型が違う値を相互に代入したり引数として与えたりすることは出来ず、キャストを書く必要があるのですが、一部の型については自動的に型変換するルールが規定されています。 void* もそのひとつです。 void* から他のポインタ型、他のポインタ型から void* の間ではキャストを書かなくても暗黙の型変換をしてくれます。

mem 系関数の void* 型の引数に int* とか float* とかを (明示的なキャストなしで) 与えてもエラーにならずに処理できるのはそのためです。 仮引数を直接 char* で受け取ることにするとその関数を使う側で常に明示的型変換をする必要が生じてしまいます。

投稿2020/11/08 06:56

SaitoAtsushi

総合スコア5444

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/11/08 07:16

詳しくご回答いただきありがとうございます!とてもすっきりしました! mem系において、関数内ではメモリ分をコピーしたいため、サイズが1byteごとのchar型(unsigned char型)を利用し、キャストを行うが、 関数を利用する際には暗黙の型変換を利用するためにvoid *で定義をしている、ということですかね! とても勉強になりました、ありがとございます! ちなみによろしければmem系の関数はバイト列を指定したサイズ分操作する関数だと思うのですが、 それは文字列に対する処理以外でどのような時に使うのでしょうか・・・? (mem系の関数の引数にchar *型ではなく、int *型などが与えられる具体的なシーンが自分では思い付かず・・・)
otn

2020/11/08 07:48

> (mem系の関数の引数にchar *型ではなく、int *型などが与えられる具体的なシーンが自分では思い付かず・・・) int x[10], y[10];で、xの全体をyにコピーするときとか。
SaitoAtsushi

2020/11/08 08:16

C では様々なアーキテクチャの都合に合わせて挙動が違っていてもよい部分が多いのですが「データ記憶域 (メモリ) はバイトの並びである」というのは保証されています。 どんなオブジェクトもメモリ上のどこかに配置されたバイト列であり、バイト列としてアクセス可能なのです。 ちなみに言語仕様上では、値を表現可能な記憶域 (メモリ) の一部分を「オブジェクト」と称し、型で意味付けされたものを「値」として用語を区別していますね。 実際にはそれほど使い分けませんけど……。
退会済みユーザー

退会済みユーザー

2020/11/09 15:40

>otnさん こちらお教えいただきありがとうございます!配列のコピーはまさにですね、本当勉強になります! >Saito Atsushiさん こちらも詳しく教えていただきありがとうございます! みなさま本当に快くお教えいただき、本当に勉強になりました。 特にSaitoさんは詳しくお教えいただきましたので、僭越ながらベストアンサーに選ばせていただきました。 本当にありがとうございます!
guest

0

関数内で利用する場合は必ずキャストする必要があるのでしょうか?

必ずしもありません。たとえば、「ヌルチェックするだけ」の関数を作った場合、void *のままでもそれは実現可能です。

投稿2020/11/08 06:46

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2020/11/08 06:49

> どのような型が渡されるか分からないため どんなポインタを渡せばいいのかのルールは、「文法外で決める」ことになります。本当に「どのようなポインタを渡してもいい」関数は、一般には「ヌルチェックするだけ」程度の用事しかこなせません。
退会済みユーザー

退会済みユーザー

2020/11/08 07:06

なるほど・・・確かにヌルチェックを行う関数の場合はキャストせずにvoid *型のまま処理を行うことができますね 反対にそれ以外の用途の場合は多くの場合、キャストする必要があるということなんですね!とても勉強になります!ありがとうございます!
guest

0

(mem系の関数の引数にchar *型ではなく、int *型などが与えられる具体的なシーンが自分では思い付かず・・・)

例えば、メモリをダンプする関数を作るとします。(16進数でメモリの内容を画面に表示する)
引数は以下のようになります。
1.ダンプ開始する先頭アドレス
2.ダンプするサイズ(バイト数)
関数定義は以下のようになります。
void dump(void *data,int data_size)

これの呼び出し側で、以下のような変数があるとき、
int a;
char b[10];
double c;

dump(&a,sizeof(a));  //aのダンプ
dump(b,10);      //bのダンプ
dump(&c,sizeof(c));  //cのダンプ
のように呼び出します。
dumpの関数定義で
void dump(char data,int data_size)
としてしまうと、int 型、double型をダンプするとき、呼び出し側でchar
型にキャストに変換する必要が
あります。(当然、呼び出し側にとっては不便な使い方になります。)

投稿2020/11/08 07:29

tatsu99

総合スコア5438

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/11/09 15:36

なるほど・・・! 根本的には呼び出し側が使いやすいように関数へ受け渡しは緩やかにし、定義内でキャストするんですね! そしてメモリダンプの時とかまさにそうですね!!とても勉強になりました! ありがとうございます!
guest

0

これはchar型の場合メモリサイズが1byteだからでしょうか・・・?

そういうことです。
バイト列をそのサイズ分コピーするだけ、の関数なのでそうなってます

投稿2020/11/08 06:56

y_waiwai

総合スコア87747

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/11/08 07:07

やはりそういうことなんですね! バイト列をそのサイズ分コピーする、といった処理は文字列以外にも使うのでしょうか・・・?
y_waiwai

2020/11/08 07:51

文字列以外でも、他の型や構造体の変数や配列なんかも、サイズ分のバイトコピーでコピーできますね
退会済みユーザー

退会済みユーザー

2020/11/09 15:38

確かに配列などもサイズ分のバイト数を引数で渡してまるっとコピーできますね・・・ 全然使い所たくさんありますね、自分の勉強不足が恥ずかしいです! 教えていただきありがとございます!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問